add filetree
This commit is contained in:
parent
f88abef25f
commit
488ec1b047
@ -1,3 +1,8 @@
|
|||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
ADD README.md /somefile.txt
|
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/
|
@ -1,5 +1,5 @@
|
|||||||
# docker-image-explorer
|
# docker-image-explorer
|
||||||
```
|
```
|
||||||
docker build -t . die-test:latest
|
docker build -t die-test:latest .
|
||||||
go run main.go
|
go run main.go
|
||||||
```
|
```
|
68
filetree.go
Normal file
68
filetree.go
Normal 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
61
filetree_test.go
Normal 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
101
tree.go
@ -1,14 +1,21 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"errors"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
type Entity interface {
|
const (
|
||||||
Visit(Visiter)
|
newLine = "\n"
|
||||||
}
|
emptySpace = " "
|
||||||
|
middleItem = "├── "
|
||||||
|
continueItem = "│ "
|
||||||
|
lastItem = "└── "
|
||||||
|
)
|
||||||
|
|
||||||
type Tree struct {
|
type Tree struct {
|
||||||
root Node
|
root Node
|
||||||
size uint
|
size int
|
||||||
}
|
}
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
@ -19,21 +26,23 @@ type Node struct {
|
|||||||
children map[string]*Node
|
children map[string]*Node
|
||||||
}
|
}
|
||||||
|
|
||||||
type Visiter func(*Node)
|
|
||||||
|
|
||||||
func NewTree() (tree *Tree) {
|
func NewTree() (tree *Tree) {
|
||||||
tree = new(Tree)
|
tree = new(Tree)
|
||||||
tree.size = 1
|
tree.size = 0
|
||||||
root := &tree.root
|
root := &tree.root
|
||||||
root.tree = tree
|
root.tree = tree
|
||||||
|
root.name = ""
|
||||||
root.children = make(map[string]*Node)
|
root.children = make(map[string]*Node)
|
||||||
return tree
|
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.name = name
|
||||||
node.data = data
|
node.data = data
|
||||||
node.children = make(map[string]*Node)
|
node.children = make(map[string]*Node)
|
||||||
|
node.parent = parent
|
||||||
|
node.tree = parent.tree
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,29 +50,69 @@ func (tree *Tree) Root() *Node {
|
|||||||
return &tree.root
|
return &tree.root
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tree *Tree) Visit(visiter Visiter) {
|
func (node *Node) AddChild(name string, data interface{}) (child *Node, error error) {
|
||||||
tree.root.Visit(visiter)
|
if node.children[name] != nil {
|
||||||
}
|
return nil, errors.New("Tree node already exists")
|
||||||
|
|
||||||
func (parent *Node) Add(name string, data interface{}) (child *Node, error error) {
|
|
||||||
if parent.children[name] != nil {
|
|
||||||
return nil, errors.New("Duplicate child")
|
|
||||||
}
|
}
|
||||||
child = NewNode(name, data)
|
child = NewNode(node, name, data)
|
||||||
child.tree = parent.tree
|
node.children[name] = child
|
||||||
child.parent = parent
|
node.tree.size++
|
||||||
child.tree.size++
|
|
||||||
parent.children[name] = child
|
|
||||||
return child, nil
|
return child, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *Node) Visit(visiter Visiter) {
|
func (node *Node) Remove() error {
|
||||||
for _, child := range node.children {
|
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
98
tree_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user