dive-zfs/filetree.go
2018-05-24 20:49:59 -04:00

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()
}