package main import ( "errors" "sort" ) const ( newLine = "\n" emptySpace = " " middleItem = "├── " continueItem = "│ " lastItem = "└── " ) type Tree struct { root Node size int } type Node struct { tree *Tree parent *Node name string data interface{} children map[string]*Node } func NewTree() (tree *Tree) { tree = new(Tree) tree.size = 0 root := &tree.root root.tree = tree root.name = "" root.children = make(map[string]*Node) return tree } func NewNode(parent *Node, name string, data interface{}) (node *Node) { node = new(Node) node.name = name node.data = data node.children = make(map[string]*Node) node.parent = parent node.tree = parent.tree return node } func (tree *Tree) Root() *Node { return &tree.root } func (node *Node) AddChild(name string, data interface{}) (child *Node, error error) { if node.children[name] != nil { return nil, errors.New("Tree node already exists") } child = NewNode(node, name, data) node.children[name] = child node.tree.size++ return child, nil } func (node *Node) Remove() error { for _, child := range node.children { child.Remove() } delete(node.parent.children, node.name) node.tree.size-- return nil } func (node *Node) String() string { return node.name } func (tree *Tree) String() string { var renderLine func(string, []bool, bool) string var walkTree func(*Node, []bool) string renderLine = func(text string, spaces []bool, last bool) string { var result string for _, space := range spaces { if space { result += emptySpace } else { result += continueItem } } indicator := middleItem if last { indicator = lastItem } return result + indicator + text + newLine } walkTree = func(node *Node, spaces []bool) string { var result string var keys []string for key := range node.children { keys = append(keys, key) } sort.Strings(keys) for idx, name := range keys { child := node.children[name] last := idx == (len(node.children) - 1) result += renderLine(child.String(), spaces, last) if len(child.children) > 0 { spacesChild := append(spaces, last) result += walkTree(child, spacesChild) } } return result } return "." + newLine + walkTree(tree.Root(), []bool{}) }