diff.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. package activity
  2. import (
  3. "fmt"
  4. "reflect"
  5. "strconv"
  6. "time"
  7. "github.com/qor5/admin/media/media_library"
  8. )
  9. var (
  10. // @snippet_begin(ActivityDefaultIgnoredFields)
  11. DefaultIgnoredFields = []string{"ID", "UpdatedAt", "DeletedAt", "CreatedAt"}
  12. // @snippet_end
  13. // @snippet_begin(ActivityDefaultTypeHandles)
  14. DefaultTypeHandles = map[reflect.Type]TypeHandler{
  15. reflect.TypeOf(time.Time{}): func(old, now interface{}, prefixField string) []Diff {
  16. oldString := old.(time.Time).Format(time.RFC3339)
  17. nowString := now.(time.Time).Format(time.RFC3339)
  18. if oldString != nowString {
  19. return []Diff{
  20. {Field: prefixField, Old: oldString, Now: nowString},
  21. }
  22. }
  23. return []Diff{}
  24. },
  25. reflect.TypeOf(media_library.MediaBox{}): func(old, now interface{}, prefixField string) (diffs []Diff) {
  26. oldMediaBox := old.(media_library.MediaBox)
  27. nowMediaBox := now.(media_library.MediaBox)
  28. if oldMediaBox.Url != nowMediaBox.Url {
  29. diffs = append(diffs, Diff{Field: formatFieldByDot(prefixField, "Url"), Old: oldMediaBox.Url, Now: nowMediaBox.Url})
  30. }
  31. if oldMediaBox.Description != nowMediaBox.Description {
  32. diffs = append(diffs, Diff{Field: formatFieldByDot(prefixField, "Description"), Old: oldMediaBox.Description, Now: nowMediaBox.Description})
  33. }
  34. if oldMediaBox.VideoLink != nowMediaBox.VideoLink {
  35. diffs = append(diffs, Diff{Field: formatFieldByDot(prefixField, "VideoLink"), Old: oldMediaBox.VideoLink, Now: nowMediaBox.VideoLink})
  36. }
  37. return diffs
  38. },
  39. }
  40. // @snippet_end
  41. )
  42. // @snippet_begin(ActivityTypeHandle)
  43. type TypeHandler func(old, now interface{}, prefixField string) []Diff
  44. // @snippet_end
  45. type Diff struct {
  46. Field string
  47. Old string
  48. Now string
  49. }
  50. type DiffBuilder struct {
  51. mb *ModelBuilder
  52. diffs []Diff
  53. }
  54. func NewDiffBuilder(mb *ModelBuilder) *DiffBuilder {
  55. return &DiffBuilder{
  56. mb: mb,
  57. }
  58. }
  59. func (db *DiffBuilder) Diff(old, now interface{}) ([]Diff, error) {
  60. err := db.diffLoop(reflect.Indirect(reflect.ValueOf(old)), reflect.Indirect(reflect.ValueOf(now)), "")
  61. return db.diffs, err
  62. }
  63. func (db *DiffBuilder) diffLoop(old, now reflect.Value, prefixField string) error {
  64. if old.Type() != now.Type() {
  65. return fmt.Errorf("old and now type mismatch: %v != %v", old.Type(), now.Type())
  66. }
  67. handleNil := func() bool {
  68. if old.IsNil() && now.IsNil() {
  69. return true
  70. }
  71. if old.IsNil() && !now.IsNil() {
  72. db.diffs = append(db.diffs, Diff{Field: prefixField, Old: "", Now: fmt.Sprintf("%+v", now.Interface())})
  73. return true
  74. }
  75. if !old.IsNil() && now.IsNil() {
  76. db.diffs = append(db.diffs, Diff{Field: prefixField, Old: fmt.Sprintf("%+v", old.Interface()), Now: ""})
  77. return true
  78. }
  79. return false
  80. }
  81. switch now.Kind() {
  82. case reflect.Invalid, reflect.Chan, reflect.Func, reflect.UnsafePointer, reflect.Uintptr:
  83. return nil
  84. case reflect.Interface, reflect.Ptr:
  85. if handleNil() {
  86. return nil
  87. }
  88. return db.diffLoop(old.Elem(), now.Elem(), prefixField)
  89. case reflect.Struct:
  90. for i := 0; i < now.Type().NumField(); i++ {
  91. if !old.Field(i).CanInterface() {
  92. continue
  93. }
  94. field := now.Type().Field(i)
  95. var needContinue bool
  96. for _, ignoredField := range DefaultIgnoredFields {
  97. if ignoredField == field.Name {
  98. needContinue = true
  99. continue
  100. }
  101. }
  102. for _, ignoredField := range db.mb.ignoredFields {
  103. if ignoredField == field.Name {
  104. needContinue = true
  105. continue
  106. }
  107. }
  108. if needContinue {
  109. continue
  110. }
  111. newPrefixField := formatFieldByDot(prefixField, field.Name)
  112. if f := DefaultTypeHandles[field.Type]; f != nil {
  113. db.diffs = append(db.diffs, f(old.Field(i).Interface(), now.Field(i).Interface(), newPrefixField)...)
  114. continue
  115. }
  116. if f := db.mb.typeHanders[field.Type]; f != nil {
  117. db.diffs = append(db.diffs, f(old.Field(i).Interface(), now.Field(i).Interface(), newPrefixField)...)
  118. continue
  119. }
  120. err := db.diffLoop(old.Field(i), now.Field(i), newPrefixField)
  121. if err != nil {
  122. return err
  123. }
  124. }
  125. case reflect.Array, reflect.Slice:
  126. if now.Kind() == reflect.Slice {
  127. if handleNil() {
  128. return nil
  129. }
  130. }
  131. var (
  132. oldLen = old.Len()
  133. nowLen = now.Len()
  134. minLen int
  135. added bool
  136. deleted bool
  137. )
  138. if oldLen > nowLen {
  139. minLen = nowLen
  140. deleted = true
  141. }
  142. if oldLen < nowLen {
  143. minLen = oldLen
  144. added = true
  145. }
  146. if oldLen == nowLen {
  147. minLen = oldLen
  148. }
  149. for i := 0; i < minLen; i++ {
  150. newPrefixField := formatFieldByDot(prefixField, strconv.Itoa(i))
  151. err := db.diffLoop(old.Index(i), now.Index(i), newPrefixField)
  152. if err != nil {
  153. return err
  154. }
  155. }
  156. if added {
  157. for i := minLen; i < nowLen; i++ {
  158. newPrefixField := formatFieldByDot(prefixField, strconv.Itoa(i))
  159. db.diffs = append(db.diffs, Diff{Field: newPrefixField, Old: "", Now: fmt.Sprintf("%+v", now.Index(i).Interface())})
  160. }
  161. }
  162. if deleted {
  163. for i := minLen; i < oldLen; i++ {
  164. newPrefixField := formatFieldByDot(prefixField, strconv.Itoa(i))
  165. db.diffs = append(db.diffs, Diff{Field: newPrefixField, Old: fmt.Sprintf("%+v", old.Index(i).Interface()), Now: ""})
  166. }
  167. }
  168. case reflect.Map:
  169. if handleNil() {
  170. return nil
  171. }
  172. var (
  173. oldKeys = old.MapKeys()
  174. newKeys = now.MapKeys()
  175. sameKeys = []reflect.Value{}
  176. addedKeys = []reflect.Value{}
  177. deletedKeys = []reflect.Value{}
  178. )
  179. for _, oldKey := range oldKeys {
  180. var find bool
  181. for _, newKey := range newKeys {
  182. if oldKey.Interface() == newKey.Interface() {
  183. find = true
  184. }
  185. }
  186. if find {
  187. sameKeys = append(sameKeys, oldKey)
  188. }
  189. if !find {
  190. deletedKeys = append(deletedKeys, oldKey)
  191. }
  192. }
  193. for _, newKey := range newKeys {
  194. var find bool
  195. for _, oldKey := range oldKeys {
  196. if oldKey.Interface() == newKey.Interface() {
  197. find = true
  198. }
  199. }
  200. if !find {
  201. addedKeys = append(addedKeys, newKey)
  202. }
  203. }
  204. for _, key := range sameKeys {
  205. newPrefixField := formatFieldByDot(prefixField, key.String())
  206. err := db.diffLoop(old.MapIndex(key), now.MapIndex(key), newPrefixField)
  207. if err != nil {
  208. return err
  209. }
  210. }
  211. for _, key := range addedKeys {
  212. newPrefixField := formatFieldByDot(prefixField, key.String())
  213. db.diffs = append(db.diffs, Diff{Field: newPrefixField, Old: "", Now: fmt.Sprintf("%+v", now.MapIndex(key).Interface())})
  214. }
  215. for _, key := range deletedKeys {
  216. newPrefixField := formatFieldByDot(prefixField, key.String())
  217. db.diffs = append(db.diffs, Diff{Field: newPrefixField, Old: fmt.Sprintf("%+v", old.MapIndex(key).Interface()), Now: ""})
  218. }
  219. default:
  220. if old.Interface() != now.Interface() {
  221. db.diffs = append(db.diffs, Diff{Field: prefixField, Old: fmt.Sprintf("%v", old.Interface()), Now: fmt.Sprintf("%v", now.Interface())})
  222. }
  223. }
  224. return nil
  225. }
  226. func formatFieldByDot(prefix string, suffix string) string {
  227. if len(prefix) == 0 {
  228. return suffix
  229. }
  230. if len(suffix) == 0 {
  231. return prefix
  232. }
  233. return prefix + "." + suffix
  234. }