zk/util/paths/diff.go

121 lines
2.9 KiB
Go
Raw Normal View History

2021-01-09 12:01:41 +00:00
package paths
2021-01-02 21:25:39 +00:00
2021-01-17 10:59:45 +00:00
import "fmt"
// DiffChange represents a file change made in a directory.
2021-01-02 21:25:39 +00:00
type DiffChange struct {
Path string
2021-01-02 21:25:39 +00:00
Kind DiffKind
}
2021-01-17 10:59:45 +00:00
// String implements Stringer.
func (c DiffChange) String() string {
return fmt.Sprintf("%v %v", c.Kind, c.Path)
}
// DiffKind represents a type of file change made in a directory.
2021-01-02 21:25:39 +00:00
type DiffKind int
const (
DiffAdded DiffKind = iota + 1
DiffModified
DiffRemoved
)
2021-01-17 10:59:45 +00:00
// String implements Stringer.
func (k DiffKind) String() string {
switch k {
case DiffAdded:
return "+"
case DiffModified:
return "~"
case DiffRemoved:
return "-"
default:
panic(fmt.Sprintf("%d: unknown DiffKind", int(k)))
}
}
2021-01-09 12:01:41 +00:00
// Diff compares two sources of Metadata and report the file changes, using the
// file modification date.
2021-01-02 21:25:39 +00:00
//
2021-01-17 11:47:05 +00:00
// Returns the number of files in the source.
//
2021-01-09 12:01:41 +00:00
// Warning: The Metadata have to be sorted by their Path for the diffing to
2021-01-02 21:25:39 +00:00
// work properly.
2021-01-17 11:47:05 +00:00
func Diff(source, target <-chan Metadata, forceModified bool, callback func(DiffChange) error) (int, error) {
var err error
2021-01-03 16:09:10 +00:00
var sourceFile, targetFile Metadata
var sourceOpened, targetOpened bool = true, true
2021-01-17 11:47:05 +00:00
sourceCount := 0
pair := diffPair{}
2021-01-02 21:25:39 +00:00
for err == nil && (sourceOpened || targetOpened) {
if pair.source == nil {
sourceFile, sourceOpened = <-source
if sourceOpened {
2021-01-17 11:47:05 +00:00
sourceCount += 1
pair.source = &sourceFile
2021-01-02 21:25:39 +00:00
}
}
if pair.target == nil {
targetFile, targetOpened = <-target
if targetOpened {
pair.target = &targetFile
2021-01-02 21:25:39 +00:00
}
}
2021-01-16 19:53:12 +00:00
change := pair.diff(forceModified)
if change != nil {
err = callback(*change)
}
}
2021-01-17 11:47:05 +00:00
return sourceCount, err
2021-01-02 21:25:39 +00:00
}
// diffPair holds the current two files to be diffed.
type diffPair struct {
2021-01-03 16:09:10 +00:00
source *Metadata
target *Metadata
2021-01-02 21:25:39 +00:00
}
// diff compares the source and target files in the current pair.
//
// If the source and target file are at the same path, we check for any change.
// If the files are different, that means that either the source file was
// added, or the target file was removed.
2021-01-16 19:53:12 +00:00
func (p *diffPair) diff(forceModified bool) *DiffChange {
2021-01-02 21:25:39 +00:00
var change *DiffChange
switch {
case p.source == nil && p.target == nil: // Both channels are closed
break
case p.source == nil && p.target != nil: // Source channel is closed
change = &DiffChange{p.target.Path, DiffRemoved}
p.target = nil
case p.source != nil && p.target == nil: // Target channel is closed
change = &DiffChange{p.source.Path, DiffAdded}
p.source = nil
case p.source.Path == p.target.Path: // Same files, compare their modification date.
2021-01-16 19:53:12 +00:00
if forceModified || p.source.Modified != p.target.Modified {
2021-01-02 21:25:39 +00:00
change = &DiffChange{p.source.Path, DiffModified}
}
p.source = nil
p.target = nil
default: // Different files, one has been added or removed.
if p.source.Path < p.target.Path {
2021-01-02 21:25:39 +00:00
change = &DiffChange{p.source.Path, DiffAdded}
p.source = nil
} else {
change = &DiffChange{p.target.Path, DiffRemoved}
p.target = nil
}
}
return change
}