diff.go 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  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 Diff(a, b interface{}) Entries {
  64. valA := reflect.Indirect(reflect.ValueOf(a))
  65. valB := reflect.Indirect(reflect.ValueOf(b))
  66. d := make(Entries, 0, valA.NumField())
  67. if valA.Type() != valB.Type() {
  68. return d
  69. }
  70. for i := 0; i < valA.NumField(); i++ {
  71. fieldA := valA.Field(i)
  72. fieldB := valB.Field(i)
  73. if !fieldA.CanInterface() || !fieldB.CanInterface() {
  74. continue
  75. }
  76. intA := fieldA.Interface()
  77. intB := fieldB.Interface()
  78. if !reflect.DeepEqual(intA, intB) {
  79. name := valB.Type().Field(i).Name
  80. value := intB
  81. if fieldB.Kind() == reflect.Struct {
  82. value = Diff(intA, intB)
  83. }
  84. d = append(d, Entry{name, value})
  85. }
  86. }
  87. return d
  88. }