gopkg.in/tools/godep.v67@v67.0.0-20160513230433-2d182dfe781d/Godeps/_workspace/src/github.com/kr/pretty/diff.go (about)

     1  package pretty
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"reflect"
     7  )
     8  
     9  type sbuf []string
    10  
    11  func (s *sbuf) Write(b []byte) (int, error) {
    12  	*s = append(*s, string(b))
    13  	return len(b), nil
    14  }
    15  
    16  // Diff returns a slice where each element describes
    17  // a difference between a and b.
    18  func Diff(a, b interface{}) (desc []string) {
    19  	Fdiff((*sbuf)(&desc), a, b)
    20  	return desc
    21  }
    22  
    23  // Fdiff writes to w a description of the differences between a and b.
    24  func Fdiff(w io.Writer, a, b interface{}) {
    25  	diffWriter{w: w}.diff(reflect.ValueOf(a), reflect.ValueOf(b))
    26  }
    27  
    28  type diffWriter struct {
    29  	w io.Writer
    30  	l string // label
    31  }
    32  
    33  func (w diffWriter) printf(f string, a ...interface{}) {
    34  	var l string
    35  	if w.l != "" {
    36  		l = w.l + ": "
    37  	}
    38  	fmt.Fprintf(w.w, l+f, a...)
    39  }
    40  
    41  func (w diffWriter) diff(av, bv reflect.Value) {
    42  	if !av.IsValid() && bv.IsValid() {
    43  		w.printf("nil != %#v", bv.Interface())
    44  		return
    45  	}
    46  	if av.IsValid() && !bv.IsValid() {
    47  		w.printf("%#v != nil", av.Interface())
    48  		return
    49  	}
    50  	if !av.IsValid() && !bv.IsValid() {
    51  		return
    52  	}
    53  
    54  	at := av.Type()
    55  	bt := bv.Type()
    56  	if at != bt {
    57  		w.printf("%v != %v", at, bt)
    58  		return
    59  	}
    60  
    61  	// numeric types, including bool
    62  	if at.Kind() < reflect.Array {
    63  		a, b := av.Interface(), bv.Interface()
    64  		if a != b {
    65  			w.printf("%#v != %#v", a, b)
    66  		}
    67  		return
    68  	}
    69  
    70  	switch at.Kind() {
    71  	case reflect.String:
    72  		a, b := av.Interface(), bv.Interface()
    73  		if a != b {
    74  			w.printf("%q != %q", a, b)
    75  		}
    76  	case reflect.Ptr:
    77  		switch {
    78  		case av.IsNil() && !bv.IsNil():
    79  			w.printf("nil != %v", bv.Interface())
    80  		case !av.IsNil() && bv.IsNil():
    81  			w.printf("%v != nil", av.Interface())
    82  		case !av.IsNil() && !bv.IsNil():
    83  			w.diff(av.Elem(), bv.Elem())
    84  		}
    85  	case reflect.Struct:
    86  		for i := 0; i < av.NumField(); i++ {
    87  			w.relabel(at.Field(i).Name).diff(av.Field(i), bv.Field(i))
    88  		}
    89  	case reflect.Slice:
    90  		lenA := av.Len()
    91  		lenB := bv.Len()
    92  		if lenA != lenB {
    93  			w.printf("%s[%d] != %s[%d]", av.Type(), lenA, bv.Type(), lenB)
    94  			break
    95  		}
    96  		for i := 0; i < lenA; i++ {
    97  			w.relabel(fmt.Sprintf("[%d]", i)).diff(av.Index(i), bv.Index(i))
    98  		}
    99  	case reflect.Map:
   100  		ak, both, bk := keyDiff(av.MapKeys(), bv.MapKeys())
   101  		for _, k := range ak {
   102  			w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
   103  			w.printf("%q != (missing)", av.MapIndex(k))
   104  		}
   105  		for _, k := range both {
   106  			w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
   107  			w.diff(av.MapIndex(k), bv.MapIndex(k))
   108  		}
   109  		for _, k := range bk {
   110  			w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
   111  			w.printf("(missing) != %q", bv.MapIndex(k))
   112  		}
   113  	case reflect.Interface:
   114  		w.diff(reflect.ValueOf(av.Interface()), reflect.ValueOf(bv.Interface()))
   115  	default:
   116  		if !reflect.DeepEqual(av.Interface(), bv.Interface()) {
   117  			w.printf("%# v != %# v", Formatter(av.Interface()), Formatter(bv.Interface()))
   118  		}
   119  	}
   120  }
   121  
   122  func (d diffWriter) relabel(name string) (d1 diffWriter) {
   123  	d1 = d
   124  	if d.l != "" && name[0] != '[' {
   125  		d1.l += "."
   126  	}
   127  	d1.l += name
   128  	return d1
   129  }
   130  
   131  func keyDiff(a, b []reflect.Value) (ak, both, bk []reflect.Value) {
   132  	for _, av := range a {
   133  		inBoth := false
   134  		for _, bv := range b {
   135  			if reflect.DeepEqual(av.Interface(), bv.Interface()) {
   136  				inBoth = true
   137  				both = append(both, av)
   138  				break
   139  			}
   140  		}
   141  		if !inBoth {
   142  			ak = append(ak, av)
   143  		}
   144  	}
   145  	for _, bv := range b {
   146  		inBoth := false
   147  		for _, av := range a {
   148  			if reflect.DeepEqual(av.Interface(), bv.Interface()) {
   149  				inBoth = true
   150  				break
   151  			}
   152  		}
   153  		if !inBoth {
   154  			bk = append(bk, bv)
   155  		}
   156  	}
   157  	return
   158  }