github.com/jbendotnet/noms@v0.0.0-20190904222105-c43e4293ea92/go/diff/summary.go (about) 1 // Copyright 2016 Attic Labs, Inc. All rights reserved. 2 // Licensed under the Apache License, version 2.0: 3 // http://www.apache.org/licenses/LICENSE-2.0 4 5 package diff 6 7 import ( 8 "fmt" 9 10 "github.com/attic-labs/noms/go/datas" 11 "github.com/attic-labs/noms/go/types" 12 "github.com/attic-labs/noms/go/util/status" 13 humanize "github.com/dustin/go-humanize" 14 ) 15 16 // Summary prints a summary of the diff between two values to stdout. 17 func Summary(value1, value2 types.Value) { 18 if datas.IsCommit(value1) && datas.IsCommit(value2) { 19 fmt.Println("Comparing commit values") 20 value1 = value1.(types.Struct).Get(datas.ValueField) 21 value2 = value2.(types.Struct).Get(datas.ValueField) 22 } 23 24 var singular, plural string 25 if value1.Kind() == value2.Kind() { 26 switch value1.Kind() { 27 case types.StructKind: 28 singular = "field" 29 plural = "fields" 30 case types.MapKind: 31 singular = "entry" 32 plural = "entries" 33 default: 34 singular = "value" 35 plural = "values" 36 } 37 } 38 39 ch := make(chan diffSummaryProgress) 40 go func() { 41 diffSummary(ch, value1, value2) 42 close(ch) 43 }() 44 45 acc := diffSummaryProgress{} 46 for p := range ch { 47 acc.Adds += p.Adds 48 acc.Removes += p.Removes 49 acc.Changes += p.Changes 50 acc.NewSize += p.NewSize 51 acc.OldSize += p.OldSize 52 if status.WillPrint() { 53 formatStatus(acc, singular, plural) 54 } 55 } 56 formatStatus(acc, singular, plural) 57 status.Done() 58 } 59 60 type diffSummaryProgress struct { 61 Adds, Removes, Changes, NewSize, OldSize uint64 62 } 63 64 func diffSummary(ch chan diffSummaryProgress, v1, v2 types.Value) { 65 if !v1.Equals(v2) { 66 if shouldDescend(v1, v2) { 67 switch v1.Kind() { 68 case types.ListKind: 69 diffSummaryList(ch, v1.(types.List), v2.(types.List)) 70 case types.MapKind: 71 diffSummaryMap(ch, v1.(types.Map), v2.(types.Map)) 72 case types.SetKind: 73 diffSummarySet(ch, v1.(types.Set), v2.(types.Set)) 74 case types.StructKind: 75 diffSummaryStructs(ch, v1.(types.Struct), v2.(types.Struct)) 76 default: 77 panic("Unrecognized type in diff function: " + types.TypeOf(v1).Describe() + " and " + types.TypeOf(v2).Describe()) 78 } 79 } else { 80 ch <- diffSummaryProgress{Adds: 1, Removes: 1, NewSize: 1, OldSize: 1} 81 } 82 } 83 } 84 85 func diffSummaryList(ch chan<- diffSummaryProgress, v1, v2 types.List) { 86 ch <- diffSummaryProgress{OldSize: v1.Len(), NewSize: v2.Len()} 87 88 spliceChan := make(chan types.Splice) 89 stopChan := make(chan struct{}, 1) // buffer size of 1, so this won't block if diff already finished 90 91 go func() { 92 v2.Diff(v1, spliceChan, stopChan) 93 close(spliceChan) 94 }() 95 96 for splice := range spliceChan { 97 if splice.SpRemoved == splice.SpAdded { 98 ch <- diffSummaryProgress{Changes: splice.SpRemoved} 99 } else { 100 ch <- diffSummaryProgress{Adds: splice.SpAdded, Removes: splice.SpRemoved} 101 } 102 } 103 } 104 105 func diffSummaryMap(ch chan<- diffSummaryProgress, v1, v2 types.Map) { 106 diffSummaryValueChanged(ch, v1.Len(), v2.Len(), func(changeChan chan<- types.ValueChanged, stopChan <-chan struct{}) { 107 v2.Diff(v1, changeChan, stopChan) 108 }) 109 } 110 111 func diffSummarySet(ch chan<- diffSummaryProgress, v1, v2 types.Set) { 112 diffSummaryValueChanged(ch, v1.Len(), v2.Len(), func(changeChan chan<- types.ValueChanged, stopChan <-chan struct{}) { 113 v2.Diff(v1, changeChan, stopChan) 114 }) 115 } 116 117 func diffSummaryStructs(ch chan<- diffSummaryProgress, v1, v2 types.Struct) { 118 // TODO: Operate on values directly 119 size1 := uint64(types.TypeOf(v1).Desc.(types.StructDesc).Len()) 120 size2 := uint64(types.TypeOf(v2).Desc.(types.StructDesc).Len()) 121 diffSummaryValueChanged(ch, size1, size2, func(changeChan chan<- types.ValueChanged, stopChan <-chan struct{}) { 122 v2.Diff(v1, changeChan, stopChan) 123 }) 124 } 125 126 func diffSummaryValueChanged(ch chan<- diffSummaryProgress, oldSize, newSize uint64, f diffFunc) { 127 ch <- diffSummaryProgress{OldSize: oldSize, NewSize: newSize} 128 129 changeChan := make(chan types.ValueChanged) 130 stopChan := make(chan struct{}, 1) // buffer size of 1, so this won't block if diff already finished 131 132 go func() { 133 f(changeChan, stopChan) 134 close(changeChan) 135 }() 136 reportChanges(ch, changeChan) 137 } 138 139 func reportChanges(ch chan<- diffSummaryProgress, changeChan chan types.ValueChanged) { 140 for change := range changeChan { 141 switch change.ChangeType { 142 case types.DiffChangeAdded: 143 ch <- diffSummaryProgress{Adds: 1} 144 case types.DiffChangeRemoved: 145 ch <- diffSummaryProgress{Removes: 1} 146 case types.DiffChangeModified: 147 ch <- diffSummaryProgress{Changes: 1} 148 default: 149 panic("unknown change type") 150 } 151 } 152 } 153 154 func formatStatus(acc diffSummaryProgress, singular, plural string) { 155 pluralize := func(singular, plural string, n uint64) string { 156 var noun string 157 if n != 1 { 158 noun = plural 159 } else { 160 noun = singular 161 } 162 return fmt.Sprintf("%s %s", humanize.Comma(int64(n)), noun) 163 } 164 165 insertions := pluralize("insertion", "insertions", acc.Adds) 166 deletions := pluralize("deletion", "deletions", acc.Removes) 167 changes := pluralize("change", "changes", acc.Changes) 168 169 oldValues := pluralize(singular, plural, acc.OldSize) 170 newValues := pluralize(singular, plural, acc.NewSize) 171 172 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) 173 }