asciitree

package module
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2024 License: Apache-2.0 Imports: 4 Imported by: 5

README

asciitree

Go Reference build and test Go Report Card Coverage

asciitree is a Go package for simple pretty-printing of tree-like data structures using pure ASCII "edges" or alternatively Unicode characters for drawing branches and edges.

root1
├── 1
├── 2
│   ├── 2.1
│   └── 2.2
└── 3
    └── 3.1
root2
└── X

Nodes can optionally be sorted by their labels. In addition, nodes may have properties (these are flat, so no properties of properties). These properties can also optionally be sorted.

asciitree is Copyright 2018‒2023 Harald Albrecht, and licensed under the Apache License, Version 2.0.

Documentation

Overview

Package asciitree pretty-prints hierarchical node data structures as ASCII trees. Asciitree offers different styles, "pure ASCII" and Unicode-based line "graphics" (see illustration below).

root1
├── 1
├── 2
│   ├── 2.1
│   └── 2.2
└── 3
    └── 3.1
root2
└── X

User-defined tree data structures can automatically be traversed if they are either structs or maps and they have been tagged. In many situations this avoids having to write adaptors (or more specific: visitors) that adapt your user data to the form needed by asciitree. With simple tagging, all you need to do is tag your data structure, such as in:

type node struct {
    Label    string   `asciitree:"label"`
    Props    []string `asciitree:"properties"`
    Children []node   `asciitree:"children"`
}

Asciitree can both work with a single-root tree, as well as with multiple roots. Simply pass the Render() function either a single root node, or a slice of root nodes, and it will handle both cases automatically. You can also pass a user-data struct with only an `asciitree:"roots"` tag, attached to a struct field storing your root nodes. Traversal will then proceed as usual.

In addition to automatically rendering tagged user-data structs, asciitree can also automatically traverse maps if those follow these rules: (1) your map must be of type "map[string]interface{}". And (2), your map needs to use the well-known map keys "label", "properties", and "children". If one or more of these keys is missing, asciitree will assume them to be zero. Finally, you can optionally pass in a top-level map with the well-known map key "roots" holding your root nodes. Or you can pass in a slide of root nodes. The Render() function will detect these use case automatically and handle them accordingly.

Example
package main

import (
	"fmt"

	asciitree "github.com/thediveo/go-asciitree"
)

func main() {
	// user-defined tree data structure with asciitree-related field tags.
	type node struct {
		Label    string   `asciitree:"label"`
		Props    []string `asciitree:"properties"`
		Children []node   `asciitree:"children"`
	}
	// set up a tree of nodes.
	root := node{
		Label: "root",
		Children: []node{
			{Label: "child 1", Props: []string{"childish"}},
			{Label: "child 2", Children: []node{
				{Label: "grandchild 1", Props: []string{"very childish"}},
				{Label: "grandchild 2"},
			}},
			{Label: "child 3"},
		},
	}
	// render the tree into a string and print it.
	fmt.Println(asciitree.RenderFancy(root))
}
Output:

root
├─ child 1
│     • childish
├─ child 2
│  ├─ grandchild 1
│  │     • very childish
│  └─ grandchild 2
└─ child 3
Example (MultiRoot)
package main

import (
	"fmt"

	asciitree "github.com/thediveo/go-asciitree"
)

func main() {
	// user-defined tree data structure with asciitree-related field tags.
	type node struct {
		Label    string   `asciitree:"label"`
		Props    []string `asciitree:"properties"`
		Children []node   `asciitree:"children"`
	}
	// set up two root nodes with their own tree of child nodes
	roots := []node{
		{
			Label: "root 1",
			Children: []node{
				{Label: "child 1", Props: []string{"childish"}},
				{Label: "child 2", Children: []node{
					{Label: "grandchild 1", Props: []string{"very childish"}},
					{Label: "grandchild 2"},
				}},
				{Label: "child 3"},
			},
		},
		{
			Label: "root 2",
			Children: []node{
				{Label: "child 2-1"},
			},
		},
	}
	// render the trees into a string and print it.
	fmt.Println(asciitree.RenderFancy(roots))
}
Output:

root 1
├─ child 1
│     • childish
├─ child 2
│  ├─ grandchild 1
│  │     • very childish
│  └─ grandchild 2
└─ child 3
root 2
└─ child 2-1
Example (Sorted)
package main

import (
	"fmt"

	asciitree "github.com/thediveo/go-asciitree"
)

func main() {
	// user-defined tree data structure with asciitree-related field tags.
	type node struct {
		Label    string   `asciitree:"label"`
		Props    []string `asciitree:"properties"`
		Children []node   `asciitree:"children"`
	}
	// set up a tree of nodes.
	rootb := node{
		Label: "beta root",
		Children: []node{
			{Label: "foo", Props: []string{"childish"}},
			{Label: "alpha", Children: []node{
				{Label: "grandchild 2"},
				{Label: "grandchild 1", Props: []string{"very childish"}},
			}},
			{Label: "bar"},
		},
	}
	roota := node{
		Label: "alpha root",
		Children: []node{
			{Label: "alphachild"},
		},
	}
	// create a new visitor and tell it to sort the nodes by label, and also
	// to sort the properties.
	sortingVisitor := asciitree.NewMapStructVisitor(true, true)
	// render the tree(s) into a string and print it.
	fmt.Println(asciitree.Render([]node{rootb, roota}, sortingVisitor, asciitree.LineTreeStyler))
}
Output:

alpha root
└─ alphachild
beta root
├─ alpha
│  ├─ grandchild 1
│  │     • very childish
│  └─ grandchild 2
├─ bar
└─ foo
      • childish

Index

Examples

Constants

This section is empty.

Variables

View Source
var ASCIIStyle = TreeStyle{
	Fork:     "+",
	Nodeconn: "-",
	Nofork:   "|",
	Lastnode: "`",
	Property: "*",
}

ASCIIStyle styles rendered trees using only "pure" ASCII characters, without any help of special line or box characters.

View Source
var DefaultTreeStyler = NewTreeStyler(ASCIIStyle)

DefaultTreeStyler offers a pure ASCII tree styler, using only "safe" ASCII characters, but no Unicode characters. Ideal for the lovers of unwatered ASCII art.

View Source
var DefaultVisitor = &MapStructVisitor{}

DefaultVisitor provides visitor capable of traversing (annotated) maps and structs.

View Source
var LineStyle = TreeStyle{
	Fork:     "├",
	Nodeconn: "─",
	Nofork:   "│",
	Lastnode: "└",
	Property: "•",
}

LineStyle styles ASCII trees using Unicode line characters.

View Source
var LineTreeStyler = NewTreeStyler(LineStyle)

LineTreeStyler uses Unicode box characters to draw slightly fancy tree branches.

Functions

func Render

func Render(roots interface{}, visitor Visitor, styler *TreeStyler) string

Render renders a tree (or multi-root tree) into a multi-line text string, using the supplied visitor and tree styler.

The roots can be specified as a slice of structs, or also as a single struct. In every case, the passed root(s), as well as their subtree nodes need to have two struct fields exported and tagged as `asciitree:"label"` and `asciitree:"children"` respectively.

For the visitor, you might want to simply use the DefaultVisitor that handles annotated structs and maps with well-known keys.

As a styler, simply use DefaultTreeStyler, or the slightly more fancyful NewTreeStyler(LineStyle).

Example
package main

import (
	"fmt"

	asciitree "github.com/thediveo/go-asciitree"
)

func main() {
	// user-defined tree data structure with asciitree-related field tags.
	type tree struct {
		Label    string `asciitree:"label"`
		Children []tree `asciitree:"children"`
	}
	// set up a tree of nodes.
	root := tree{
		Label: "root",
		Children: []tree{
			{Label: "child 1"},
			{Label: "child 2", Children: []tree{
				{Label: "grandchild 1"},
				{Label: "grandchild 2"},
			}},
			{Label: "child 3"},
		},
	}
	// render the tree into a string and print it.
	fmt.Println(
		asciitree.Render(root,
			asciitree.DefaultVisitor,
			asciitree.DefaultTreeStyler))
}
Output:

root
+- child 1
+- child 2
|  +- grandchild 1
|  `- grandchild 2
`- child 3

func RenderFancy

func RenderFancy(roots interface{}) string

RenderFancy works like RenderPlain, rendering a tree or multi-root tree into a multi-line text string, but it uses Unicode box characters to render the branch lines.

Example
package main

import (
	"fmt"

	asciitree "github.com/thediveo/go-asciitree"
)

func main() {
	// user-defined tree data structure with asciitree-related field tags.
	type tree struct {
		Label    string `asciitree:"label"`
		Children []tree `asciitree:"children"`
	}
	// set up a tree of nodes.
	root := tree{
		Label: "root",
		Children: []tree{
			{Label: "child 1"},
			{Label: "child 2", Children: []tree{
				{Label: "grandchild 1"},
				{Label: "grandchild 2"},
			}},
			{Label: "child 3"},
		},
	}
	// render the tree into a string and print it.
	fmt.Println(asciitree.RenderFancy(root))
}
Output:

root
├─ child 1
├─ child 2
│  ├─ grandchild 1
│  └─ grandchild 2
└─ child 3

func RenderPlain

func RenderPlain(roots interface{}) string

RenderPlain renders a tree or multi-root tree into a multi-line text string using the default tree styling and (user data) visitor.

The roots can be specified as a slice of structs, or also as a single struct. In every case, the passed root(s), as well as their subtree nodes need to have two struct fields exported and tagged as `asciitree:"label"` and `asciitree:"children"` respectively.

Example
package main

import (
	"fmt"

	asciitree "github.com/thediveo/go-asciitree"
)

func main() {
	// user-defined tree data structure with asciitree-related field tags.
	type tree struct {
		Label    string `asciitree:"label"`
		Children []tree `asciitree:"children"`
	}
	// set up a tree of nodes.
	root := tree{
		Label: "root",
		Children: []tree{
			{Label: "child 1"},
			{Label: "child 2", Children: []tree{
				{Label: "grandchild 1"},
				{Label: "grandchild 2"},
			}},
			{Label: "child 3"},
		},
	}
	// render the tree into a string and print it.
	fmt.Println(asciitree.RenderPlain(root))
}
Output:

root
+- child 1
+- child 2
|  +- grandchild 1
|  `- grandchild 2
`- child 3

Types

type MapStructVisitor

type MapStructVisitor struct {
	Visitor
	SortNodes      bool
	SortProperties bool
}

MapStructVisitor visits tagged ("annotated") user-defined structs as well as maps (the latter using well-known keys) and retrieves their tree-relevant data. For convenience, it also handles slices and pointers to structs and maps.

func NewMapStructVisitor

func NewMapStructVisitor(sortNodes bool, sortProperties bool) *MapStructVisitor

NewMapStructVisitor creates a visitor that optionally sorts nodes and their properties.

func (*MapStructVisitor) Get

func (v *MapStructVisitor) Get(node reflect.Value) (label string, properties []string, children reflect.Value)

Get returns the label, properties, and children of a tree node, hiding pesty details about how to fetch them from tagged structs or maps with well-known fields.

func (*MapStructVisitor) Label

func (v *MapStructVisitor) Label(node reflect.Value) (label string)

Label returns the label for a tree node.

func (*MapStructVisitor) Roots

func (v *MapStructVisitor) Roots(roots reflect.Value) (children []reflect.Value)

Roots returns the list of root nodes, while handling different types of Roots data types; for instance, struct, []struct, map, and []map, as well as pointers.

type TreeStyle

type TreeStyle struct {
	Fork     string // depicts forking off a node, such as "├".
	Nodeconn string // depicts the branch going to a node "leaf", such as "─".
	Nofork   string // depicts a continuing vertical main branch, such as "│".
	Lastnode string // depicts a vertical main branch ending in a node, such as "└".
	Property string // depicts a property, such as "•"
}

TreeStyle defines the ASCII art elements required for "painting" beautiful ASCII trees. In this case, we also subsume Unicode under ASCII for a suitable definition of "ASCII" and "Unicode".

type TreeStyler

type TreeStyler struct {
	Style       TreeStyle // The specific TreeStyle to use, such as ASCIIStyle, or LineStyle.
	ChildIndent int       // The indentation of child nodes.
	PropIndent  int       // The indentation of properties w.r.t. their node
}

TreeStyler describes the tree branch and node properties indentations, as well as the style of "line art" to use when rendering ASCII trees.

func NewTreeStyler

func NewTreeStyler(style TreeStyle) *TreeStyler

NewTreeStyler returns a Style object suitable for use with rendering trees.

type Visitor

type Visitor interface {
	Roots(roots reflect.Value) (children []reflect.Value)
	Label(node reflect.Value) (label string)
	Get(node reflect.Value) (label string, properties []string, children reflect.Value)
}

Visitor looks into user-defined data structures, trying to locate the anointed, erm, annotated ("tagged") user-data entries which represent node labels, properties, and children. The Visitor interface is used by tree renderers for visiting nodes in order to retrieve their tree-relevant information while traversing trees.

Visitors thus avoid having to implement an asciitree-specific interface on user data-structures, that is, they avoid adaptors. In many use cases, a MapStructVisitor will suffice as it implements the visitor pattern on tagged structs and maps with well-known keys.

Please note that Visitors do not traverse; that is the job of the asciitree Render...() functions.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL