File: threadcontainer.go

package info (click to toggle)
aerc 0.21.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,900 kB
  • sloc: ansic: 1,181; python: 1,000; sh: 553; awk: 360; makefile: 23
file content (144 lines) | stat: -rw-r--r-- 3,612 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Author: Jim Idle - jimi@idle.ws / jimi@gatherstars.com
// SPDX-License-Identifier: Apache-2.0

package jwz

// NoThreadableError indicates that the container was not in a valid state when a utility function was called
// against it
type NoThreadableError struct{}

func (e NoThreadableError) Error() string {
	return "Container is not a root (has parent), but contains no Threadable element"
}

// threadContainer  is used to encapsulate a Threadable implementation. It holds some intermediate state used while
// threading.  This is private to the module and is used while performing the threading operation, then discarded.
type threadContainer struct {
	// threadable contains the base of the threadable items in this instance of the struct
	//
	threadable Threadable

	// parent shows which threadable item is the parent of this container, which can be nil for a root node
	//
	parent *threadContainer

	// child shows which threadable is the child of this container
	//
	child *threadContainer

	// next shows which threadable is the sibling of this container
	//
	next *threadContainer

	// forID holds the message id that this container is holding. This is only useful for
	// when we don't ever see the actual email (it is referenced by one email, but we don't
	// have the email as we are parsing a partial set)
	//
	forID string
}

// flush copies the ThreadContainer tree structure down into the underlying
// Threadable elements. I.E. make the IThreadable tree look like
// the ThreadContainer tree.
func (tc *threadContainer) flush() error {
	// Only a root node can have no threadable element - if we have a parent, then
	// we are not a root node.
	//
	if tc.parent != nil && tc.threadable == nil {
		return NoThreadableError{}
	}

	tc.parent = nil

	if tc.threadable != nil {
		if tc.child == nil {
			tc.threadable.SetChild(nil)
		} else {
			tc.threadable.SetChild(tc.child.threadable)
		}
	}

	if tc.child != nil {
		_ = tc.child.flush()
		tc.child = nil
	}

	if tc.threadable != nil {
		if tc.next == nil {
			tc.threadable.SetNext(nil)
		} else {
			tc.threadable.SetNext(tc.next.threadable)
		}
	}

	if tc.next != nil {
		_ = tc.next.flush()
		tc.next = nil
	}

	tc.threadable = nil

	return nil
}

// findChild returns true if child is under self's tree. This is used for
// detecting circularities in the references header.
func (tc *threadContainer) findChild(target *threadContainer) bool {
	found := false
	for t := tc.child; t != nil; t = t.next {
		found = found || t == target || t.findChild(target)
	}
	return found
}

// reverseChildren does what it implies and reverses the order of the child elements
func (tc *threadContainer) reverseChildren() {
	if tc.child != nil {

		var kid, prev, rest *threadContainer

		// Reverse the children of this one
		//
		prev = nil
		kid = tc.child
		rest = kid.next
		for kid != nil {

			kid.next = prev

			prev = kid
			kid = rest
			if rest != nil {
				rest = rest.next
			}
		}
		tc.child = prev

		// Now reverse the children of this one's children
		//
		for kid = tc.child; kid != nil; kid = kid.next {
			kid.reverseChildren()
		}
	}
}

func (tc *threadContainer) fillDummy(t Threadable) {
	// Only a root node can have no threadable element - if we have a message id, then
	// we are not a root node.
	//
	if tc.threadable == nil && tc.forID != "" {
		tc.threadable = t.MakeDummy(tc.forID)
	}

	// Depth first, but it does not matter
	//
	if tc.child != nil {
		tc.child.fillDummy(t)
	}

	// Simple walk this one, no need to save stack space by following the next chain here
	//
	if tc.next != nil {
		tc.next.fillDummy(t)
	}
}