diff.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package structdiff
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "reflect"
  7. "strings"
  8. "sync"
  9. )
  10. var bufPool = sync.Pool{
  11. New: func() interface{} {
  12. return new(bytes.Buffer)
  13. },
  14. }
  15. var builderPool = sync.Pool{
  16. New: func() interface{} {
  17. return new(strings.Builder)
  18. },
  19. }
  20. type Entry struct {
  21. Name string
  22. Value interface{}
  23. }
  24. type Entries []Entry
  25. func (d Entries) String() string {
  26. buf := builderPool.Get().(*strings.Builder)
  27. last := len(d) - 1
  28. buf.Reset()
  29. for i, e := range d {
  30. buf.WriteString(e.Name)
  31. buf.WriteString(": ")
  32. if dd, ok := e.Value.(Entries); ok {
  33. buf.WriteByte('{')
  34. buf.WriteString(dd.String())
  35. buf.WriteByte('}')
  36. } else {
  37. fmt.Fprint(buf, e.Value)
  38. }
  39. if i != last {
  40. buf.WriteString("; ")
  41. }
  42. }
  43. return buf.String()
  44. }
  45. func (d Entries) MarshalJSON() ([]byte, error) {
  46. buf := bufPool.Get().(*bytes.Buffer)
  47. last := len(d) - 1
  48. buf.Reset()
  49. buf.WriteByte('{')
  50. for i, e := range d {
  51. j, err := json.Marshal(e.Value)
  52. if err != nil {
  53. return nil, err
  54. }
  55. fmt.Fprintf(buf, "%q:%s", e.Name, j)
  56. if i != last {
  57. buf.WriteByte(',')
  58. }
  59. }
  60. buf.WriteByte('}')
  61. return buf.Bytes(), nil
  62. }
  63. func (d Entries) flatten(m map[string]interface{}, prefix string) {
  64. for _, e := range d {
  65. key := e.Name
  66. if len(prefix) > 0 {
  67. key = prefix + "." + key
  68. }
  69. if dd, ok := e.Value.(Entries); ok {
  70. dd.flatten(m, key)
  71. } else {
  72. m[key] = e.Value
  73. }
  74. }
  75. }
  76. func (d Entries) Flatten() map[string]interface{} {
  77. m := make(map[string]interface{})
  78. d.flatten(m, "")
  79. return m
  80. }
  81. func Diff(a, b interface{}) Entries {
  82. valA := reflect.Indirect(reflect.ValueOf(a))
  83. valB := reflect.Indirect(reflect.ValueOf(b))
  84. d := make(Entries, 0, valA.NumField())
  85. if valA.Type() != valB.Type() {
  86. return d
  87. }
  88. for i := 0; i < valA.NumField(); i++ {
  89. fieldA := valA.Field(i)
  90. fieldB := valB.Field(i)
  91. if !fieldA.CanInterface() || !fieldB.CanInterface() {
  92. continue
  93. }
  94. intA := fieldA.Interface()
  95. intB := fieldB.Interface()
  96. if !reflect.DeepEqual(intA, intB) {
  97. name := valB.Type().Field(i).Name
  98. value := intB
  99. if fieldB.Kind() == reflect.Struct {
  100. value = Diff(intA, intB)
  101. }
  102. d = append(d, Entry{name, value})
  103. }
  104. }
  105. return d
  106. }