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"
|
|
|
|
|
2021-01-09 11:17:15 +00:00
|
|
|
// DiffChange represents a file change made in a directory.
|
2021-01-02 21:25:39 +00:00
|
|
|
type DiffChange struct {
|
2021-01-09 11:17:15 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2021-01-09 11:17:15 +00:00
|
|
|
// 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) {
|
2021-01-03 11:28:06 +00:00
|
|
|
var err error
|
2021-01-03 16:09:10 +00:00
|
|
|
var sourceFile, targetFile Metadata
|
2021-01-03 11:28:06 +00:00
|
|
|
var sourceOpened, targetOpened bool = true, true
|
2021-01-17 11:47:05 +00:00
|
|
|
sourceCount := 0
|
2021-01-03 11:28:06 +00:00
|
|
|
pair := diffPair{}
|
2021-01-02 21:25:39 +00:00
|
|
|
|
2021-01-03 11:28:06 +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
|
2021-01-03 11:28:06 +00:00
|
|
|
pair.source = &sourceFile
|
2021-01-02 21:25:39 +00:00
|
|
|
}
|
2021-01-03 11:28:06 +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)
|
2021-01-03 11:28:06 +00:00
|
|
|
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.
|
2021-01-09 11:17:15 +00:00
|
|
|
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
|
|
|
|
}
|