Files
dolt/go/diff/summary.go
2016-10-20 10:19:57 -07:00

173 lines
5.2 KiB
Go

// Copyright 2016 Attic Labs, Inc. All rights reserved.
// Licensed under the Apache License, version 2.0:
// http://www.apache.org/licenses/LICENSE-2.0
package diff
import (
"fmt"
"github.com/attic-labs/noms/go/datas"
"github.com/attic-labs/noms/go/types"
"github.com/attic-labs/noms/go/util/status"
humanize "github.com/dustin/go-humanize"
)
// Summary prints a summary of the diff between two values to stdout.
func Summary(value1, value2 types.Value) {
if datas.IsCommitType(value1.Type()) && datas.IsCommitType(value2.Type()) {
fmt.Println("Comparing commit values")
value1 = value1.(types.Struct).Get(datas.ValueField)
value2 = value2.(types.Struct).Get(datas.ValueField)
}
var singular, plural string
if value1.Type().Kind() == value2.Type().Kind() {
switch value1.Type().Kind() {
case types.StructKind:
singular = "field"
plural = "fields"
case types.MapKind:
singular = "entry"
plural = "entries"
default:
singular = "value"
plural = "values"
}
}
ch := make(chan diffSummaryProgress)
go func() {
diffSummary(ch, value1, value2)
close(ch)
}()
acc := diffSummaryProgress{}
for p := range ch {
acc.Adds += p.Adds
acc.Removes += p.Removes
acc.Changes += p.Changes
acc.NewSize += p.NewSize
acc.OldSize += p.OldSize
if status.WillPrint() {
formatStatus(acc, singular, plural)
}
}
formatStatus(acc, singular, plural)
status.Done()
}
type diffSummaryProgress struct {
Adds, Removes, Changes, NewSize, OldSize uint64
}
func diffSummary(ch chan diffSummaryProgress, v1, v2 types.Value) {
if !v1.Equals(v2) {
if shouldDescend(v1, v2) {
switch v1.Type().Kind() {
case types.ListKind:
diffSummaryList(ch, v1.(types.List), v2.(types.List))
case types.MapKind:
diffSummaryMap(ch, v1.(types.Map), v2.(types.Map))
case types.SetKind:
diffSummarySet(ch, v1.(types.Set), v2.(types.Set))
case types.StructKind:
diffSummaryStructs(ch, v1.(types.Struct), v2.(types.Struct))
default:
panic("Unrecognized type in diff function: " + v1.Type().Describe() + " and " + v2.Type().Describe())
}
} else {
ch <- diffSummaryProgress{Adds: 1, Removes: 1, NewSize: 1, OldSize: 1}
}
}
}
func diffSummaryList(ch chan<- diffSummaryProgress, v1, v2 types.List) {
ch <- diffSummaryProgress{OldSize: v1.Len(), NewSize: v2.Len()}
spliceChan := make(chan types.Splice)
stopChan := make(chan struct{}, 1) // buffer size of 1, so this won't block if diff already finished
go func() {
v2.Diff(v1, spliceChan, stopChan)
close(spliceChan)
}()
for splice := range spliceChan {
if splice.SpRemoved == splice.SpAdded {
ch <- diffSummaryProgress{Changes: splice.SpRemoved}
} else {
ch <- diffSummaryProgress{Adds: splice.SpAdded, Removes: splice.SpRemoved}
}
}
}
func diffSummaryMap(ch chan<- diffSummaryProgress, v1, v2 types.Map) {
diffSummaryValueChanged(ch, v1.Len(), v2.Len(), func(changeChan chan<- types.ValueChanged, stopChan <-chan struct{}) {
v2.Diff(v1, changeChan, stopChan)
})
}
func diffSummarySet(ch chan<- diffSummaryProgress, v1, v2 types.Set) {
diffSummaryValueChanged(ch, v1.Len(), v2.Len(), func(changeChan chan<- types.ValueChanged, stopChan <-chan struct{}) {
v2.Diff(v1, changeChan, stopChan)
})
}
func diffSummaryStructs(ch chan<- diffSummaryProgress, v1, v2 types.Struct) {
size1 := uint64(v1.Type().Desc.(types.StructDesc).Len())
size2 := uint64(v2.Type().Desc.(types.StructDesc).Len())
diffSummaryValueChanged(ch, size1, size2, func(changeChan chan<- types.ValueChanged, stopChan <-chan struct{}) {
v2.Diff(v1, changeChan, stopChan)
})
}
func diffSummaryValueChanged(ch chan<- diffSummaryProgress, oldSize, newSize uint64, f diffFunc) {
ch <- diffSummaryProgress{OldSize: oldSize, NewSize: newSize}
changeChan := make(chan types.ValueChanged)
stopChan := make(chan struct{}, 1) // buffer size of 1, so this won't block if diff already finished
go func() {
f(changeChan, stopChan)
close(changeChan)
}()
reportChanges(ch, changeChan)
}
func reportChanges(ch chan<- diffSummaryProgress, changeChan chan types.ValueChanged) {
for change := range changeChan {
switch change.ChangeType {
case types.DiffChangeAdded:
ch <- diffSummaryProgress{Adds: 1}
case types.DiffChangeRemoved:
ch <- diffSummaryProgress{Removes: 1}
case types.DiffChangeModified:
ch <- diffSummaryProgress{Changes: 1}
default:
panic("unknown change type")
}
}
}
func formatStatus(acc diffSummaryProgress, singular, plural string) {
pluralize := func(singular, plural string, n uint64) string {
var noun string
if n != 1 {
noun = plural
} else {
noun = singular
}
return fmt.Sprintf("%s %s", humanize.Comma(int64(n)), noun)
}
insertions := pluralize("insertion", "insertions", acc.Adds)
deletions := pluralize("deletion", "deletions", acc.Removes)
changes := pluralize("change", "changes", acc.Changes)
oldValues := pluralize(singular, plural, acc.OldSize)
newValues := pluralize(singular, plural, acc.NewSize)
status.Printf("%s (%.2f%%), %s (%.2f%%), %s (%.2f%%), (%s vs %s)", insertions, (float64(100*acc.Adds) / float64(acc.OldSize)), deletions, (float64(100*acc.Removes) / float64(acc.OldSize)), changes, (float64(100*acc.Changes) / float64(acc.OldSize)), oldValues, newValues)
}