map.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // Copyright 2014 Dario Castañé. All rights reserved.
  2. // Copyright 2009 The Go Authors. All rights reserved.
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file.
  5. // Based on src/pkg/reflect/deepequal.go from official
  6. // golang's stdlib.
  7. package mergo
  8. import (
  9. "fmt"
  10. "reflect"
  11. "unicode"
  12. "unicode/utf8"
  13. )
  14. func changeInitialCase(s string, mapper func(rune) rune) string {
  15. if s == "" {
  16. return s
  17. }
  18. r, n := utf8.DecodeRuneInString(s)
  19. return string(mapper(r)) + s[n:]
  20. }
  21. func isExported(field reflect.StructField) bool {
  22. r, _ := utf8.DecodeRuneInString(field.Name)
  23. return r >= 'A' && r <= 'Z'
  24. }
  25. // Traverses recursively both values, assigning src's fields values to dst.
  26. // The map argument tracks comparisons that have already been seen, which allows
  27. // short circuiting on recursive types.
  28. func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
  29. overwrite := config.Overwrite
  30. if dst.CanAddr() {
  31. addr := dst.UnsafeAddr()
  32. h := 17 * addr
  33. seen := visited[h]
  34. typ := dst.Type()
  35. for p := seen; p != nil; p = p.next {
  36. if p.ptr == addr && p.typ == typ {
  37. return nil
  38. }
  39. }
  40. // Remember, remember...
  41. visited[h] = &visit{addr, typ, seen}
  42. }
  43. zeroValue := reflect.Value{}
  44. switch dst.Kind() {
  45. case reflect.Map:
  46. dstMap := dst.Interface().(map[string]interface{})
  47. for i, n := 0, src.NumField(); i < n; i++ {
  48. srcType := src.Type()
  49. field := srcType.Field(i)
  50. if !isExported(field) {
  51. continue
  52. }
  53. fieldName := field.Name
  54. fieldName = changeInitialCase(fieldName, unicode.ToLower)
  55. if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v)) || overwrite) {
  56. dstMap[fieldName] = src.Field(i).Interface()
  57. }
  58. }
  59. case reflect.Ptr:
  60. if dst.IsNil() {
  61. v := reflect.New(dst.Type().Elem())
  62. dst.Set(v)
  63. }
  64. dst = dst.Elem()
  65. fallthrough
  66. case reflect.Struct:
  67. srcMap := src.Interface().(map[string]interface{})
  68. for key := range srcMap {
  69. srcValue := srcMap[key]
  70. fieldName := changeInitialCase(key, unicode.ToUpper)
  71. dstElement := dst.FieldByName(fieldName)
  72. if dstElement == zeroValue {
  73. // We discard it because the field doesn't exist.
  74. continue
  75. }
  76. srcElement := reflect.ValueOf(srcValue)
  77. dstKind := dstElement.Kind()
  78. srcKind := srcElement.Kind()
  79. if srcKind == reflect.Ptr && dstKind != reflect.Ptr {
  80. srcElement = srcElement.Elem()
  81. srcKind = reflect.TypeOf(srcElement.Interface()).Kind()
  82. } else if dstKind == reflect.Ptr {
  83. // Can this work? I guess it can't.
  84. if srcKind != reflect.Ptr && srcElement.CanAddr() {
  85. srcPtr := srcElement.Addr()
  86. srcElement = reflect.ValueOf(srcPtr)
  87. srcKind = reflect.Ptr
  88. }
  89. }
  90. if !srcElement.IsValid() {
  91. continue
  92. }
  93. if srcKind == dstKind {
  94. if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
  95. return
  96. }
  97. } else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface {
  98. if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
  99. return
  100. }
  101. } else if srcKind == reflect.Map {
  102. if err = deepMap(dstElement, srcElement, visited, depth+1, config); err != nil {
  103. return
  104. }
  105. } else {
  106. return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind)
  107. }
  108. }
  109. }
  110. return
  111. }
  112. // Map sets fields' values in dst from src.
  113. // src can be a map with string keys or a struct. dst must be the opposite:
  114. // if src is a map, dst must be a valid pointer to struct. If src is a struct,
  115. // dst must be map[string]interface{}.
  116. // It won't merge unexported (private) fields and will do recursively
  117. // any exported field.
  118. // If dst is a map, keys will be src fields' names in lower camel case.
  119. // Missing key in src that doesn't match a field in dst will be skipped. This
  120. // doesn't apply if dst is a map.
  121. // This is separated method from Merge because it is cleaner and it keeps sane
  122. // semantics: merging equal types, mapping different (restricted) types.
  123. func Map(dst, src interface{}, opts ...func(*Config)) error {
  124. return _map(dst, src, opts...)
  125. }
  126. // MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overridden by
  127. // non-empty src attribute values.
  128. // Deprecated: Use Map(…) with WithOverride
  129. func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
  130. return _map(dst, src, append(opts, WithOverride)...)
  131. }
  132. func _map(dst, src interface{}, opts ...func(*Config)) error {
  133. var (
  134. vDst, vSrc reflect.Value
  135. err error
  136. )
  137. config := &Config{}
  138. for _, opt := range opts {
  139. opt(config)
  140. }
  141. if vDst, vSrc, err = resolveValues(dst, src); err != nil {
  142. return err
  143. }
  144. // To be friction-less, we redirect equal-type arguments
  145. // to deepMerge. Only because arguments can be anything.
  146. if vSrc.Kind() == vDst.Kind() {
  147. return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
  148. }
  149. switch vSrc.Kind() {
  150. case reflect.Struct:
  151. if vDst.Kind() != reflect.Map {
  152. return ErrExpectedMapAsDestination
  153. }
  154. case reflect.Map:
  155. if vDst.Kind() != reflect.Struct {
  156. return ErrExpectedStructAsDestination
  157. }
  158. default:
  159. return ErrNotSupported
  160. }
  161. return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, config)
  162. }