diff options
-rw-r--r-- | cmp/compare.go | 102 | ||||
-rw-r--r-- | cmp/path.go | 106 |
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. |