Skip to content

Commit

Permalink
Improve clarity of compareAny
Browse files Browse the repository at this point in the history
  • Loading branch information
dsnet committed Mar 12, 2019
1 parent 2940eda commit 2deb0e3
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 @@ -123,8 +123,7 @@ func Equal(x, y interface{}, opts ...Option) bool {
// Do not depend on this output being stable.
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 @@ -155,9 +154,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 @@ -209,8 +206,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 @@ -234,67 +229,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{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{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 @@ -422,6 +384,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{pathStep: pathStep{typ: t.Elem()}}
withIndexes := func(ix, iy int) *sliceIndex {
if ix >= 0 {
Expand Down Expand Up @@ -491,7 +461,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 @@ -500,6 +469,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{pathStep: pathStep{typ: t.Elem()}}
Expand Down Expand Up @@ -529,6 +500,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{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{pathStep{vx.Type(), vx, vy}})
}

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

0 comments on commit 2deb0e3

Please sign in to comment.