summaryrefslogtreecommitdiff
path: root/vendor/github.com/xlab/treeprint
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/xlab/treeprint')
-rw-r--r--vendor/github.com/xlab/treeprint/.gitignore3
-rw-r--r--vendor/github.com/xlab/treeprint/LICENSE20
-rw-r--r--vendor/github.com/xlab/treeprint/README.md154
-rw-r--r--vendor/github.com/xlab/treeprint/helpers.go47
-rw-r--r--vendor/github.com/xlab/treeprint/struct.go322
-rw-r--r--vendor/github.com/xlab/treeprint/treeprint.go294
6 files changed, 840 insertions, 0 deletions
diff --git a/vendor/github.com/xlab/treeprint/.gitignore b/vendor/github.com/xlab/treeprint/.gitignore
new file mode 100644
index 0000000..7c9305d
--- /dev/null
+++ b/vendor/github.com/xlab/treeprint/.gitignore
@@ -0,0 +1,3 @@
+vendor/**
+.idea
+**/**.iml
diff --git a/vendor/github.com/xlab/treeprint/LICENSE b/vendor/github.com/xlab/treeprint/LICENSE
new file mode 100644
index 0000000..5ab533a
--- /dev/null
+++ b/vendor/github.com/xlab/treeprint/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+Copyright © 2016 Maxim Kupriianov <max@kc.vc>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/xlab/treeprint/README.md b/vendor/github.com/xlab/treeprint/README.md
new file mode 100644
index 0000000..59fb121
--- /dev/null
+++ b/vendor/github.com/xlab/treeprint/README.md
@@ -0,0 +1,154 @@
+treeprint [![GoDoc](https://godoc.org/github.com/xlab/treeprint?status.svg)](https://godoc.org/github.com/xlab/treeprint) ![test coverage](https://img.shields.io/badge/coverage-68.6%25-green.svg)
+=========
+
+Package `treeprint` provides a simple ASCII tree composing tool.
+
+<a href="https://upload.wikimedia.org/wikipedia/commons/5/58/ENC_SYSTEME_FIGURE.jpeg"><img alt="SYSTEME FIGURE" src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/58/ENC_SYSTEME_FIGURE.jpeg/896px-ENC_SYSTEME_FIGURE.jpeg" align="left" width="300"></a>
+
+If you are familiar with the [tree](http://mama.indstate.edu/users/ice/tree/) utility that is a recursive directory listing command that produces a depth indented listing of files, then you have the idea of what it would look like.
+
+On my system the command yields the following
+
+```
+ $ tree
+.
+├── LICENSE
+├── README.md
+├── treeprint.go
+└── treeprint_test.go
+
+0 directories, 4 files
+```
+
+and I'd like to have the same format for my Go data structures when I print them.
+
+## Installation
+
+```
+$ go get github.com/xlab/treeprint
+```
+
+## Concept of work
+
+The general idea is that you initialise a new tree with `treeprint.New()` and then add nodes and
+branches into it. Use `AddNode()` when you want add a node on the same level as the target or
+use `AddBranch()` when you want to go a level deeper. So `tree.AddBranch().AddNode().AddNode()` would
+create a new level with two distinct nodes on it. So `tree.AddNode().AddNode()` is a flat thing and
+`tree.AddBranch().AddBranch().AddBranch()` is a high thing. Use `String()` or `Bytes()` on a branch
+to render a subtree, or use it on the root to print the whole tree.
+
+The utility will yield Unicode-friendly trees. The output is predictable and there is no platform-dependent exceptions, so if you have issues with displaying the tree in the console, all platform-related transformations can be done after the tree has been rendered: [an example](https://github.com/xlab/treeprint/issues/2#issuecomment-324944141) for Asian locales.
+
+## Use cases
+
+### When you want to render a complex data structure:
+
+```go
+func main() {
+ // to add a custom root name use `treeprint.NewWithRoot()` instead
+ tree := treeprint.New()
+
+ // create a new branch in the root
+ one := tree.AddBranch("one")
+
+ // add some nodes
+ one.AddNode("subnode1").AddNode("subnode2")
+
+ // create a new sub-branch
+ one.AddBranch("two").
+ AddNode("subnode1").AddNode("subnode2"). // add some nodes
+ AddBranch("three"). // add a new sub-branch
+ AddNode("subnode1").AddNode("subnode2") // add some nodes too
+
+ // add one more node that should surround the inner branch
+ one.AddNode("subnode3")
+
+ // add a new node to the root
+ tree.AddNode("outernode")
+
+ fmt.Println(tree.String())
+}
+```
+
+Will give you:
+
+```
+.
+├── one
+│   ├── subnode1
+│   ├── subnode2
+│   ├── two
+│   │   ├── subnode1
+│   │   ├── subnode2
+│   │   └── three
+│   │   ├── subnode1
+│   │   └── subnode2
+│   └── subnode3
+└── outernode
+```
+
+### Another case, when you have to make a tree where any leaf may have some meta-data (as `tree` is capable of it):
+
+```go
+func main {
+ // to add a custom root name use `treeprint.NewWithRoot()` instead
+ tree := treeprint.New()
+
+ tree.AddNode("Dockerfile")
+ tree.AddNode("Makefile")
+ tree.AddNode("aws.sh")
+ tree.AddMetaBranch(" 204", "bin").
+ AddNode("dbmaker").AddNode("someserver").AddNode("testtool")
+ tree.AddMetaBranch(" 374", "deploy").
+ AddNode("Makefile").AddNode("bootstrap.sh")
+ tree.AddMetaNode("122K", "testtool.a")
+
+ fmt.Println(tree.String())
+}
+```
+
+Output:
+
+```
+.
+├── Dockerfile
+├── Makefile
+├── aws.sh
+├── [ 204] bin
+│   ├── dbmaker
+│   ├── someserver
+│   └── testtool
+├── [ 374] deploy
+│   ├── Makefile
+│   └── bootstrap.sh
+└── [122K] testtool.a
+```
+
+### Iterating over the tree nodes
+
+```go
+tree := New()
+
+one := tree.AddBranch("one")
+one.AddNode("one-subnode1").AddNode("one-subnode2")
+one.AddBranch("two").AddNode("two-subnode1").AddNode("two-subnode2").
+ AddBranch("three").AddNode("three-subnode1").AddNode("three-subnode2")
+tree.AddNode("outernode")
+
+// if you need to iterate over the whole tree
+// call `VisitAll` from your top root node.
+tree.VisitAll(func(item *node) {
+ if len(item.Nodes) > 0 {
+ // branch nodes
+ fmt.Println(item.Value) // will output one, two, three
+ } else {
+ // leaf nodes
+ fmt.Println(item.Value) // will output one-*, two-*, three-* and outernode
+ }
+})
+
+```
+Yay! So it works.
+
+## License
+MIT
diff --git a/vendor/github.com/xlab/treeprint/helpers.go b/vendor/github.com/xlab/treeprint/helpers.go
new file mode 100644
index 0000000..a091a5a
--- /dev/null
+++ b/vendor/github.com/xlab/treeprint/helpers.go
@@ -0,0 +1,47 @@
+package treeprint
+
+import (
+ "reflect"
+ "strings"
+)
+
+func isEmpty(v *reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+ return v.Len() == 0
+ case reflect.Bool:
+ return !v.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return v.Float() == 0
+ case reflect.Interface, reflect.Ptr:
+ return v.IsNil()
+ }
+ return false
+}
+
+func tagSpec(tag string) (name string, omit bool) {
+ parts := strings.Split(tag, ",")
+ if len(parts) < 2 {
+ return tag, false
+ }
+ if parts[1] == "omitempty" {
+ return parts[0], true
+ }
+ return parts[0], false
+}
+
+func filterTags(tag reflect.StructTag) string {
+ tags := strings.Split(string(tag), " ")
+ filtered := make([]string, 0, len(tags))
+ for i := range tags {
+ if strings.HasPrefix(tags[i], "tree:") {
+ continue
+ }
+ filtered = append(filtered, tags[i])
+ }
+ return strings.Join(filtered, " ")
+}
diff --git a/vendor/github.com/xlab/treeprint/struct.go b/vendor/github.com/xlab/treeprint/struct.go
new file mode 100644
index 0000000..4d5cc82
--- /dev/null
+++ b/vendor/github.com/xlab/treeprint/struct.go
@@ -0,0 +1,322 @@
+package treeprint
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+type StructTreeOption int
+
+const (
+ StructNameTree StructTreeOption = iota
+ StructValueTree
+ StructTagTree
+ StructTypeTree
+ StructTypeSizeTree
+)
+
+func FromStruct(v interface{}, opt ...StructTreeOption) (Tree, error) {
+ var treeOpt StructTreeOption
+ if len(opt) > 0 {
+ treeOpt = opt[0]
+ }
+ switch treeOpt {
+ case StructNameTree:
+ tree := New()
+ err := nameTree(tree, v)
+ return tree, err
+ case StructValueTree:
+ tree := New()
+ err := valueTree(tree, v)
+ return tree, err
+ case StructTagTree:
+ tree := New()
+ err := tagTree(tree, v)
+ return tree, err
+ case StructTypeTree:
+ tree := New()
+ err := typeTree(tree, v)
+ return tree, err
+ case StructTypeSizeTree:
+ tree := New()
+ err := typeSizeTree(tree, v)
+ return tree, err
+ default:
+ err := fmt.Errorf("treeprint: invalid StructTreeOption %v", treeOpt)
+ return nil, err
+ }
+}
+
+type FmtFunc func(name string, v interface{}) (string, bool)
+
+func FromStructWithMeta(v interface{}, fmtFunc FmtFunc) (Tree, error) {
+ if fmtFunc == nil {
+ tree := New()
+ err := nameTree(tree, v)
+ return tree, err
+ }
+ tree := New()
+ err := metaTree(tree, v, fmtFunc)
+ return tree, err
+}
+
+func Repr(v interface{}) string {
+ tree := New()
+ vType := reflect.TypeOf(v)
+ vValue := reflect.ValueOf(v)
+ _, val, isStruct := getValue(vType, &vValue)
+ if !isStruct {
+ return fmt.Sprintf("%+v", val.Interface())
+ }
+ err := valueTree(tree, val.Interface())
+ if err != nil {
+ return err.Error()
+ }
+ return tree.String()
+}
+
+func nameTree(tree Tree, v interface{}) error {
+ typ, val, err := checkType(v)
+ if err != nil {
+ return err
+ }
+ fields := typ.NumField()
+ for i := 0; i < fields; i++ {
+ field := typ.Field(i)
+ fieldValue := val.Field(i)
+ name, skip, omit := getMeta(field.Name, field.Tag)
+ if skip || omit && isEmpty(&fieldValue) {
+ continue
+ }
+ typ, val, isStruct := getValue(field.Type, &fieldValue)
+ if !isStruct {
+ tree.AddNode(name)
+ continue
+ } else if subNum := typ.NumField(); subNum == 0 {
+ tree.AddNode(name)
+ continue
+ }
+ branch := tree.AddBranch(name)
+ if err := nameTree(branch, val.Interface()); err != nil {
+ err := fmt.Errorf("%v on struct branch %s", err, name)
+ return err
+ }
+ }
+ return nil
+}
+
+func getMeta(fieldName string, tag reflect.StructTag) (name string, skip, omit bool) {
+ if tagStr := tag.Get("tree"); len(tagStr) > 0 {
+ name, omit = tagSpec(tagStr)
+ }
+ if name == "-" {
+ return fieldName, true, omit
+ }
+ if len(name) == 0 {
+ name = fieldName
+ } else if trimmed := strings.TrimSpace(name); len(trimmed) == 0 {
+ name = fieldName
+ }
+ return
+}
+
+func valueTree(tree Tree, v interface{}) error {
+ typ, val, err := checkType(v)
+ if err != nil {
+ return err
+ }
+ fields := typ.NumField()
+ for i := 0; i < fields; i++ {
+ field := typ.Field(i)
+ fieldValue := val.Field(i)
+ name, skip, omit := getMeta(field.Name, field.Tag)
+ if skip || omit && isEmpty(&fieldValue) {
+ continue
+ }
+ typ, val, isStruct := getValue(field.Type, &fieldValue)
+ if !isStruct {
+ tree.AddMetaNode(val.Interface(), name)
+ continue
+ } else if subNum := typ.NumField(); subNum == 0 {
+ tree.AddMetaNode(val.Interface(), name)
+ continue
+ }
+ branch := tree.AddBranch(name)
+ if err := valueTree(branch, val.Interface()); err != nil {
+ err := fmt.Errorf("%v on struct branch %s", err, name)
+ return err
+ }
+ }
+ return nil
+}
+
+func tagTree(tree Tree, v interface{}) error {
+ typ, val, err := checkType(v)
+ if err != nil {
+ return err
+ }
+ fields := typ.NumField()
+ for i := 0; i < fields; i++ {
+ field := typ.Field(i)
+ fieldValue := val.Field(i)
+ name, skip, omit := getMeta(field.Name, field.Tag)
+ if skip || omit && isEmpty(&fieldValue) {
+ continue
+ }
+ filteredTag := filterTags(field.Tag)
+ typ, val, isStruct := getValue(field.Type, &fieldValue)
+ if !isStruct {
+ tree.AddMetaNode(filteredTag, name)
+ continue
+ } else if subNum := typ.NumField(); subNum == 0 {
+ tree.AddMetaNode(filteredTag, name)
+ continue
+ }
+ branch := tree.AddMetaBranch(filteredTag, name)
+ if err := tagTree(branch, val.Interface()); err != nil {
+ err := fmt.Errorf("%v on struct branch %s", err, name)
+ return err
+ }
+ }
+ return nil
+}
+
+func typeTree(tree Tree, v interface{}) error {
+ typ, val, err := checkType(v)
+ if err != nil {
+ return err
+ }
+ fields := typ.NumField()
+ for i := 0; i < fields; i++ {
+ field := typ.Field(i)
+ fieldValue := val.Field(i)
+ name, skip, omit := getMeta(field.Name, field.Tag)
+ if skip || omit && isEmpty(&fieldValue) {
+ continue
+ }
+ typ, val, isStruct := getValue(field.Type, &fieldValue)
+ typename := fmt.Sprintf("%T", val.Interface())
+ if !isStruct {
+ tree.AddMetaNode(typename, name)
+ continue
+ } else if subNum := typ.NumField(); subNum == 0 {
+ tree.AddMetaNode(typename, name)
+ continue
+ }
+ branch := tree.AddMetaBranch(typename, name)
+ if err := typeTree(branch, val.Interface()); err != nil {
+ err := fmt.Errorf("%v on struct branch %s", err, name)
+ return err
+ }
+ }
+ return nil
+}
+
+func typeSizeTree(tree Tree, v interface{}) error {
+ typ, val, err := checkType(v)
+ if err != nil {
+ return err
+ }
+ fields := typ.NumField()
+ for i := 0; i < fields; i++ {
+ field := typ.Field(i)
+ fieldValue := val.Field(i)
+ name, skip, omit := getMeta(field.Name, field.Tag)
+ if skip || omit && isEmpty(&fieldValue) {
+ continue
+ }
+ typ, val, isStruct := getValue(field.Type, &fieldValue)
+ typesize := typ.Size()
+ if !isStruct {
+ tree.AddMetaNode(typesize, name)
+ continue
+ } else if subNum := typ.NumField(); subNum == 0 {
+ tree.AddMetaNode(typesize, name)
+ continue
+ }
+ branch := tree.AddMetaBranch(typesize, name)
+ if err := typeSizeTree(branch, val.Interface()); err != nil {
+ err := fmt.Errorf("%v on struct branch %s", err, name)
+ return err
+ }
+ }
+ return nil
+}
+
+func metaTree(tree Tree, v interface{}, fmtFunc FmtFunc) error {
+ typ, val, err := checkType(v)
+ if err != nil {
+ return err
+ }
+ fields := typ.NumField()
+ for i := 0; i < fields; i++ {
+ field := typ.Field(i)
+ fieldValue := val.Field(i)
+ name, skip, omit := getMeta(field.Name, field.Tag)
+ if skip || omit && isEmpty(&fieldValue) {
+ continue
+ }
+ typ, val, isStruct := getValue(field.Type, &fieldValue)
+ formatted, show := fmtFunc(name, val.Interface())
+ if !isStruct {
+ if show {
+ tree.AddMetaNode(formatted, name)
+ continue
+ }
+ tree.AddNode(name)
+ continue
+ } else if subNum := typ.NumField(); subNum == 0 {
+ if show {
+ tree.AddMetaNode(formatted, name)
+ continue
+ }
+ tree.AddNode(name)
+ continue
+ }
+ var branch Tree
+ if show {
+ branch = tree.AddMetaBranch(formatted, name)
+ } else {
+ branch = tree.AddBranch(name)
+ }
+ if err := metaTree(branch, val.Interface(), fmtFunc); err != nil {
+ err := fmt.Errorf("%v on struct branch %s", err, name)
+ return err
+ }
+ }
+ return nil
+}
+
+func getValue(typ reflect.Type, val *reflect.Value) (reflect.Type, *reflect.Value, bool) {
+ switch typ.Kind() {
+ case reflect.Ptr:
+ typ = typ.Elem()
+ if typ.Kind() == reflect.Struct {
+ elem := val.Elem()
+ return typ, &elem, true
+ }
+ case reflect.Struct:
+ return typ, val, true
+ }
+ return typ, val, false
+}
+
+func checkType(v interface{}) (reflect.Type, *reflect.Value, error) {
+ typ := reflect.TypeOf(v)
+ val := reflect.ValueOf(v)
+ switch typ.Kind() {
+ case reflect.Ptr:
+ typ = typ.Elem()
+ if typ.Kind() != reflect.Struct {
+ err := fmt.Errorf("treeprint: %T is not a struct we could work with", v)
+ return nil, nil, err
+ }
+ val = val.Elem()
+ case reflect.Struct:
+ default:
+ err := fmt.Errorf("treeprint: %T is not a struct we could work with", v)
+ return nil, nil, err
+ }
+ return typ, &val, nil
+}
diff --git a/vendor/github.com/xlab/treeprint/treeprint.go b/vendor/github.com/xlab/treeprint/treeprint.go
new file mode 100644
index 0000000..fc8204b
--- /dev/null
+++ b/vendor/github.com/xlab/treeprint/treeprint.go
@@ -0,0 +1,294 @@
+// Package treeprint provides a simple ASCII tree composing tool.
+package treeprint
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "reflect"
+ "strings"
+)
+
+// Value defines any value
+type Value interface{}
+
+// MetaValue defines any meta value
+type MetaValue interface{}
+
+// NodeVisitor function type for iterating over nodes
+type NodeVisitor func(item *Node)
+
+// Tree represents a tree structure with leaf-nodes and branch-nodes.
+type Tree interface {
+ // AddNode adds a new Node to a branch.
+ AddNode(v Value) Tree
+ // AddMetaNode adds a new Node with meta value provided to a branch.
+ AddMetaNode(meta MetaValue, v Value) Tree
+ // AddBranch adds a new branch Node (a level deeper).
+ AddBranch(v Value) Tree
+ // AddMetaBranch adds a new branch Node (a level deeper) with meta value provided.
+ AddMetaBranch(meta MetaValue, v Value) Tree
+ // Branch converts a leaf-Node to a branch-Node,
+ // applying this on a branch-Node does no effect.
+ Branch() Tree
+ // FindByMeta finds a Node whose meta value matches the provided one by reflect.DeepEqual,
+ // returns nil if not found.
+ FindByMeta(meta MetaValue) Tree
+ // FindByValue finds a Node whose value matches the provided one by reflect.DeepEqual,
+ // returns nil if not found.
+ FindByValue(value Value) Tree
+ // returns the last Node of a tree
+ FindLastNode() Tree
+ // String renders the tree or subtree as a string.
+ String() string
+ // Bytes renders the tree or subtree as byteslice.
+ Bytes() []byte
+
+ SetValue(value Value)
+ SetMetaValue(meta MetaValue)
+
+ // VisitAll iterates over the tree, branches and nodes.
+ // If need to iterate over the whole tree, use the root Node.
+ // Note this method uses a breadth-first approach.
+ VisitAll(fn NodeVisitor)
+}
+
+type Node struct {
+ Root *Node
+ Meta MetaValue
+ Value Value
+ Nodes []*Node
+}
+
+func (n *Node) FindLastNode() Tree {
+ ns := n.Nodes
+ if len(ns) == 0 {
+ return nil
+ }
+ return ns[len(ns)-1]
+}
+
+func (n *Node) AddNode(v Value) Tree {
+ n.Nodes = append(n.Nodes, &Node{
+ Root: n,
+ Value: v,
+ })
+ return n
+}
+
+func (n *Node) AddMetaNode(meta MetaValue, v Value) Tree {
+ n.Nodes = append(n.Nodes, &Node{
+ Root: n,
+ Meta: meta,
+ Value: v,
+ })
+ return n
+}
+
+func (n *Node) AddBranch(v Value) Tree {
+ branch := &Node{
+ Root: n,
+ Value: v,
+ }
+ n.Nodes = append(n.Nodes, branch)
+ return branch
+}
+
+func (n *Node) AddMetaBranch(meta MetaValue, v Value) Tree {
+ branch := &Node{
+ Root: n,
+ Meta: meta,
+ Value: v,
+ }
+ n.Nodes = append(n.Nodes, branch)
+ return branch
+}
+
+func (n *Node) Branch() Tree {
+ n.Root = nil
+ return n
+}
+
+func (n *Node) FindByMeta(meta MetaValue) Tree {
+ for _, node := range n.Nodes {
+ if reflect.DeepEqual(node.Meta, meta) {
+ return node
+ }
+ if v := node.FindByMeta(meta); v != nil {
+ return v
+ }
+ }
+ return nil
+}
+
+func (n *Node) FindByValue(value Value) Tree {
+ for _, node := range n.Nodes {
+ if reflect.DeepEqual(node.Value, value) {
+ return node
+ }
+ if v := node.FindByMeta(value); v != nil {
+ return v
+ }
+ }
+ return nil
+}
+
+func (n *Node) Bytes() []byte {
+ buf := new(bytes.Buffer)
+ level := 0
+ var levelsEnded []int
+ if n.Root == nil {
+ if n.Meta != nil {
+ buf.WriteString(fmt.Sprintf("[%v] %v", n.Meta, n.Value))
+ } else {
+ buf.WriteString(fmt.Sprintf("%v", n.Value))
+ }
+ buf.WriteByte('\n')
+ } else {
+ edge := EdgeTypeMid
+ if len(n.Nodes) == 0 {
+ edge = EdgeTypeEnd
+ levelsEnded = append(levelsEnded, level)
+ }
+ printValues(buf, 0, levelsEnded, edge, n)
+ }
+ if len(n.Nodes) > 0 {
+ printNodes(buf, level, levelsEnded, n.Nodes)
+ }
+ return buf.Bytes()
+}
+
+func (n *Node) String() string {
+ return string(n.Bytes())
+}
+
+func (n *Node) SetValue(value Value) {
+ n.Value = value
+}
+
+func (n *Node) SetMetaValue(meta MetaValue) {
+ n.Meta = meta
+}
+
+func (n *Node) VisitAll(fn NodeVisitor) {
+ for _, node := range n.Nodes {
+ fn(node)
+
+ if len(node.Nodes) > 0 {
+ node.VisitAll(fn)
+ continue
+ }
+ }
+}
+
+func printNodes(wr io.Writer,
+ level int, levelsEnded []int, nodes []*Node) {
+
+ for i, node := range nodes {
+ edge := EdgeTypeMid
+ if i == len(nodes)-1 {
+ levelsEnded = append(levelsEnded, level)
+ edge = EdgeTypeEnd
+ }
+ printValues(wr, level, levelsEnded, edge, node)
+ if len(node.Nodes) > 0 {
+ printNodes(wr, level+1, levelsEnded, node.Nodes)
+ }
+ }
+}
+
+func printValues(wr io.Writer,
+ level int, levelsEnded []int, edge EdgeType, node *Node) {
+
+ for i := 0; i < level; i++ {
+ if isEnded(levelsEnded, i) {
+ fmt.Fprint(wr, strings.Repeat(" ", IndentSize+1))
+ continue
+ }
+ fmt.Fprintf(wr, "%s%s", EdgeTypeLink, strings.Repeat(" ", IndentSize))
+ }
+
+ val := renderValue(level, node)
+ meta := node.Meta
+
+ if meta != nil {
+ fmt.Fprintf(wr, "%s [%v] %v\n", edge, meta, val)
+ return
+ }
+ fmt.Fprintf(wr, "%s %v\n", edge, val)
+}
+
+func isEnded(levelsEnded []int, level int) bool {
+ for _, l := range levelsEnded {
+ if l == level {
+ return true
+ }
+ }
+ return false
+}
+
+func renderValue(level int, node *Node) Value {
+ lines := strings.Split(fmt.Sprintf("%v", node.Value), "\n")
+
+ // If value does not contain multiple lines, return itself.
+ if len(lines) < 2 {
+ return node.Value
+ }
+
+ // If value contains multiple lines,
+ // generate a padding and prefix each line with it.
+ pad := padding(level, node)
+
+ for i := 1; i < len(lines); i++ {
+ lines[i] = fmt.Sprintf("%s%s", pad, lines[i])
+ }
+
+ return strings.Join(lines, "\n")
+}
+
+// padding returns a padding for the multiline values with correctly placed link edges.
+// It is generated by traversing the tree upwards (from leaf to the root of the tree)
+// and, on each level, checking if the Node the last one of its siblings.
+// If a Node is the last one, the padding on that level should be empty (there's nothing to link to below it).
+// 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.
+func padding(level int, node *Node) string {
+ links := make([]string, level+1)
+
+ for node.Root != nil {
+ if isLast(node) {
+ links[level] = strings.Repeat(" ", IndentSize+1)
+ } else {
+ links[level] = fmt.Sprintf("%s%s", EdgeTypeLink, strings.Repeat(" ", IndentSize))
+ }
+ level--
+ node = node.Root
+ }
+
+ return strings.Join(links, "")
+}
+
+// isLast checks if the Node is the last one in the slice of its parent children
+func isLast(n *Node) bool {
+ return n == n.Root.FindLastNode()
+}
+
+type EdgeType string
+
+var (
+ EdgeTypeLink EdgeType = "│"
+ EdgeTypeMid EdgeType = "├──"
+ EdgeTypeEnd EdgeType = "└──"
+)
+
+// IndentSize is the number of spaces per tree level.
+var IndentSize = 3
+
+// New Generates new tree
+func New() Tree {
+ return &Node{Value: "."}
+}
+
+// NewWithRoot Generates new tree with the given root value
+func NewWithRoot(root Value) Tree {
+ return &Node{Value: root}
+}