add filetree

This commit is contained in:
Alex Goodman 2018-05-20 10:39:59 -04:00
parent f88abef25f
commit 488ec1b047
No known key found for this signature in database
GPG Key ID: 05328C611D8A520E
6 changed files with 309 additions and 28 deletions

View File

@ -1,3 +1,8 @@
FROM alpine:latest
ADD README.md /somefile.txt
RUN cp /somefile.txt /root/somefile.txt
RUN mkdir /root/example
RUN cp /somefile.txt /root/example/somefile1.txt
RUN cp /somefile.txt /root/example/somefile2.txt
RUN cp /somefile.txt /root/example/somefile3.txt
RUN mv /root/example/somefile3.txt /root/saved.txt
RUN rm -rf /root/example/

View File

@ -1,5 +1,5 @@
# docker-image-explorer
```
docker build -t . die-test:latest
docker build -t die-test:latest .
go run main.go
```

68
filetree.go Normal file
View File

@ -0,0 +1,68 @@
package main
import (
"errors"
"strings"
)
type FileTree interface {
AddPath(string, interface{})
RemovePath(string)
Visit(Visiter)
}
type Visiter func(*Node)
func (tree *Tree) Visit(visiter Visiter) {
tree.root.Visit(visiter)
}
func (node *Node) Visit(visiter Visiter) {
for _, child := range node.children {
child.Visit(visiter)
}
visiter(node)
}
func (tree *Tree) AddPath(path string, data interface{}) (*Node, error) {
nodeNames := strings.Split(path, "/")
node := tree.Root()
var err error
for idx, name := range nodeNames {
if name == "" {
continue
}
// find or create node
if node.children[name] != nil {
node = node.children[name]
} else {
node, _ = node.AddChild(name, nil)
if err != nil {
return node, err
}
}
// attach payload
if idx == len(nodeNames)-1 {
node.data = data
}
}
return node, nil
}
func (tree *Tree) RemovePath(path string) error {
nodeNames := strings.Split(path, "/")
node := tree.Root()
for _, name := range nodeNames {
if node.children[name] != nil {
node = node.children[name]
} else {
return errors.New("Path does not exist")
}
}
// this node's parent should be a leaf
return node.Remove()
}

61
filetree_test.go Normal file
View File

@ -0,0 +1,61 @@
package main
import "testing"
func TestAddPath(t *testing.T) {
tree := NewTree()
tree.AddPath("/etc/nginx/nginx.conf", 1)
tree.AddPath("/etc/nginx/public", 2)
tree.AddPath("/var/run/systemd", 3)
tree.AddPath("/var/run/bashful", 4)
tree.AddPath("/tmp", 5)
tree.AddPath("/tmp/nonsense", 6)
expected := `.
etc
nginx
nginx.conf
public
tmp
nonsense
var
run
bashful
systemd
`
actual := tree.String()
if expected != actual {
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
}
}
func TestRemovePath(t *testing.T) {
tree := NewTree()
tree.AddPath("/etc/nginx/nginx.conf", 1)
tree.AddPath("/etc/nginx/public", 2)
tree.AddPath("/var/run/systemd", 3)
tree.AddPath("/var/run/bashful", 4)
tree.AddPath("/tmp", 5)
tree.AddPath("/tmp/nonsense", 6)
tree.RemovePath("/var/run/bashful")
tree.RemovePath("/tmp")
expected := `.
etc
nginx
nginx.conf
public
var
run
systemd
`
actual := tree.String()
if expected != actual {
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
}
}

101
tree.go
View File

@ -1,14 +1,21 @@
package main
import "errors"
import (
"errors"
"sort"
)
type Entity interface {
Visit(Visiter)
}
const (
newLine = "\n"
emptySpace = " "
middleItem = "├── "
continueItem = "│ "
lastItem = "└── "
)
type Tree struct {
root Node
size uint
size int
}
type Node struct {
@ -19,21 +26,23 @@ type Node struct {
children map[string]*Node
}
type Visiter func(*Node)
func NewTree() (tree *Tree) {
tree = new(Tree)
tree.size = 1
tree.size = 0
root := &tree.root
root.tree = tree
root.name = ""
root.children = make(map[string]*Node)
return tree
}
func NewNode(name string, data interface{}) (node *Node) {
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
}
@ -41,29 +50,69 @@ func (tree *Tree) Root() *Node {
return &tree.root
}
func (tree *Tree) Visit(visiter Visiter) {
tree.root.Visit(visiter)
}
func (parent *Node) Add(name string, data interface{}) (child *Node, error error) {
if parent.children[name] != nil {
return nil, errors.New("Duplicate child")
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(name, data)
child.tree = parent.tree
child.parent = parent
child.tree.size++
parent.children[name] = child
child = NewNode(node, name, data)
node.children[name] = child
node.tree.size++
return child, nil
}
func (node *Node) Visit(visiter Visiter) {
func (node *Node) Remove() error {
for _, child := range node.children {
child.Visit(visiter)
child.Remove()
}
visiter(node)
delete(node.parent.children, node.name)
node.tree.size--
return nil
}
func main() {
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{})
}

98
tree_test.go Normal file
View File

@ -0,0 +1,98 @@
package main
import "testing"
func TestAddChild(t *testing.T) {
var expected, actual int
tree := NewTree()
_, err := tree.Root().AddChild("first node!", 1)
if err != nil {
t.Errorf("Adding valued child should not result in error.")
}
two, err := tree.Root().AddChild("nil node!", nil)
if err != nil {
t.Errorf("Adding nil child should not result in error.")
}
tree.Root().AddChild("third node!", 3)
two.AddChild("forth, one level down...", 4)
two.AddChild("fifth, one level down...", 5)
_, err = two.AddChild("fifth, one level down...", 5)
if err == nil {
t.Errorf("Expected an error when adding duplicate nodes, no error given.")
}
expected, actual = 5, tree.size
if expected != actual {
t.Errorf("Expected a tree size of %d got %d.", expected, actual)
}
expected, actual = 2, len(two.children)
if expected != actual {
t.Errorf("Expected 'twos' number of children to be %d got %d.", expected, actual)
}
expected, actual = 3, len(tree.Root().children)
if expected != actual {
t.Errorf("Expected 'twos' number of children to be %d got %d.", expected, actual)
}
}
func TestRemoveChild(t *testing.T) {
var expected, actual int
tree := NewTree()
tree.Root().AddChild("first", 1)
two, _ := tree.Root().AddChild("nil", nil)
tree.Root().AddChild("third", 3)
forth, _ := two.AddChild("forth", 4)
two.AddChild("fifth", 5)
forth.Remove()
expected, actual = 4, tree.size
if expected != actual {
t.Errorf("Expected a tree size of %d got %d.", expected, actual)
}
if tree.Root().children["forth"] != nil {
t.Errorf("Expected 'forth' node to be deleted.")
}
two.Remove()
expected, actual = 2, tree.size
if expected != actual {
t.Errorf("Expected a tree size of %d got %d.", expected, actual)
}
if tree.Root().children["nil"] != nil {
t.Errorf("Expected 'nil' node to be deleted.")
}
}
func TestPrintTree(t *testing.T) {
tree := NewTree()
tree.Root().AddChild("first node!", nil)
two, _ := tree.Root().AddChild("second node!", nil)
tree.Root().AddChild("third node!", nil)
two.AddChild("forth, one level down...", nil)
expected := `.
first node!
second node!
forth, one level down...
third node!
`
actual := tree.String()
if expected != actual {
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
}
}