vendoring of treeprint library
This commit is contained in:
parent
ccb3a7c584
commit
8b037432e7
|
@ -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.
|
|
@ -0,0 +1,126 @@
|
|||
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.
|
||||
|
||||
## Use cases
|
||||
|
||||
When you want to render a complex data structure:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
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 {
|
||||
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
|
||||
```
|
||||
|
||||
Yay! So it works.
|
||||
|
||||
## License
|
||||
MIT
|
|
@ -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, " ")
|
||||
}
|
|
@ -0,0 +1,340 @@
|
|||
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 !val.CanSet() {
|
||||
continue
|
||||
}
|
||||
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", 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 !val.CanSet() {
|
||||
continue
|
||||
}
|
||||
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", 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 !val.CanSet() {
|
||||
continue
|
||||
}
|
||||
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", 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)
|
||||
if !val.CanSet() {
|
||||
continue
|
||||
}
|
||||
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", 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)
|
||||
if !val.CanSet() {
|
||||
continue
|
||||
}
|
||||
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", 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)
|
||||
if !val.CanSet() {
|
||||
continue
|
||||
}
|
||||
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", 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
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
// Package treeprint provides a simple ASCII tree composing tool.
|
||||
package treeprint
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type Value interface{}
|
||||
type MetaValue interface{}
|
||||
|
||||
// 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
|
||||
// String renders the tree or subtree as a string.
|
||||
String() string
|
||||
// Bytes renders the tree or subtree as byteslice.
|
||||
Bytes() []byte
|
||||
}
|
||||
|
||||
type node struct {
|
||||
Root *node
|
||||
Meta MetaValue
|
||||
Value Value
|
||||
Nodes []*node
|
||||
}
|
||||
|
||||
func (n *node) AddNode(v Value) Tree {
|
||||
n.Nodes = append(n.Nodes, &node{
|
||||
Root: n,
|
||||
Value: v,
|
||||
})
|
||||
if n.Root != nil {
|
||||
return n.Root
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *node) AddMetaNode(meta MetaValue, v Value) Tree {
|
||||
n.Nodes = append(n.Nodes, &node{
|
||||
Root: n,
|
||||
Meta: meta,
|
||||
Value: v,
|
||||
})
|
||||
if n.Root != nil {
|
||||
return n.Root
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *node) AddBranch(v Value) Tree {
|
||||
branch := &node{
|
||||
Value: v,
|
||||
}
|
||||
n.Nodes = append(n.Nodes, branch)
|
||||
return branch
|
||||
}
|
||||
|
||||
func (n *node) AddMetaBranch(meta MetaValue, v Value) Tree {
|
||||
branch := &node{
|
||||
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
|
||||
levelEnded := make(map[int]bool)
|
||||
if n.Root == nil {
|
||||
buf.WriteString(string(EdgeTypeStart))
|
||||
buf.WriteByte('\n')
|
||||
} else {
|
||||
edge := EdgeTypeMid
|
||||
if len(n.Nodes) == 0 {
|
||||
edge = EdgeTypeEnd
|
||||
levelEnded[level] = true
|
||||
}
|
||||
printValues(buf, 0, levelEnded, edge, n.Meta, n.Value)
|
||||
}
|
||||
if len(n.Nodes) > 0 {
|
||||
printNodes(buf, level, levelEnded, n.Nodes)
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (n *node) String() string {
|
||||
return string(n.Bytes())
|
||||
}
|
||||
|
||||
func printNodes(wr io.Writer,
|
||||
level int, levelEnded map[int]bool, nodes []*node) {
|
||||
|
||||
for i, node := range nodes {
|
||||
edge := EdgeTypeMid
|
||||
if i == len(nodes)-1 {
|
||||
levelEnded[level] = true
|
||||
edge = EdgeTypeEnd
|
||||
}
|
||||
printValues(wr, level, levelEnded, edge, node.Meta, node.Value)
|
||||
if len(node.Nodes) > 0 {
|
||||
printNodes(wr, level+1, levelEnded, node.Nodes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printValues(wr io.Writer,
|
||||
level int, levelEnded map[int]bool, edge EdgeType, meta MetaValue, val Value) {
|
||||
|
||||
for i := 0; i < level; i++ {
|
||||
if levelEnded[i] {
|
||||
fmt.Fprint(wr, " ")
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(wr, "%s ", EdgeTypeLink)
|
||||
}
|
||||
if meta != nil {
|
||||
fmt.Fprintf(wr, "%s [%v] %v\n", edge, meta, val)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(wr, "%s %v\n", edge, val)
|
||||
}
|
||||
|
||||
type EdgeType string
|
||||
|
||||
const (
|
||||
EdgeTypeStart EdgeType = "."
|
||||
EdgeTypeLink EdgeType = "│"
|
||||
EdgeTypeMid EdgeType = "├──"
|
||||
EdgeTypeEnd EdgeType = "└──"
|
||||
)
|
||||
|
||||
func New() Tree {
|
||||
return &node{}
|
||||
}
|
|
@ -3210,6 +3210,12 @@
|
|||
"revision": "ff0417f4272e480246b4507459b3f6ae721a87ac",
|
||||
"revisionTime": "2017-02-25T17:21:24Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "xtw+Llokq30p1Gn+Q8JBZ7NtE+I=",
|
||||
"path": "github.com/xlab/treeprint",
|
||||
"revision": "1d6e342255576c977e946a2384fc487a22d3fceb",
|
||||
"revisionTime": "2016-10-29T10:40:18Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "eXEiPlpDRaamJQ4vPX/9t333kQc=",
|
||||
"comment": "v1.5.4-13-g75ce5fb",
|
||||
|
|
Loading…
Reference in New Issue