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 }