123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- package activity
- import (
- "fmt"
- "reflect"
- "strconv"
- "time"
- "github.com/qor5/admin/media/media_library"
- )
- var (
- // @snippet_begin(ActivityDefaultIgnoredFields)
- DefaultIgnoredFields = []string{"ID", "UpdatedAt", "DeletedAt", "CreatedAt"}
- // @snippet_end
- // @snippet_begin(ActivityDefaultTypeHandles)
- DefaultTypeHandles = map[reflect.Type]TypeHandler{
- reflect.TypeOf(time.Time{}): func(old, now interface{}, prefixField string) []Diff {
- oldString := old.(time.Time).Format(time.RFC3339)
- nowString := now.(time.Time).Format(time.RFC3339)
- if oldString != nowString {
- return []Diff{
- {Field: prefixField, Old: oldString, Now: nowString},
- }
- }
- return []Diff{}
- },
- reflect.TypeOf(media_library.MediaBox{}): func(old, now interface{}, prefixField string) (diffs []Diff) {
- oldMediaBox := old.(media_library.MediaBox)
- nowMediaBox := now.(media_library.MediaBox)
- if oldMediaBox.Url != nowMediaBox.Url {
- diffs = append(diffs, Diff{Field: formatFieldByDot(prefixField, "Url"), Old: oldMediaBox.Url, Now: nowMediaBox.Url})
- }
- if oldMediaBox.Description != nowMediaBox.Description {
- diffs = append(diffs, Diff{Field: formatFieldByDot(prefixField, "Description"), Old: oldMediaBox.Description, Now: nowMediaBox.Description})
- }
- if oldMediaBox.VideoLink != nowMediaBox.VideoLink {
- diffs = append(diffs, Diff{Field: formatFieldByDot(prefixField, "VideoLink"), Old: oldMediaBox.VideoLink, Now: nowMediaBox.VideoLink})
- }
- return diffs
- },
- }
- // @snippet_end
- )
- // @snippet_begin(ActivityTypeHandle)
- type TypeHandler func(old, now interface{}, prefixField string) []Diff
- // @snippet_end
- type Diff struct {
- Field string
- Old string
- Now string
- }
- type DiffBuilder struct {
- mb *ModelBuilder
- diffs []Diff
- }
- func NewDiffBuilder(mb *ModelBuilder) *DiffBuilder {
- return &DiffBuilder{
- mb: mb,
- }
- }
- func (db *DiffBuilder) Diff(old, now interface{}) ([]Diff, error) {
- err := db.diffLoop(reflect.Indirect(reflect.ValueOf(old)), reflect.Indirect(reflect.ValueOf(now)), "")
- return db.diffs, err
- }
- func (db *DiffBuilder) diffLoop(old, now reflect.Value, prefixField string) error {
- if old.Type() != now.Type() {
- return fmt.Errorf("old and now type mismatch: %v != %v", old.Type(), now.Type())
- }
- handleNil := func() bool {
- if old.IsNil() && now.IsNil() {
- return true
- }
- if old.IsNil() && !now.IsNil() {
- db.diffs = append(db.diffs, Diff{Field: prefixField, Old: "", Now: fmt.Sprintf("%+v", now.Interface())})
- return true
- }
- if !old.IsNil() && now.IsNil() {
- db.diffs = append(db.diffs, Diff{Field: prefixField, Old: fmt.Sprintf("%+v", old.Interface()), Now: ""})
- return true
- }
- return false
- }
- switch now.Kind() {
- case reflect.Invalid, reflect.Chan, reflect.Func, reflect.UnsafePointer, reflect.Uintptr:
- return nil
- case reflect.Interface, reflect.Ptr:
- if handleNil() {
- return nil
- }
- return db.diffLoop(old.Elem(), now.Elem(), prefixField)
- case reflect.Struct:
- for i := 0; i < now.Type().NumField(); i++ {
- if !old.Field(i).CanInterface() {
- continue
- }
- field := now.Type().Field(i)
- var needContinue bool
- for _, ignoredField := range DefaultIgnoredFields {
- if ignoredField == field.Name {
- needContinue = true
- continue
- }
- }
- for _, ignoredField := range db.mb.ignoredFields {
- if ignoredField == field.Name {
- needContinue = true
- continue
- }
- }
- if needContinue {
- continue
- }
- newPrefixField := formatFieldByDot(prefixField, field.Name)
- if f := DefaultTypeHandles[field.Type]; f != nil {
- db.diffs = append(db.diffs, f(old.Field(i).Interface(), now.Field(i).Interface(), newPrefixField)...)
- continue
- }
- if f := db.mb.typeHanders[field.Type]; f != nil {
- db.diffs = append(db.diffs, f(old.Field(i).Interface(), now.Field(i).Interface(), newPrefixField)...)
- continue
- }
- err := db.diffLoop(old.Field(i), now.Field(i), newPrefixField)
- if err != nil {
- return err
- }
- }
- case reflect.Array, reflect.Slice:
- if now.Kind() == reflect.Slice {
- if handleNil() {
- return nil
- }
- }
- var (
- oldLen = old.Len()
- nowLen = now.Len()
- minLen int
- added bool
- deleted bool
- )
- if oldLen > nowLen {
- minLen = nowLen
- deleted = true
- }
- if oldLen < nowLen {
- minLen = oldLen
- added = true
- }
- if oldLen == nowLen {
- minLen = oldLen
- }
- for i := 0; i < minLen; i++ {
- newPrefixField := formatFieldByDot(prefixField, strconv.Itoa(i))
- err := db.diffLoop(old.Index(i), now.Index(i), newPrefixField)
- if err != nil {
- return err
- }
- }
- if added {
- for i := minLen; i < nowLen; i++ {
- newPrefixField := formatFieldByDot(prefixField, strconv.Itoa(i))
- db.diffs = append(db.diffs, Diff{Field: newPrefixField, Old: "", Now: fmt.Sprintf("%+v", now.Index(i).Interface())})
- }
- }
- if deleted {
- for i := minLen; i < oldLen; i++ {
- newPrefixField := formatFieldByDot(prefixField, strconv.Itoa(i))
- db.diffs = append(db.diffs, Diff{Field: newPrefixField, Old: fmt.Sprintf("%+v", old.Index(i).Interface()), Now: ""})
- }
- }
- case reflect.Map:
- if handleNil() {
- return nil
- }
- var (
- oldKeys = old.MapKeys()
- newKeys = now.MapKeys()
- sameKeys = []reflect.Value{}
- addedKeys = []reflect.Value{}
- deletedKeys = []reflect.Value{}
- )
- for _, oldKey := range oldKeys {
- var find bool
- for _, newKey := range newKeys {
- if oldKey.Interface() == newKey.Interface() {
- find = true
- }
- }
- if find {
- sameKeys = append(sameKeys, oldKey)
- }
- if !find {
- deletedKeys = append(deletedKeys, oldKey)
- }
- }
- for _, newKey := range newKeys {
- var find bool
- for _, oldKey := range oldKeys {
- if oldKey.Interface() == newKey.Interface() {
- find = true
- }
- }
- if !find {
- addedKeys = append(addedKeys, newKey)
- }
- }
- for _, key := range sameKeys {
- newPrefixField := formatFieldByDot(prefixField, key.String())
- err := db.diffLoop(old.MapIndex(key), now.MapIndex(key), newPrefixField)
- if err != nil {
- return err
- }
- }
- for _, key := range addedKeys {
- newPrefixField := formatFieldByDot(prefixField, key.String())
- db.diffs = append(db.diffs, Diff{Field: newPrefixField, Old: "", Now: fmt.Sprintf("%+v", now.MapIndex(key).Interface())})
- }
- for _, key := range deletedKeys {
- newPrefixField := formatFieldByDot(prefixField, key.String())
- db.diffs = append(db.diffs, Diff{Field: newPrefixField, Old: fmt.Sprintf("%+v", old.MapIndex(key).Interface()), Now: ""})
- }
- default:
- if old.Interface() != now.Interface() {
- db.diffs = append(db.diffs, Diff{Field: prefixField, Old: fmt.Sprintf("%v", old.Interface()), Now: fmt.Sprintf("%v", now.Interface())})
- }
- }
- return nil
- }
- func formatFieldByDot(prefix string, suffix string) string {
- if len(prefix) == 0 {
- return suffix
- }
- if len(suffix) == 0 {
- return prefix
- }
- return prefix + "." + suffix
- }
|