dive-zfs/tree_compare.go
2018-05-31 16:59:45 -04:00

162 lines
3.1 KiB
Go

package main
import (
"bytes"
"fmt"
)
func differ() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
type DiffType int
// enum to show whether a file has changed
const (
Unchanged DiffType = iota
Changed
Added
Removed
)
func (d DiffType) String() string {
switch d {
case Unchanged:
return "Unchanged"
case Changed:
return "Changed"
case Added:
return "Added"
case Removed:
return "Removed"
default:
return fmt.Sprintf("%d", int(d))
}
}
func compareNodes(a *Node, b *Node) DiffType {
if a == nil && b == nil {
return Unchanged
}
// a is nil but not b
if a == nil && b != nil {
return Added
}
// b is nil but not a
if a != nil && b == nil {
return Removed
}
if b.IsWhiteout() {
return Removed
}
if a.name != b.name {
panic("comparing mismatched nodes")
}
// TODO: fails on nil
return getDiffType(a.data, b.data)
}
func getDiffType(a *FileChangeInfo, b *FileChangeInfo) DiffType {
if a == nil && b == nil {
return Unchanged
}
if a == nil || b == nil {
return Changed
}
if a.typeflag == b.typeflag {
if bytes.Compare(a.md5sum[:], b.md5sum[:]) == 0 {
return Unchanged
}
}
return Changed
}
func mergeDiffTypes(a DiffType, b DiffType) DiffType {
if a == b {
return a
}
return Changed
}
func (tree *FileTree) compareTo(upper *FileTree) error {
// TODO mark all as unchanged
markAllUnchanged := func(node *Node) error {
return node.AssignDiffType(Unchanged)
}
err := tree.Visit(markAllUnchanged)
if err != nil {
return err
}
graft := func(node *Node) error {
if node.IsWhiteout() {
err := tree.MarkRemoved(node.Path())
if err != nil {
return fmt.Errorf("Cannot remove node %s: %v", node.Path(), err.Error())
}
} else {
existingNode, _ := tree.GetNode(node.Path())
if existingNode == nil {
newNode, err := tree.AddPath(node.Path(), node.data)
fmt.Printf("added new node at %s\n", newNode.Path())
if err != nil {
return fmt.Errorf("Cannot add new node %s: %v", node.Path(), err.Error())
}
newNode.AssignDiffType(Added)
} else {
diffType := compareNodes(existingNode, node)
fmt.Printf("found existing node at %s\n", existingNode.Path())
existingNode.DiffTypeFromChildren(diffType)
}
}
return nil
}
return upper.Visit(graft)
}
// THE DIFF_TYPE OF A NODE IS ALWAYS THE DIFF_TYPE OF ITS ATTRIBUTES AND ITS CONTENTS
// THE CONTENTS ARE THE BYTES OF A FILE OR THE CHILDREN OF A DIRECTORY
func (tree *FileTree) MarkRemoved(path string) error {
node, err := tree.GetNode(path)
if err != nil {
return err
}
return node.AssignDiffType(Removed)
}
func (node *Node) IsLeaf() bool {
return len(node.children) == 0
}
func (node *Node) DiffTypeFromChildren(diffType DiffType) error {
if node.IsLeaf() {
node.AssignDiffType(diffType)
return nil
}
myDiffType := diffType
for _, v := range node.children {
vData := v.data
myDiffType = mergeDiffTypes(myDiffType, vData.diffType)
}
node.AssignDiffType(myDiffType)
return nil
}
func (node *Node) AssignDiffType(diffType DiffType) error {
if node.Path() == "/" {
return nil
}
node.data.diffType = diffType
return nil
}