diff.go 2.6 KB

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