2024-03-03 10:39:10 +01:00

181 lines
2.8 KiB
Go

/*
Tree builder and viewer
This allows you to create a tree of files and display them as a tree or flat view.
Example
t := tree.New()
t.AddPath("a/b/c/d")
t.AddPath("a/b/e/f")
for v := range t.FlatView() {
fmt.Println(v)
}
for v := range t.TreeView() {
fmt.Println(v)
}
*/
package tree
import (
"path/filepath"
"sort"
"strings"
)
type Tree interface {
Add(string) Tree
AddPath(string) Tree
FlatView() chan string
TreeView() chan string
}
/*
Create a new tree
*/
func New() Tree {
return &node{
Name: ".",
}
}
type node struct {
Name string
Nodes map[string]*node
}
/*
Add a node to your tree and return it
You can chain it:
t.Add("a").Add("b").Add("c").Add("d")
Or
c := t
for _, s := range strings.Split("a/b/c/d", "/") {
c = c.Add(s)
}
*/
func (t *node) Add(name string) Tree {
if t.Nodes == nil {
t.Nodes = map[string]*node{}
}
n, ok := t.Nodes[name]
if !ok {
n = &node{Name: name}
t.Nodes[name] = n
}
return n
}
/*
Add a path to your tree, separated with /
t.AddPath("a/b/c/d")
*/
func (t *node) AddPath(path string) Tree {
n := Tree(t)
for _, name := range strings.Split(path, "/") {
n = n.Add(name)
}
return n
}
/*
Return a sorted list of children
*/
func (t *node) Children() []*node {
children := make([]*node, len(t.Nodes))
i := 0
for _, n := range t.Nodes {
children[i] = n
i++
}
sort.Slice(children, func(i, j int) bool {
return children[i].Name < children[j].Name
})
return children
}
/*
Check if your node has a children
*/
func (t *node) HasChildren() bool {
return t.Nodes != nil
}
/*
Return a flat view of your tree
for v := range t.FlatView() {
fmt.Println(v)
}
*/
func (t *node) FlatView() (out chan string) {
out = make(chan string)
go func() {
defer close(out)
var flatten func(string, *node)
flatten = func(path string, t *node) {
switch t.HasChildren() {
case false:
out <- path
case true:
for _, child := range t.Children() {
flatten(filepath.Join(path, child.Name), child)
}
}
}
flatten("", t)
}()
return out
}
/*
Return a tree view of your tree
for v := range t.TreeView() {
fmt.Println(v)
}
*/
func (t *node) TreeView() (out chan string) {
out = make(chan string)
treeLinkChar := "│ "
treeMidChar := "├── "
treeEndChar := "└── "
treeAfterEndChar := " "
go func() {
defer close(out)
var tree func(string, *node)
tree = func(prefix string, t *node) {
children := t.Children()
for i, st := range children {
switch i {
case len(children) - 1:
out <- prefix + treeEndChar + st.Name
tree(prefix+treeAfterEndChar, st)
case 0:
out <- prefix + treeMidChar + st.Name
tree(prefix+treeLinkChar, st)
default:
out <- prefix + treeMidChar + st.Name
tree(prefix+treeLinkChar, st)
}
}
}
out <- t.Name
tree("", t)
}()
return out
}