aboutsummaryrefslogtreecommitdiff
path: root/cmp
diff options
context:
space:
mode:
authorJoe Tsai <joetsai@digital-static.net>2019-02-16 21:39:33 -0800
committerGitHub <noreply@github.com>2019-02-16 21:39:33 -0800
commit8ca8745eefdc2a98d5e00dab0e88b036d7bcd62c (patch)
treec968a52a585811a945cdc1965a1e26602dacbfa1 /cmp
parentfb7c318bbf8ca09bdddb5fbd1d721927abca6077 (diff)
downloadgo-cmp-8ca8745eefdc2a98d5e00dab0e88b036d7bcd62c.tar.gz
Reorder logic to be consistent (#107)
Reorder logic to consistently handle steps in the order of: StructField, SliceIndex, MapIndex, Indirect, TypeAssertion
Diffstat (limited to 'cmp')
-rw-r--r--cmp/compare.go102
-rw-r--r--cmp/path.go106
2 files changed, 102 insertions, 106 deletions
diff --git a/cmp/compare.go b/cmp/compare.go
index 30196a4..7d172ba 100644
--- a/cmp/compare.go
+++ b/cmp/compare.go
@@ -240,6 +240,21 @@ func (s *state) compareAny(vx, vy reflect.Value) {
case reflect.Func:
s.report(vx.IsNil() && vy.IsNil(), vx, vy)
return
+ case reflect.Struct:
+ s.compareStruct(vx, vy, t)
+ return
+ case reflect.Slice:
+ if vx.IsNil() || vy.IsNil() {
+ s.report(vx.IsNil() && vy.IsNil(), vx, vy)
+ return
+ }
+ fallthrough
+ case reflect.Array:
+ s.compareSlice(vx, vy, t)
+ return
+ case reflect.Map:
+ s.compareMap(vx, vy, t)
+ return
case reflect.Ptr:
if vx.IsNil() || vy.IsNil() {
s.report(vx.IsNil() && vy.IsNil(), vx, vy)
@@ -262,21 +277,6 @@ func (s *state) compareAny(vx, vy reflect.Value) {
defer s.curPath.pop()
s.compareAny(vx.Elem(), vy.Elem())
return
- case reflect.Slice:
- if vx.IsNil() || vy.IsNil() {
- s.report(vx.IsNil() && vy.IsNil(), vx, vy)
- return
- }
- fallthrough
- case reflect.Array:
- s.compareArray(vx, vy, t)
- return
- case reflect.Map:
- s.compareMap(vx, vy, t)
- return
- case reflect.Struct:
- s.compareStruct(vx, vy, t)
- return
default:
panic(fmt.Sprintf("%v kind not handled", t.Kind()))
}
@@ -393,7 +393,42 @@ func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value {
return v
}
-func (s *state) compareArray(vx, vy reflect.Value, t reflect.Type) {
+func (s *state) compareStruct(vx, vy reflect.Value, t reflect.Type) {
+ var vax, vay reflect.Value // Addressable versions of vx and vy
+
+ step := &structField{}
+ s.curPath.push(step)
+ defer s.curPath.pop()
+ for i := 0; i < t.NumField(); i++ {
+ vvx := vx.Field(i)
+ vvy := vy.Field(i)
+ step.typ = t.Field(i).Type
+ step.name = t.Field(i).Name
+ step.idx = i
+ step.unexported = !isExported(step.name)
+ if step.unexported {
+ if step.name == "_" {
+ continue
+ }
+ // Defer checking of unexported fields until later to give an
+ // Ignore a chance to ignore the field.
+ if !vax.IsValid() || !vay.IsValid() {
+ // For unsafeRetrieveField to work, the parent struct must
+ // be addressable. Create a new copy of the values if
+ // necessary to make them addressable.
+ vax = makeAddressable(vx)
+ vay = makeAddressable(vy)
+ }
+ step.force = s.exporters[t]
+ step.pvx = vax
+ step.pvy = vay
+ step.field = t.Field(i)
+ }
+ s.compareAny(vvx, vvy)
+ }
+}
+
+func (s *state) compareSlice(vx, vy reflect.Value, t reflect.Type) {
step := &sliceIndex{pathStep{t.Elem()}, 0, 0}
s.curPath.push(step)
@@ -477,41 +512,6 @@ func (s *state) compareMap(vx, vy reflect.Value, t reflect.Type) {
}
}
-func (s *state) compareStruct(vx, vy reflect.Value, t reflect.Type) {
- var vax, vay reflect.Value // Addressable versions of vx and vy
-
- step := &structField{}
- s.curPath.push(step)
- defer s.curPath.pop()
- for i := 0; i < t.NumField(); i++ {
- vvx := vx.Field(i)
- vvy := vy.Field(i)
- step.typ = t.Field(i).Type
- step.name = t.Field(i).Name
- step.idx = i
- step.unexported = !isExported(step.name)
- if step.unexported {
- if step.name == "_" {
- continue
- }
- // Defer checking of unexported fields until later to give an
- // Ignore a chance to ignore the field.
- if !vax.IsValid() || !vay.IsValid() {
- // For unsafeRetrieveField to work, the parent struct must
- // be addressable. Create a new copy of the values if
- // necessary to make them addressable.
- vax = makeAddressable(vx)
- vay = makeAddressable(vy)
- }
- step.force = s.exporters[t]
- step.pvx = vax
- step.pvy = vay
- step.field = t.Field(i)
- }
- s.compareAny(vvx, vvy)
- }
-}
-
// report records the result of a single comparison.
// It also calls Report if any reporter is registered.
func (s *state) report(eq bool, vx, vy reflect.Value) {
diff --git a/cmp/path.go b/cmp/path.go
index 24a5657..49b622b 100644
--- a/cmp/path.go
+++ b/cmp/path.go
@@ -33,6 +33,13 @@ type (
isPathStep()
}
+ // StructField represents a struct field access on a field called Name.
+ StructField interface {
+ PathStep
+ Name() string
+ Index() int
+ isStructField()
+ }
// SliceIndex is an index operation on a slice or array at some index Key.
SliceIndex interface {
PathStep
@@ -57,23 +64,16 @@ type (
Key() reflect.Value
isMapIndex()
}
- // TypeAssertion represents a type assertion on an interface.
- TypeAssertion interface {
- PathStep
- isTypeAssertion()
- }
- // StructField represents a struct field access on a field called Name.
- StructField interface {
- PathStep
- Name() string
- Index() int
- isStructField()
- }
// Indirect represents pointer indirection on the parent type.
Indirect interface {
PathStep
isIndirect()
}
+ // TypeAssertion represents a type assertion on an interface.
+ TypeAssertion interface {
+ PathStep
+ isTypeAssertion()
+ }
// Transform is a transformation from the parent type to the current type.
Transform interface {
PathStep
@@ -188,17 +188,6 @@ type (
typ reflect.Type
}
- sliceIndex struct {
- pathStep
- xkey, ykey int
- }
- mapIndex struct {
- pathStep
- key reflect.Value
- }
- typeAssertion struct {
- pathStep
- }
structField struct {
pathStep
name string
@@ -211,9 +200,20 @@ type (
pvx, pvy reflect.Value // Parent values
field reflect.StructField // Field information
}
+ sliceIndex struct {
+ pathStep
+ xkey, ykey int
+ }
+ mapIndex struct {
+ pathStep
+ key reflect.Value
+ }
indirect struct {
pathStep
}
+ typeAssertion struct {
+ pathStep
+ }
transform struct {
pathStep
trans *transformer
@@ -231,6 +231,12 @@ func (ps pathStep) String() string {
}
return fmt.Sprintf("{%s}", s)
}
+func (ps pathStep) isPathStep() {}
+
+func (sf structField) String() string { return fmt.Sprintf(".%s", sf.name) }
+func (sf structField) Name() string { return sf.name }
+func (sf structField) Index() int { return sf.idx }
+func (sf structField) isStructField() {}
func (si sliceIndex) String() string {
switch {
@@ -247,12 +253,6 @@ func (si sliceIndex) String() string {
return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey)
}
}
-func (mi mapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) }
-func (ta typeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) }
-func (sf structField) String() string { return fmt.Sprintf(".%s", sf.name) }
-func (in indirect) String() string { return "*" }
-func (tf transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) }
-
func (si sliceIndex) Key() int {
if si.xkey != si.ykey {
return -1
@@ -260,35 +260,31 @@ func (si sliceIndex) Key() int {
return si.xkey
}
func (si sliceIndex) SplitKeys() (x, y int) { return si.xkey, si.ykey }
-func (mi mapIndex) Key() reflect.Value { return mi.key }
-func (sf structField) Name() string { return sf.name }
-func (sf structField) Index() int { return sf.idx }
-func (tf transform) Name() string { return tf.trans.name }
-func (tf transform) Func() reflect.Value { return tf.trans.fnc }
-func (tf transform) Option() Option { return tf.trans }
+func (si sliceIndex) isSliceIndex() {}
-func (pathStep) isPathStep() {}
-func (sliceIndex) isSliceIndex() {}
-func (mapIndex) isMapIndex() {}
-func (typeAssertion) isTypeAssertion() {}
-func (structField) isStructField() {}
-func (indirect) isIndirect() {}
-func (transform) isTransform() {}
+func (mi mapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) }
+func (mi mapIndex) Key() reflect.Value { return mi.key }
+func (mi mapIndex) isMapIndex() {}
-var (
- _ SliceIndex = sliceIndex{}
- _ MapIndex = mapIndex{}
- _ TypeAssertion = typeAssertion{}
- _ StructField = structField{}
- _ Indirect = indirect{}
- _ Transform = transform{}
+func (in indirect) String() string { return "*" }
+func (in indirect) isIndirect() {}
- _ PathStep = sliceIndex{}
- _ PathStep = mapIndex{}
- _ PathStep = typeAssertion{}
- _ PathStep = structField{}
- _ PathStep = indirect{}
- _ PathStep = transform{}
+func (ta typeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) }
+func (ta typeAssertion) isTypeAssertion() {}
+
+func (tf transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) }
+func (tf transform) Name() string { return tf.trans.name }
+func (tf transform) Func() reflect.Value { return tf.trans.fnc }
+func (tf transform) Option() Option { return tf.trans }
+func (tf transform) isTransform() {}
+
+var (
+ _ PathStep = StructField(structField{})
+ _ PathStep = SliceIndex(sliceIndex{})
+ _ PathStep = MapIndex(mapIndex{})
+ _ PathStep = Indirect(indirect{})
+ _ PathStep = TypeAssertion(typeAssertion{})
+ _ PathStep = Transform(transform{})
)
// isExported reports whether the identifier is exported.