| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 | package structdiffimport (	"bytes"	"encoding/json"	"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 Entry struct {	Name  string	Value interface{}}type Entries []Entryfunc (d Entries) String() string {	buf := builderPool.Get().(*strings.Builder)	last := len(d) - 1	buf.Reset()	for i, e := range d {		buf.WriteString(e.Name)		buf.WriteString(": ")		if dd, ok := e.Value.(Entries); ok {			buf.WriteByte('{')			buf.WriteString(dd.String())			buf.WriteByte('}')		} else {			fmt.Fprint(buf, e.Value)		}		if i != last {			buf.WriteString("; ")		}	}	return buf.String()}func (d Entries) MarshalJSON() ([]byte, error) {	buf := bufPool.Get().(*bytes.Buffer)	last := len(d) - 1	buf.Reset()	buf.WriteByte('{')	for i, e := range d {		j, err := json.Marshal(e.Value)		if err != nil {			return nil, err		}		fmt.Fprintf(buf, "%q:%s", e.Name, j)		if i != last {			buf.WriteByte(',')		}	}	buf.WriteByte('}')	return buf.Bytes(), nil}func (d Entries) flatten(m map[string]interface{}, prefix string) {	for _, e := range d {		key := e.Name		if len(prefix) > 0 {			key = prefix + "." + key		}		if dd, ok := e.Value.(Entries); ok {			dd.flatten(m, key)		} else {			m[key] = e.Value		}	}}func (d Entries) Flatten() map[string]interface{} {	m := make(map[string]interface{})	d.flatten(m, "")	return m}func Diff(a, b interface{}) Entries {	valA := reflect.Indirect(reflect.ValueOf(a))	valB := reflect.Indirect(reflect.ValueOf(b))	d := make(Entries, 0, valA.NumField())	if valA.Type() != valB.Type() {		return d	}	for i := 0; i < valA.NumField(); i++ {		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) {			name := valB.Type().Field(i).Name			value := intB			if fieldB.Kind() == reflect.Struct {				value = Diff(intA, intB)			}			d = append(d, Entry{name, value})		}	}	return d}
 |