Codebase list golang-github-xlab-treeprint / 30e22a5
New upstream release. Debian Janitor 2 years ago
6 changed file(s) with 324 addition(s) and 44 deletion(s). Raw diff Collapse all Expand all
4040
4141 ## Use cases
4242
43 When you want to render a complex data structure:
43 ### When you want to render a complex data structure:
4444
4545 ```go
4646 func main() {
47 // to add a custom root name use `treeprint.NewWithRoot()` instead
4748 tree := treeprint.New()
4849
4950 // create a new branch in the root
8586 └── outernode
8687 ```
8788
88 Another case, when you have to make a tree where any leaf may have some meta-data (as `tree` is capable of it):
89 ### Another case, when you have to make a tree where any leaf may have some meta-data (as `tree` is capable of it):
8990
9091 ```go
9192 func main {
93 // to add a custom root name use `treeprint.NewWithRoot()` instead
9294 tree := treeprint.New()
9395
9496 tree.AddNode("Dockerfile")
121123 └── [122K] testtool.a
122124 ```
123125
126 ### Iterating over the tree nodes
127
128 ```go
129 tree := New()
130
131 one := tree.AddBranch("one")
132 one.AddNode("one-subnode1").AddNode("one-subnode2")
133 one.AddBranch("two").AddNode("two-subnode1").AddNode("two-subnode2").
134 AddBranch("three").AddNode("three-subnode1").AddNode("three-subnode2")
135 tree.AddNode("outernode")
136
137 // if you need to iterate over the whole tree
138 // call `VisitAll` from your top root node.
139 tree.VisitAll(func(item *node) {
140 if len(item.Nodes) > 0 {
141 // branch nodes
142 fmt.Println(item.Value) // will output one, two, three
143 } else {
144 // leaf nodes
145 fmt.Println(item.Value) // will output one-*, two-*, three-* and outernode
146 }
147 })
148
149 ```
124150 Yay! So it works.
125151
126152 ## License
0 golang-github-xlab-treeprint (1.1.0-1) UNRELEASED; urgency=low
1
2 * New upstream release.
3
4 -- Debian Janitor <janitor@jelmer.uk> Thu, 05 May 2022 10:53:03 -0000
5
06 golang-github-xlab-treeprint (0.0~git20181112.a009c39-2) unstable; urgency=medium
17
28 [ Debian Janitor ]
0 module github.com/xlab/treeprint
1
2 go 1.13
3
4 require github.com/stretchr/testify v1.7.0
0 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
1 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
3 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
4 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
5 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
6 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
7 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
8 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
9 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
55 "fmt"
66 "io"
77 "reflect"
8 "strings"
89 )
910
11 // Value defines any value
1012 type Value interface{}
13
14 // MetaValue defines any meta value
1115 type MetaValue interface{}
16
17 // NodeVisitor function type for iterating over nodes
18 type NodeVisitor func(item *node)
1219
1320 // Tree represents a tree structure with leaf-nodes and branch-nodes.
1421 type Tree interface {
3845
3946 SetValue(value Value)
4047 SetMetaValue(meta MetaValue)
48
49 // VisitAll iterates over the tree, branches and nodes.
50 // If need to iterate over the whole tree, use the root node.
51 // Note this method uses a breadth-first approach.
52 VisitAll(fn NodeVisitor)
4153 }
4254
4355 type node struct {
4961
5062 func (n *node) FindLastNode() Tree {
5163 ns := n.Nodes
52 n = ns[len(ns)-1]
53 return n
64 if len(ns) == 0 {
65 return nil
66 }
67 return ns[len(ns)-1]
5468 }
5569
5670 func (n *node) AddNode(v Value) Tree {
5872 Root: n,
5973 Value: v,
6074 })
61 if n.Root != nil {
62 return n.Root
63 }
6475 return n
6576 }
6677
7081 Meta: meta,
7182 Value: v,
7283 })
73 if n.Root != nil {
74 return n.Root
75 }
7684 return n
7785 }
7886
7987 func (n *node) AddBranch(v Value) Tree {
8088 branch := &node{
89 Root: n,
8190 Value: v,
8291 }
8392 n.Nodes = append(n.Nodes, branch)
8695
8796 func (n *node) AddMetaBranch(meta MetaValue, v Value) Tree {
8897 branch := &node{
98 Root: n,
8999 Meta: meta,
90100 Value: v,
91101 }
130140 if n.Meta != nil {
131141 buf.WriteString(fmt.Sprintf("[%v] %v", n.Meta, n.Value))
132142 } else {
133 buf.WriteString(fmt.Sprintf("%v",n.Value))
143 buf.WriteString(fmt.Sprintf("%v", n.Value))
134144 }
135145 buf.WriteByte('\n')
136146 } else {
139149 edge = EdgeTypeEnd
140150 levelsEnded = append(levelsEnded, level)
141151 }
142 printValues(buf, 0, levelsEnded, edge, n.Meta, n.Value)
152 printValues(buf, 0, levelsEnded, edge, n)
143153 }
144154 if len(n.Nodes) > 0 {
145155 printNodes(buf, level, levelsEnded, n.Nodes)
151161 return string(n.Bytes())
152162 }
153163
154 func (n *node) SetValue(value Value){
164 func (n *node) SetValue(value Value) {
155165 n.Value = value
156166 }
157167
158 func (n *node) SetMetaValue(meta MetaValue){
168 func (n *node) SetMetaValue(meta MetaValue) {
159169 n.Meta = meta
170 }
171
172 func (n *node) VisitAll(fn NodeVisitor) {
173 for _, node := range n.Nodes {
174 fn(node)
175
176 if len(node.Nodes) > 0 {
177 node.VisitAll(fn)
178 continue
179 }
180 }
160181 }
161182
162183 func printNodes(wr io.Writer,
168189 levelsEnded = append(levelsEnded, level)
169190 edge = EdgeTypeEnd
170191 }
171 printValues(wr, level, levelsEnded, edge, node.Meta, node.Value)
192 printValues(wr, level, levelsEnded, edge, node)
172193 if len(node.Nodes) > 0 {
173194 printNodes(wr, level+1, levelsEnded, node.Nodes)
174195 }
176197 }
177198
178199 func printValues(wr io.Writer,
179 level int, levelsEnded []int, edge EdgeType, meta MetaValue, val Value) {
200 level int, levelsEnded []int, edge EdgeType, node *node) {
180201
181202 for i := 0; i < level; i++ {
182203 if isEnded(levelsEnded, i) {
183 fmt.Fprint(wr, " ")
204 fmt.Fprint(wr, strings.Repeat(" ", IndentSize+1))
184205 continue
185206 }
186 fmt.Fprintf(wr, "%s   ", EdgeTypeLink)
187 }
207 fmt.Fprintf(wr, "%s%s", EdgeTypeLink, strings.Repeat(" ", IndentSize))
208 }
209
210 val := renderValue(level, node)
211 meta := node.Meta
212
188213 if meta != nil {
189214 fmt.Fprintf(wr, "%s [%v] %v\n", edge, meta, val)
190215 return
201226 return false
202227 }
203228
229 func renderValue(level int, node *node) Value {
230 lines := strings.Split(fmt.Sprintf("%v", node.Value), "\n")
231
232 // If value does not contain multiple lines, return itself.
233 if len(lines) < 2 {
234 return node.Value
235 }
236
237 // If value contains multiple lines,
238 // generate a padding and prefix each line with it.
239 pad := padding(level, node)
240
241 for i := 1; i < len(lines); i++ {
242 lines[i] = fmt.Sprintf("%s%s", pad, lines[i])
243 }
244
245 return strings.Join(lines, "\n")
246 }
247
248 // padding returns a padding for the multiline values with correctly placed link edges.
249 // It is generated by traversing the tree upwards (from leaf to the root of the tree)
250 // and, on each level, checking if the node the last one of its siblings.
251 // If a node is the last one, the padding on that level should be empty (there's nothing to link to below it).
252 // If a node is not the last one, the padding on that level should be the link edge so the sibling below is correctly connected.
253 func padding(level int, node *node) string {
254 links := make([]string, level+1)
255
256 for node.Root != nil {
257 if isLast(node) {
258 links[level] = strings.Repeat(" ", IndentSize+1)
259 } else {
260 links[level] = fmt.Sprintf("%s%s", EdgeTypeLink, strings.Repeat(" ", IndentSize))
261 }
262 level--
263 node = node.Root
264 }
265
266 return strings.Join(links, "")
267 }
268
269 // isLast checks if the node is the last one in the slice of its parent children
270 func isLast(n *node) bool {
271 return n == n.Root.FindLastNode()
272 }
273
204274 type EdgeType string
205275
206276 var (
207 EdgeTypeLink EdgeType = "│"
208 EdgeTypeMid EdgeType = "├──"
209 EdgeTypeEnd EdgeType = "└──"
277 EdgeTypeLink EdgeType = "│"
278 EdgeTypeMid EdgeType = "├──"
279 EdgeTypeEnd EdgeType = "└──"
210280 )
211281
282 // IndentSize is the number of spaces per tree level.
283 var IndentSize = 3
284
285 // New Generates new tree
212286 func New() Tree {
213287 return &node{Value: "."}
214288 }
289
290 // NewWithRoot Generates new tree with the given root value
291 func NewWithRoot(root Value) Tree {
292 return &node{Value: root}
293 }
55 "github.com/stretchr/testify/assert"
66 )
77
8 func TestZeroNodesWithRoot(t *testing.T) {
9 assert := assert.New(t)
10
11 tree := NewWithRoot("mytree")
12 actual := tree.String()
13 expected := "mytree\n"
14 assert.Equal(expected, actual)
15 }
16
817 func TestOneNode(t *testing.T) {
918 assert := assert.New(t)
1019
1221 tree.AddNode("hello")
1322 actual := tree.String()
1423 expected := `.
24 └── hello
25 `
26 assert.Equal(expected, actual)
27 }
28
29 func TestOneNodeWithRoot(t *testing.T) {
30 assert := assert.New(t)
31
32 tree := NewWithRoot("mytree")
33 tree.AddNode("hello")
34 actual := tree.String()
35 expected := `mytree
1536 └── hello
1637 `
1738 assert.Equal(expected, actual)
5475 actual := tree.String()
5576 expected := `.
5677 ├── hello
57 │   ├── my friend
58 │   └── lol
78 │ ├── my friend
79 │ └── lol
5980 └── world
6081 `
6182 assert.Equal(expected, actual)
7192 actual := tree.String()
7293 expected := `friends
7394 ├── hello
74 │   ├── my friend
75 │   └── lol
95 │ ├── my friend
96 │ └── lol
7697 └── world
7798 `
7899 assert.Equal(expected, actual)
94115 actual := tree.String()
95116 expected := `.
96117 ├── one
97 │   ├── subnode1
98 │   ├── subnode2
99 │   ├── two
100 │   │   ├── subnode1
101 │   │   ├── subnode2
102 │   │   └── three
103 │   │   ├── subnode1
104 │   │   └── subnode2
105 │   └── subnode3
118 │ ├── subnode1
119 │ ├── subnode2
120 │ ├── two
121 │ │ ├── subnode1
122 │ │ ├── subnode2
123 │ │ └── three
124 │ │ ├── subnode1
125 │ │ └── subnode2
126 │ └── subnode3
106127 └── outernode
107128 `
108129 assert.Equal(expected, actual)
127148 ├── Makefile
128149 ├── aws.sh
129150 ├── [ 204] bin
130 │   ├── dbmaker
131 │   ├── someserver
132 │   └── testtool
151 │ ├── dbmaker
152 │ ├── someserver
153 │ └── testtool
133154 ├── [ 374] deploy
134 │   ├── Makefile
135 │   └── bootstrap.sh
155 │ ├── Makefile
156 │ └── bootstrap.sh
136157 └── [122K] testtool.a
137158 `
138159 assert.Equal(expected, actual)
150171 actual := tree.String()
151172 expected := `.
152173 ├── one
153 │   └── two
174 │ └── two
154175 └── foo
155176 ├── bar
156 │   ├── a
157 │   ├── b
158 │   └── c
177 │ ├── a
178 │ ├── b
179 │ └── c
159180 └── end
160181 `
161182 assert.Equal(expected, actual)
162183 }
184
185 func TestEdgeTypeAndIndent(t *testing.T) {
186 assert := assert.New(t)
187
188 // Restore to the original values
189 defer func(link, mid, end EdgeType, indent int) {
190 EdgeTypeLink = link
191 EdgeTypeMid = mid
192 EdgeTypeEnd = end
193 IndentSize = indent
194 }(EdgeTypeLink, EdgeTypeMid, EdgeTypeEnd, IndentSize)
195
196 EdgeTypeLink = "|"
197 EdgeTypeMid = "+-"
198 EdgeTypeEnd = "+-"
199 IndentSize = 2
200
201 tree := New()
202 tree.AddBranch("one").AddNode("two")
203 foo := tree.AddBranch("foo")
204 foo.AddBranch("bar").AddNode("a").AddNode("b").AddNode("c")
205 foo.AddNode("end")
206
207 actual := tree.String()
208 expected := `.
209 +- one
210 | +- two
211 +- foo
212 +- bar
213 | +- a
214 | +- b
215 | +- c
216 +- end
217 `
218 assert.Equal(expected, actual)
219 }
220
221 func TestRelationships(t *testing.T) {
222 assert := assert.New(t)
223
224 tree := New()
225 tree.AddBranch("one").AddNode("two")
226 foo := tree.AddBranch("foo")
227 foo.AddBranch("bar").AddNode("a").AddNode("b").AddNode("c")
228 foo.AddNode("end")
229
230 treeNode := tree.(*node)
231
232 assert.Nil(treeNode.Root)
233 assert.Len(treeNode.Nodes, 2)
234 assert.Equal(treeNode, treeNode.Nodes[0].Root)
235 assert.Equal(treeNode.Nodes[0], treeNode.Nodes[0].Nodes[0].Root)
236 }
237
238 func TestMultiline(t *testing.T) {
239 assert := assert.New(t)
240
241 multi1 := `I am
242 a multiline
243 value`
244
245 multi2 := `I have
246 many
247
248
249 empty lines`
250
251 multi3 := `I am another
252 multiple
253 lines value`
254
255 tree := New()
256 tree.AddBranch("one").AddMetaNode("meta", multi1)
257 tree.AddBranch("two")
258 foo := tree.AddBranch("foo")
259 foo.AddBranch("bar").AddNode("a").AddNode(multi2).AddNode("c")
260 foo.AddBranch(multi3)
261
262 actual := tree.String()
263 expected := `.
264 ├── one
265 │ └── [meta] I am
266 │ a multiline
267 │ value
268 ├── two
269 └── foo
270 ├── bar
271 │ ├── a
272 │ ├── I have
273 │ │ many
274 │ │
275 │ │
276 │ │ empty lines
277 │ └── c
278 └── I am another
279 multiple
280 lines value
281 `
282
283 assert.Equal(expected, actual)
284 }
285
286 func TestVisitAll(t *testing.T) {
287
288 tree := New()
289 one := tree.AddBranch("one")
290 one.AddNode("one-subnode1").AddNode("one-subnode2")
291 one.AddBranch("two").AddNode("two-subnode1").AddNode("two-subnode2").
292 AddBranch("three").AddNode("three-subnode1").AddNode("three-subnode2")
293 tree.AddNode("outernode")
294
295 var visitedNodeValues []Value
296 expectedNodeValues := []Value{
297 "one",
298 "one-subnode1",
299 "one-subnode2",
300 "two",
301 "two-subnode1",
302 "two-subnode2",
303 "three",
304 "three-subnode1",
305 "three-subnode2",
306 "outernode",
307 }
308
309 tree.VisitAll(func(item *node) {
310 visitedNodeValues = append(visitedNodeValues, item.Value)
311 })
312
313 assert := assert.New(t)
314 assert.Equal(expectedNodeValues, visitedNodeValues)
315
316 }