diff --git a/metrics/metrics.go b/metrics/metrics.go index 50db8e22..42b95bf4 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -71,11 +71,7 @@ func setMetadata(ctx context.Context, key string, value any) { } func SetMetadata(ctx context.Context, key string, value any) { - type diffable interface { - Diff() structdiff.Entries - } - - if diff, ok := value.(diffable); ok { + if diff, ok := value.(structdiff.Diffable); ok { m := diff.Diff().Flatten() for k, v := range m { setMetadata(ctx, fmt.Sprintf("%s.%s", key, k), v) diff --git a/structdiff/diff.go b/structdiff/diff.go index 6751da54..461680fa 100644 --- a/structdiff/diff.go +++ b/structdiff/diff.go @@ -6,19 +6,10 @@ import ( "fmt" "reflect" "strings" - "sync" ) -var bufPool = sync.Pool{ - New: func() interface{} { - return new(bytes.Buffer) - }, -} - -var builderPool = sync.Pool{ - New: func() interface{} { - return new(strings.Builder) - }, +type Diffable interface { + Diff() Entries } type Entry struct { @@ -29,11 +20,9 @@ type Entry struct { type Entries []Entry func (d Entries) String() string { - buf := builderPool.Get().(*strings.Builder) + buf := new(strings.Builder) last := len(d) - 1 - buf.Reset() - for i, e := range d { buf.WriteString(e.Name) buf.WriteString(": ") @@ -43,7 +32,7 @@ func (d Entries) String() string { buf.WriteString(dd.String()) buf.WriteByte('}') } else { - fmt.Fprint(buf, e.Value) + fmt.Fprintf(buf, "%+v", e.Value) } if i != last { @@ -55,11 +44,9 @@ func (d Entries) String() string { } func (d Entries) MarshalJSON() ([]byte, error) { - buf := bufPool.Get().(*bytes.Buffer) + buf := new(bytes.Buffer) last := len(d) - 1 - buf.Reset() - buf.WriteByte('{') for i, e := range d { @@ -101,6 +88,45 @@ func (d Entries) Flatten() map[string]interface{} { return m } +func valDiff(a, b reflect.Value) (any, bool) { + if !a.CanInterface() || !b.CanInterface() { + return nil, false + } + + typeB := b.Type() + + if a.Type() != typeB { + return b.Interface(), true + } + + intA := a.Interface() + intB := b.Interface() + + if reflect.DeepEqual(intA, intB) { + return nil, false + } + + if typeB.Kind() == reflect.Struct { + return Diff(intA, intB), true + } + + if typeB.Kind() == reflect.Ptr && typeB.Elem().Kind() == reflect.Struct { + if !a.IsNil() && !b.IsNil() { + return Diff(intA, intB), true + } + + if !b.IsNil() { + if diffable, ok := intB.(Diffable); ok { + return diffable.Diff(), true + } + } + + return nil, true + } + + return intB, true +} + func Diff(a, b interface{}) Entries { valA := reflect.Indirect(reflect.ValueOf(a)) valB := reflect.Indirect(reflect.ValueOf(b)) @@ -115,22 +141,9 @@ func Diff(a, b interface{}) Entries { fieldA := valA.Field(i) fieldB := valB.Field(i) - if !fieldA.CanInterface() || !fieldB.CanInterface() { - continue - } - - intA := fieldA.Interface() - intB := fieldB.Interface() - - if !reflect.DeepEqual(intA, intB) { + if v, ok := valDiff(fieldA, fieldB); ok { name := valB.Type().Field(i).Name - value := intB - - if fieldB.Kind() == reflect.Struct { - value = Diff(intA, intB) - } - - d = append(d, Entry{name, value}) + d = append(d, Entry{name, v}) } }