in pkg/cloud/api/diff.go [119:224]
func (d *differ[T]) do(p Path, av, bv reflect.Value) error {
// cmpZero applies to pointer, slice and map values. Returns true if no
// further diff'ing is required for the values.
cmpZero := func() bool {
switch {
case av.IsZero() && bv.IsZero():
return true
case !av.IsZero() && bv.IsZero():
d.result.add(DiffItemOnlyInA, p, av, bv)
return true
case av.IsZero() && !bv.IsZero():
d.result.add(DiffItemOnlyInB, p, av, bv)
return true
}
return false
}
switch {
case isBasicV(av):
if !av.Equal(bv) {
d.result.add(DiffItemDifferent, p, av, bv)
}
return nil
case av.Type().Kind() == reflect.Pointer:
if cmpZero() {
return nil
}
return d.do(p.Pointer(), av.Elem(), bv.Elem())
case av.Type().Kind() == reflect.Struct:
for i := 0; i < av.NumField(); i++ {
afv := av.Field(i)
aft := av.Type().Field(i)
if aft.Name == "NullFields" || aft.Name == "ForceSendFields" {
continue
}
fp := p.Field(aft.Name)
switch d.traits.FieldType(fp) {
case FieldTypeOutputOnly, FieldTypeSystem:
continue
}
bfv := bv.FieldByName(aft.Name)
if !bfv.IsValid() {
d.result.add(DiffItemOnlyInA, p, av, bv)
continue
}
if err := d.do(fp, afv, bfv); err != nil {
return fmt.Errorf("differ struct %p: %w", fp, err)
}
}
return nil
case av.Type().Kind() == reflect.Slice:
if cmpZero() {
return nil
}
// If we find the list lengths are difference, don't recurse into a list
// to compare item by item. There isn't a use case for a more fine grain
// diff within a slice at the moment.
if av.Len() != bv.Len() {
d.result.add(DiffItemDifferent, p, av, bv)
return nil
}
for i := 0; i < av.Len(); i++ {
asv := av.Index(i)
bsv := bv.Index(i)
sp := p.Index(i)
if err := d.do(sp, asv, bsv); err != nil {
return fmt.Errorf("differ slice %p: %w", sp, err)
}
}
return nil
case av.Type().Kind() == reflect.Map:
if cmpZero() {
return nil
}
if av.Len() != bv.Len() {
d.result.add(DiffItemDifferent, p, av, bv)
return nil
}
// For maps of the same size, for the maps to be equal, all keys in A
// must be present in B for these to be equal. This means we don't have
// to check in the opposite direction from B to A. However, this makes
// the Diff function non-symmetric.
for _, amk := range av.MapKeys() {
amv := av.MapIndex(amk)
bmv := bv.MapIndex(amk)
mp := p.MapIndex(amk)
if !bmv.IsValid() {
d.result.add(DiffItemDifferent, mp, amv, bmv)
}
if err := d.do(mp, amv, bmv); err != nil {
return fmt.Errorf("differ map %p: %w", mp, err)
}
}
return nil
}
return fmt.Errorf("differ: invalid type: %s", av.Type())
}