1
0

diff.go 2.4 KB

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