137 lines
2.6 KiB
Go
137 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"fmt"
|
|
)
|
|
|
|
type FileTree interface {
|
|
AddPath(string, interface{}) *Node
|
|
RemovePath(string) error
|
|
Visit(Visiter) error
|
|
// Diff(*Tree) error
|
|
Stack(*Tree) (Tree, error)
|
|
}
|
|
|
|
type Visiter func(*Node) error
|
|
|
|
func (tree *Tree) Visit(visiter Visiter) error {
|
|
return tree.root.Visit(visiter)
|
|
}
|
|
|
|
func (node *Node) Visit(visiter Visiter) error {
|
|
for _, child := range node.children {
|
|
err := child.Visit(visiter)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return visiter(node)
|
|
}
|
|
|
|
func (node *Node) IsWhiteout() bool {
|
|
return strings.HasPrefix(node.name, ".wh.")
|
|
}
|
|
|
|
func (node *Node) Path() string {
|
|
path := []string{}
|
|
curNode := node
|
|
for {
|
|
if curNode.parent == nil{
|
|
break
|
|
}
|
|
path = append([]string{curNode.name}, path...)
|
|
curNode = curNode.parent
|
|
}
|
|
return "/" + strings.Join(path, "/")
|
|
}
|
|
|
|
|
|
func (node *Node) WhiteoutPath() string {
|
|
path := []string{}
|
|
curNode := node
|
|
for {
|
|
if curNode.parent == nil{
|
|
break
|
|
}
|
|
|
|
name := curNode.name
|
|
if curNode == node {
|
|
name = strings.TrimPrefix(name, ".wh.")
|
|
}
|
|
|
|
path = append([]string{name}, path...)
|
|
curNode = curNode.parent
|
|
}
|
|
return "/" + strings.Join(path, "/")
|
|
}
|
|
|
|
|
|
|
|
func (tree *Tree) Stack(upper *Tree) (error) {
|
|
graft := func(node *Node) error {
|
|
if node.IsWhiteout() {
|
|
err := tree.RemovePath(node.WhiteoutPath())
|
|
if err != nil {
|
|
return fmt.Errorf("Cannot remove node %s: %v", node.Path(), err.Error())
|
|
}
|
|
} else {
|
|
newNode, err := tree.AddPath(node.Path(), node.data)
|
|
if err != nil {
|
|
return fmt.Errorf("Cannot add node %s: %v", newNode.Path(), err.Error())
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
return upper.Visit(graft)
|
|
}
|
|
|
|
func (tree *Tree) GetNode(path string) (*Node, error) {
|
|
nodeNames := strings.Split(path, "/")
|
|
node := tree.Root()
|
|
for _, name := range nodeNames {
|
|
if name == "" {
|
|
continue
|
|
}
|
|
if node.children[name] == nil {
|
|
return nil, errors.New("Path does not exist")
|
|
}
|
|
node = node.children[name]
|
|
}
|
|
return node, nil
|
|
}
|
|
|
|
func (tree *Tree) AddPath(path string, data interface{}) (*Node, error) {
|
|
nodeNames := strings.Split(path, "/")
|
|
node := tree.Root()
|
|
for idx, name := range nodeNames {
|
|
if name == "" {
|
|
continue
|
|
}
|
|
// find or create node
|
|
if node.children[name] != nil {
|
|
node = node.children[name]
|
|
} else {
|
|
// don't attach the payload. The payload is destined for the
|
|
// path's end node, not any intermediary node.
|
|
node = node.AddChild(name, nil)
|
|
}
|
|
|
|
// attach payload to the last specified node
|
|
if idx == len(nodeNames)-1 {
|
|
node.data = data
|
|
}
|
|
|
|
}
|
|
return node, nil
|
|
}
|
|
|
|
func (tree *Tree) RemovePath(path string) error {
|
|
node, err := tree.GetNode(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return node.Remove()
|
|
}
|