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  }