Skip to content

Commit

Permalink
Improve clarity of compareAny (#132)
Browse files Browse the repository at this point in the history
  • Loading branch information
dsnet committed Mar 12, 2019
1 parent b5cce89 commit 6f77996
Showing 1 changed file with 41 additions and 45 deletions.
86 changes: 41 additions & 45 deletions cmp/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,7 @@ func Equal(x, y interface{}, opts ...Option) bool {
// programmatically interpret the difference, consider using a custom Reporter.
func Diff(x, y interface{}, opts ...Option) string {
r := new(defaultReporter)
opts = Options{Options(opts), Reporter(r)}
eq := Equal(x, y, opts...)
eq := Equal(x, y, Options(opts), Reporter(r))
d := r.String()
if (d == "") != eq {
panic("inconsistent difference and equality results")
Expand Down Expand Up @@ -156,9 +155,7 @@ type state struct {
func newState(opts []Option) *state {
// Always ensure a validator option exists to validate the inputs.
s := &state{opts: Options{validator{}}}
for _, opt := range opts {
s.processOption(opt)
}
s.processOption(Options(opts))
return s
}

Expand Down Expand Up @@ -210,8 +207,6 @@ func (s *state) statelessCompare(step PathStep) diff.Result {
}

func (s *state) compareAny(step PathStep) {
// TODO: Support cyclic data structures.

// Update the path stack.
s.curPath.push(step)
defer s.curPath.pop()
Expand All @@ -235,67 +230,34 @@ func (s *state) compareAny(step PathStep) {
return
}

// Rule 3: Recursively descend into each value's underlying kind.
// Rule 3: Compare based on the underlying kind.
switch t.Kind() {
case reflect.Bool:
s.report(vx.Bool() == vy.Bool(), 0)
return
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
s.report(vx.Int() == vy.Int(), 0)
return
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
s.report(vx.Uint() == vy.Uint(), 0)
return
case reflect.Float32, reflect.Float64:
s.report(vx.Float() == vy.Float(), 0)
return
case reflect.Complex64, reflect.Complex128:
s.report(vx.Complex() == vy.Complex(), 0)
return
case reflect.String:
s.report(vx.String() == vy.String(), 0)
return
case reflect.Chan, reflect.UnsafePointer:
s.report(vx.Pointer() == vy.Pointer(), 0)
return
case reflect.Func:
s.report(vx.IsNil() && vy.IsNil(), 0)
return
case reflect.Struct:
s.compareStruct(t, vx, vy)
return
case reflect.Slice:
if vx.IsNil() || vy.IsNil() {
s.report(vx.IsNil() && vy.IsNil(), 0)
return
}
fallthrough
case reflect.Array:
case reflect.Slice, reflect.Array:
s.compareSlice(t, vx, vy)
return
case reflect.Map:
s.compareMap(t, vx, vy)
return
case reflect.Ptr:
if vx.IsNil() || vy.IsNil() {
s.report(vx.IsNil() && vy.IsNil(), 0)
return
}
vx, vy = vx.Elem(), vy.Elem()
s.compareAny(Indirect{&indirect{pathStep{t.Elem(), vx, vy}}})
return
s.comparePtr(t, vx, vy)
case reflect.Interface:
if vx.IsNil() || vy.IsNil() {
s.report(vx.IsNil() && vy.IsNil(), 0)
return
}
vx, vy = vx.Elem(), vy.Elem()
if vx.Type() != vy.Type() {
s.report(false, 0)
return
}
s.compareAny(TypeAssertion{&typeAssertion{pathStep{vx.Type(), vx, vy}}})
return
s.compareInterface(t, vx, vy)
default:
panic(fmt.Sprintf("%v kind not handled", t.Kind()))
}
Expand Down Expand Up @@ -423,6 +385,14 @@ func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) {
}

func (s *state) compareSlice(t reflect.Type, vx, vy reflect.Value) {
isSlice := t.Kind() == reflect.Slice
if isSlice && (vx.IsNil() || vy.IsNil()) {
s.report(vx.IsNil() && vy.IsNil(), 0)
return
}

// TODO: Support cyclic data structures.

step := SliceIndex{&sliceIndex{pathStep: pathStep{typ: t.Elem()}}}
withIndexes := func(ix, iy int) SliceIndex {
if ix >= 0 {
Expand Down Expand Up @@ -492,7 +462,6 @@ func (s *state) compareSlice(t reflect.Type, vx, vy reflect.Value) {
iy++
}
}
return
}

func (s *state) compareMap(t reflect.Type, vx, vy reflect.Value) {
Expand All @@ -501,6 +470,8 @@ func (s *state) compareMap(t reflect.Type, vx, vy reflect.Value) {
return
}

// TODO: Support cyclic data structures.

// We combine and sort the two map keys so that we can perform the
// comparisons in a deterministic order.
step := MapIndex{&mapIndex{pathStep: pathStep{typ: t.Elem()}}}
Expand Down Expand Up @@ -530,6 +501,31 @@ func (s *state) compareMap(t reflect.Type, vx, vy reflect.Value) {
}
}

func (s *state) comparePtr(t reflect.Type, vx, vy reflect.Value) {
if vx.IsNil() || vy.IsNil() {
s.report(vx.IsNil() && vy.IsNil(), 0)
return
}

// TODO: Support cyclic data structures.

vx, vy = vx.Elem(), vy.Elem()
s.compareAny(Indirect{&indirect{pathStep{t.Elem(), vx, vy}}})
}

func (s *state) compareInterface(t reflect.Type, vx, vy reflect.Value) {
if vx.IsNil() || vy.IsNil() {
s.report(vx.IsNil() && vy.IsNil(), 0)
return
}
vx, vy = vx.Elem(), vy.Elem()
if vx.Type() != vy.Type() {
s.report(false, 0)
return
}
s.compareAny(TypeAssertion{&typeAssertion{pathStep{vx.Type(), vx, vy}}})
}

func (s *state) report(eq bool, rf resultFlags) {
if rf&reportByIgnore == 0 {
if eq {
Expand Down

0 comments on commit 6f77996

Please sign in to comment.