tree - nodes.go
1 package tree
2
3 import (
4 "io"
5 "iter"
6 "slices"
7 "strings"
8 )
9
10 // Leaf represents a childless Node that contains only data.
11 //
12 // The Lead itself is a byte-slice.
13 type Leaf []byte
14
15 // Children will return an empty iterator for Leaf Nodes.
16 func (Leaf) Children() iter.Seq2[string, Node] {
17 return noChildren
18 }
19
20 // WriteTo will pass the Nodes data to the given io.Writer as a single
21 // byte-slice.
22 func (l Leaf) WriteTo(w io.Writer) (int64, error) {
23 n, err := w.Write(l)
24
25 return int64(n), err
26 }
27
28 // Data returns the Nodes data.
29 func (l Leaf) Data() []byte {
30 return l
31 }
32
33 // DataLen returns the length of the data stored on this Node.
34 func (l Leaf) DataLen() int64 {
35 return int64(len(l))
36 }
37
38 // Child will always return nil with a ChildNotFoundError error for a Leaf Node.
39 func (Leaf) Child(name string) (Node, error) {
40 return nil, ChildNotFoundError(name)
41 }
42
43 // NumChildren will always return 0 for a Leaf Node.
44 func (Leaf) NumChildren() int {
45 return 0
46 }
47
48 type nameNode struct {
49 Name string
50 Node
51 }
52
53 func (n nameNode) compare(m nameNode) int {
54 return strings.Compare(n.Name, m.Name)
55 }
56
57 // Branch is a collection of named Nodes.
58 type Branch []nameNode
59
60 // Add adds a named Node to the branch.
61 //
62 // No locking takes place, so all children should be added before using the
63 // Branch Node.
64 func (b *Branch) Add(name string, node Node) error {
65 pos, exists := slices.BinarySearchFunc(*b, nameNode{Name: name}, nameNode.compare)
66 if exists {
67 return DuplicateChildError{name}
68 }
69
70 *b = slices.Insert(*b, pos, nameNode{Name: name, Node: node})
71
72 return nil
73 }
74
75 // Children returns an iterator that loops through all of the child Nodes.
76 func (b Branch) Children() iter.Seq2[string, Node] {
77 return func(yield func(string, Node) bool) {
78 for _, nn := range b {
79 if !yield(nn.Name, nn.Node) {
80 break
81 }
82 }
83 }
84 }
85
86 // WriteTo always returns 0, nil for a Branch Node.
87 func (Branch) WriteTo(_ io.Writer) (int64, error) {
88 return 0, nil
89 }
90
91 // Child attempts to retrieve a child Node corresponding to the given name.
92 //
93 // If no child matches the given name, the returned error will be of type
94 // ChildNotFoundError.
95 func (b Branch) Child(name string) (Node, error) {
96 pos, exists := slices.BinarySearchFunc(b, nameNode{Name: name}, nameNode.compare)
97 if !exists {
98 return nil, ChildNotFoundError(name)
99 }
100
101 return b[pos], nil
102 }
103
104 // Data returns the Nodes data.
105 func (Branch) Data() []byte {
106 return nil
107 }
108
109 // DataLen will always return 0 for a Branch Node.
110 func (Branch) DataLen() int64 {
111 return 0
112 }
113
114 // NumChildren returns the number of child Nodes that are attached to this Node.
115 func (b Branch) NumChildren() int {
116 return len(b)
117 }
118
119 type multiNode struct {
120 name string
121 nodes []Node
122 }
123
124 func (m multiNode) compare(n multiNode) int {
125 return strings.Compare(m.name, n.name)
126 }
127
128 type Roots []multiNode
129
130 // Merge combines the children from multiple nodes, merging same named
131 // children similarly.
132 //
133 // Changes made to the Nodes after merging will not be recognised.
134 func Merge(nodes ...Node) (Roots, error) {
135 var (
136 b = make(map[string][]Node)
137 r Roots
138 )
139
140 for _, node := range nodes {
141 for name, child := range node.Children() {
142 if ce, ok := child.(ChildrenError); ok {
143 return nil, ce.error
144 }
145
146 b[name] = append(b[name], child)
147 }
148 }
149
150 for name, nodes := range b {
151 pos, _ := r.childPos(name)
152
153 r = slices.Insert(r, pos, multiNode{name: name, nodes: nodes})
154 }
155
156 return r, nil
157 }
158
159 func (r Roots) childPos(name string) (int, bool) {
160 return slices.BinarySearchFunc(r, multiNode{name: name}, multiNode.compare)
161 }
162
163 // Children returns an iterator that loops through all of the child Nodes.
164 //
165 // Any errors will be expressed with a final Node of underlying type
166 // ChildrenError.
167 func (r Roots) Children() iter.Seq2[string, Node] {
168 return func(yield func(string, Node) bool) {
169 for _, children := range r {
170 if len(children.nodes) == 1 {
171 if !yield(children.name, children.nodes[0]) {
172 return
173 }
174
175 continue
176 }
177
178 roots, err := Merge(children.nodes...)
179 if err != nil {
180 yield(children.name, ChildrenError{err})
181
182 return
183 }
184
185 yield(children.name, roots)
186 }
187 }
188 }
189
190 // WriteTo always return 0, nil for a Roots Node.
191 func (Roots) WriteTo(_ io.Writer) (int64, error) {
192 return 0, nil
193 }
194
195 // Child attempts to retrieve a child Node corresponding to the given name.
196 //
197 // If no child matches the given name, the returned error will be of type
198 // ChildNotFoundError.
199 func (r Roots) Child(name string) (Node, error) {
200 pos, exists := r.childPos(name)
201 if !exists {
202 return nil, ChildNotFoundError(name)
203 }
204
205 if len(r[pos].nodes) == 1 {
206 return r[pos].nodes[0], nil
207 }
208
209 return Merge(r[pos].nodes...)
210 }
211
212 // Data will always return nil for a Roots Node.
213 func (Roots) Data() []byte {
214 return nil
215 }
216
217 // DataLen will always return 0 for a Roots Node.
218 func (Roots) DataLen() int64 {
219 return 0
220 }
221
222 // NumChildren returns the number of child Nodes that are attached to this Node.
223 func (r Roots) NumChildren() int {
224 return len(r)
225 }
226