121 lines
3.5 KiB
Go
121 lines
3.5 KiB
Go
// Copyright 2019, The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package cmp
|
|
|
|
import "reflect"
|
|
|
|
// valueNode represents a single node within a report, which is a
|
|
// structured representation of the value tree, containing information
|
|
// regarding which nodes are equal or not.
|
|
type valueNode struct {
|
|
parent *valueNode
|
|
|
|
Type reflect.Type
|
|
ValueX reflect.Value
|
|
ValueY reflect.Value
|
|
|
|
// NumSame is the number of leaf nodes that are equal.
|
|
// All descendants are equal only if NumDiff is 0.
|
|
NumSame int
|
|
// NumDiff is the number of leaf nodes that are not equal.
|
|
NumDiff int
|
|
// NumIgnored is the number of leaf nodes that are ignored.
|
|
NumIgnored int
|
|
// NumCompared is the number of leaf nodes that were compared
|
|
// using an Equal method or Comparer function.
|
|
NumCompared int
|
|
// NumTransformed is the number of non-leaf nodes that were transformed.
|
|
NumTransformed int
|
|
// NumChildren is the number of transitive descendants of this node.
|
|
// This counts from zero; thus, leaf nodes have no descendants.
|
|
NumChildren int
|
|
// MaxDepth is the maximum depth of the tree. This counts from zero;
|
|
// thus, leaf nodes have a depth of zero.
|
|
MaxDepth int
|
|
|
|
// Records is a list of struct fields, slice elements, or map entries.
|
|
Records []reportRecord // If populated, implies Value is not populated
|
|
|
|
// Value is the result of a transformation, pointer indirect, of
|
|
// type assertion.
|
|
Value *valueNode // If populated, implies Records is not populated
|
|
|
|
// TransformerName is the name of the transformer.
|
|
TransformerName string // If non-empty, implies Value is populated
|
|
}
|
|
type reportRecord struct {
|
|
Key reflect.Value // Invalid for slice element
|
|
Value *valueNode
|
|
}
|
|
|
|
func (parent *valueNode) PushStep(ps PathStep) (child *valueNode) {
|
|
vx, vy := ps.Values()
|
|
child = &valueNode{parent: parent, Type: ps.Type(), ValueX: vx, ValueY: vy}
|
|
switch s := ps.(type) {
|
|
case StructField:
|
|
assert(parent.Value == nil)
|
|
parent.Records = append(parent.Records, reportRecord{Key: reflect.ValueOf(s.Name()), Value: child})
|
|
case SliceIndex:
|
|
assert(parent.Value == nil)
|
|
parent.Records = append(parent.Records, reportRecord{Value: child})
|
|
case MapIndex:
|
|
assert(parent.Value == nil)
|
|
parent.Records = append(parent.Records, reportRecord{Key: s.Key(), Value: child})
|
|
case Indirect:
|
|
assert(parent.Value == nil && parent.Records == nil)
|
|
parent.Value = child
|
|
case TypeAssertion:
|
|
assert(parent.Value == nil && parent.Records == nil)
|
|
parent.Value = child
|
|
case Transform:
|
|
assert(parent.Value == nil && parent.Records == nil)
|
|
parent.Value = child
|
|
parent.TransformerName = s.Name()
|
|
parent.NumTransformed++
|
|
default:
|
|
assert(parent == nil) // Must be the root step
|
|
}
|
|
return child
|
|
}
|
|
|
|
func (r *valueNode) Report(rs Result) {
|
|
assert(r.MaxDepth == 0) // May only be called on leaf nodes
|
|
|
|
if rs.ByIgnore() {
|
|
r.NumIgnored++
|
|
} else {
|
|
if rs.Equal() {
|
|
r.NumSame++
|
|
} else {
|
|
r.NumDiff++
|
|
}
|
|
}
|
|
assert(r.NumSame+r.NumDiff+r.NumIgnored == 1)
|
|
|
|
if rs.ByMethod() {
|
|
r.NumCompared++
|
|
}
|
|
if rs.ByFunc() {
|
|
r.NumCompared++
|
|
}
|
|
assert(r.NumCompared <= 1)
|
|
}
|
|
|
|
func (child *valueNode) PopStep() (parent *valueNode) {
|
|
if child.parent == nil {
|
|
return nil
|
|
}
|
|
parent = child.parent
|
|
parent.NumSame += child.NumSame
|
|
parent.NumDiff += child.NumDiff
|
|
parent.NumIgnored += child.NumIgnored
|
|
parent.NumCompared += child.NumCompared
|
|
parent.NumTransformed += child.NumTransformed
|
|
parent.NumChildren += child.NumChildren + 1
|
|
if parent.MaxDepth < child.MaxDepth+1 {
|
|
parent.MaxDepth = child.MaxDepth + 1
|
|
}
|
|
return parent
|
|
}
|