1
2 package treeprint
3
4 import (
5 "bytes"
6 "fmt"
7 "io"
8 "reflect"
9 "strings"
10 )
11
12
13 type Value interface{}
14
15
16 type MetaValue interface{}
17
18
19 type NodeVisitor func(item *Node)
20
21
22 type Tree interface {
23
24 AddNode(v Value) Tree
25
26 AddMetaNode(meta MetaValue, v Value) Tree
27
28 AddBranch(v Value) Tree
29
30 AddMetaBranch(meta MetaValue, v Value) Tree
31
32
33 Branch() Tree
34
35
36 FindByMeta(meta MetaValue) Tree
37
38
39 FindByValue(value Value) Tree
40
41 FindLastNode() Tree
42
43 String() string
44
45 Bytes() []byte
46
47 SetValue(value Value)
48 SetMetaValue(meta MetaValue)
49
50
51
52
53 VisitAll(fn NodeVisitor)
54 }
55
56 type Node struct {
57 Root *Node
58 Meta MetaValue
59 Value Value
60 Nodes []*Node
61 }
62
63 func (n *Node) FindLastNode() Tree {
64 ns := n.Nodes
65 if len(ns) == 0 {
66 return nil
67 }
68 return ns[len(ns)-1]
69 }
70
71 func (n *Node) AddNode(v Value) Tree {
72 n.Nodes = append(n.Nodes, &Node{
73 Root: n,
74 Value: v,
75 })
76 return n
77 }
78
79 func (n *Node) AddMetaNode(meta MetaValue, v Value) Tree {
80 n.Nodes = append(n.Nodes, &Node{
81 Root: n,
82 Meta: meta,
83 Value: v,
84 })
85 return n
86 }
87
88 func (n *Node) AddBranch(v Value) Tree {
89 branch := &Node{
90 Root: n,
91 Value: v,
92 }
93 n.Nodes = append(n.Nodes, branch)
94 return branch
95 }
96
97 func (n *Node) AddMetaBranch(meta MetaValue, v Value) Tree {
98 branch := &Node{
99 Root: n,
100 Meta: meta,
101 Value: v,
102 }
103 n.Nodes = append(n.Nodes, branch)
104 return branch
105 }
106
107 func (n *Node) Branch() Tree {
108 n.Root = nil
109 return n
110 }
111
112 func (n *Node) FindByMeta(meta MetaValue) Tree {
113 for _, node := range n.Nodes {
114 if reflect.DeepEqual(node.Meta, meta) {
115 return node
116 }
117 if v := node.FindByMeta(meta); v != nil {
118 return v
119 }
120 }
121 return nil
122 }
123
124 func (n *Node) FindByValue(value Value) Tree {
125 for _, node := range n.Nodes {
126 if reflect.DeepEqual(node.Value, value) {
127 return node
128 }
129 if v := node.FindByMeta(value); v != nil {
130 return v
131 }
132 }
133 return nil
134 }
135
136 func (n *Node) Bytes() []byte {
137 buf := new(bytes.Buffer)
138 level := 0
139 var levelsEnded []int
140 if n.Root == nil {
141 if n.Meta != nil {
142 buf.WriteString(fmt.Sprintf("[%v] %v", n.Meta, n.Value))
143 } else {
144 buf.WriteString(fmt.Sprintf("%v", n.Value))
145 }
146 buf.WriteByte('\n')
147 } else {
148 edge := EdgeTypeMid
149 if len(n.Nodes) == 0 {
150 edge = EdgeTypeEnd
151 levelsEnded = append(levelsEnded, level)
152 }
153 printValues(buf, 0, levelsEnded, edge, n)
154 }
155 if len(n.Nodes) > 0 {
156 printNodes(buf, level, levelsEnded, n.Nodes)
157 }
158 return buf.Bytes()
159 }
160
161 func (n *Node) String() string {
162 return string(n.Bytes())
163 }
164
165 func (n *Node) SetValue(value Value) {
166 n.Value = value
167 }
168
169 func (n *Node) SetMetaValue(meta MetaValue) {
170 n.Meta = meta
171 }
172
173 func (n *Node) VisitAll(fn NodeVisitor) {
174 for _, node := range n.Nodes {
175 fn(node)
176
177 if len(node.Nodes) > 0 {
178 node.VisitAll(fn)
179 continue
180 }
181 }
182 }
183
184 func printNodes(wr io.Writer,
185 level int, levelsEnded []int, nodes []*Node) {
186
187 for i, node := range nodes {
188 edge := EdgeTypeMid
189 if i == len(nodes)-1 {
190 levelsEnded = append(levelsEnded, level)
191 edge = EdgeTypeEnd
192 }
193 printValues(wr, level, levelsEnded, edge, node)
194 if len(node.Nodes) > 0 {
195 printNodes(wr, level+1, levelsEnded, node.Nodes)
196 }
197 }
198 }
199
200 func printValues(wr io.Writer,
201 level int, levelsEnded []int, edge EdgeType, node *Node) {
202
203 for i := 0; i < level; i++ {
204 if isEnded(levelsEnded, i) {
205 fmt.Fprint(wr, strings.Repeat(" ", IndentSize+1))
206 continue
207 }
208 fmt.Fprintf(wr, "%s%s", EdgeTypeLink, strings.Repeat(" ", IndentSize))
209 }
210
211 val := renderValue(level, node)
212 meta := node.Meta
213
214 if meta != nil {
215 fmt.Fprintf(wr, "%s [%v] %v\n", edge, meta, val)
216 return
217 }
218 fmt.Fprintf(wr, "%s %v\n", edge, val)
219 }
220
221 func isEnded(levelsEnded []int, level int) bool {
222 for _, l := range levelsEnded {
223 if l == level {
224 return true
225 }
226 }
227 return false
228 }
229
230 func renderValue(level int, node *Node) Value {
231 lines := strings.Split(fmt.Sprintf("%v", node.Value), "\n")
232
233
234 if len(lines) < 2 {
235 return node.Value
236 }
237
238
239
240 pad := padding(level, node)
241
242 for i := 1; i < len(lines); i++ {
243 lines[i] = fmt.Sprintf("%s%s", pad, lines[i])
244 }
245
246 return strings.Join(lines, "\n")
247 }
248
249
250
251
252
253
254 func padding(level int, node *Node) string {
255 links := make([]string, level+1)
256
257 for node.Root != nil {
258 if isLast(node) {
259 links[level] = strings.Repeat(" ", IndentSize+1)
260 } else {
261 links[level] = fmt.Sprintf("%s%s", EdgeTypeLink, strings.Repeat(" ", IndentSize))
262 }
263 level--
264 node = node.Root
265 }
266
267 return strings.Join(links, "")
268 }
269
270
271 func isLast(n *Node) bool {
272 return n == n.Root.FindLastNode()
273 }
274
275 type EdgeType string
276
277 var (
278 EdgeTypeLink EdgeType = "│"
279 EdgeTypeMid EdgeType = "├──"
280 EdgeTypeEnd EdgeType = "└──"
281 )
282
283
284 var IndentSize = 3
285
286
287 func New() Tree {
288 return &Node{Value: "."}
289 }
290
291
292 func NewWithRoot(root Value) Tree {
293 return &Node{Value: root}
294 }
295
View as plain text