1// Copyright 2013 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 6// Based on src/pkg/reflect/deepequal.go from official 7// golang's stdlib. 8 9package mergo 10 11import ( 12 "fmt" 13 "reflect" 14) 15 16func hasExportedField(dst reflect.Value) (exported bool) { 17 for i, n := 0, dst.NumField(); i < n; i++ { 18 field := dst.Type().Field(i) 19 if field.Anonymous && dst.Field(i).Kind() == reflect.Struct { 20 exported = exported || hasExportedField(dst.Field(i)) 21 } else { 22 exported = exported || len(field.PkgPath) == 0 23 } 24 } 25 return 26} 27 28type Config struct { 29 Overwrite bool 30 AppendSlice bool 31 Transformers Transformers 32} 33 34type Transformers interface { 35 Transformer(reflect.Type) func(dst, src reflect.Value) error 36} 37 38// Traverses recursively both values, assigning src's fields values to dst. 39// The map argument tracks comparisons that have already been seen, which allows 40// short circuiting on recursive types. 41func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) { 42 overwrite := config.Overwrite 43 44 if !src.IsValid() { 45 return 46 } 47 if dst.CanAddr() { 48 addr := dst.UnsafeAddr() 49 h := 17 * addr 50 seen := visited[h] 51 typ := dst.Type() 52 for p := seen; p != nil; p = p.next { 53 if p.ptr == addr && p.typ == typ { 54 return nil 55 } 56 } 57 // Remember, remember... 58 visited[h] = &visit{addr, typ, seen} 59 } 60 61 if config.Transformers != nil && !isEmptyValue(dst) { 62 if fn := config.Transformers.Transformer(dst.Type()); fn != nil { 63 err = fn(dst, src) 64 return 65 } 66 } 67 68 switch dst.Kind() { 69 case reflect.Struct: 70 if hasExportedField(dst) { 71 for i, n := 0, dst.NumField(); i < n; i++ { 72 if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil { 73 return 74 } 75 } 76 } else { 77 if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) { 78 dst.Set(src) 79 } 80 } 81 case reflect.Map: 82 if dst.IsNil() && !src.IsNil() { 83 dst.Set(reflect.MakeMap(dst.Type())) 84 } 85 for _, key := range src.MapKeys() { 86 srcElement := src.MapIndex(key) 87 if !srcElement.IsValid() { 88 continue 89 } 90 dstElement := dst.MapIndex(key) 91 switch srcElement.Kind() { 92 case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice: 93 if srcElement.IsNil() { 94 continue 95 } 96 fallthrough 97 default: 98 if !srcElement.CanInterface() { 99 continue 100 } 101 switch reflect.TypeOf(srcElement.Interface()).Kind() { 102 case reflect.Struct: 103 fallthrough 104 case reflect.Ptr: 105 fallthrough 106 case reflect.Map: 107 srcMapElm := srcElement 108 dstMapElm := dstElement 109 if srcMapElm.CanInterface() { 110 srcMapElm = reflect.ValueOf(srcMapElm.Interface()) 111 if dstMapElm.IsValid() { 112 dstMapElm = reflect.ValueOf(dstMapElm.Interface()) 113 } 114 } 115 if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil { 116 return 117 } 118 case reflect.Slice: 119 srcSlice := reflect.ValueOf(srcElement.Interface()) 120 121 var dstSlice reflect.Value 122 if !dstElement.IsValid() || dstElement.IsNil() { 123 dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len()) 124 } else { 125 dstSlice = reflect.ValueOf(dstElement.Interface()) 126 } 127 128 if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { 129 dstSlice = srcSlice 130 } else if config.AppendSlice { 131 if srcSlice.Type() != dstSlice.Type() { 132 return fmt.Errorf("cannot append two slice with different type (%s, %s)", srcSlice.Type(), dstSlice.Type()) 133 } 134 dstSlice = reflect.AppendSlice(dstSlice, srcSlice) 135 } 136 dst.SetMapIndex(key, dstSlice) 137 } 138 } 139 if dstElement.IsValid() && reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map { 140 continue 141 } 142 143 if srcElement.IsValid() && (overwrite || (!dstElement.IsValid() || isEmptyValue(dstElement))) { 144 if dst.IsNil() { 145 dst.Set(reflect.MakeMap(dst.Type())) 146 } 147 dst.SetMapIndex(key, srcElement) 148 } 149 } 150 case reflect.Slice: 151 if !dst.CanSet() { 152 break 153 } 154 if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { 155 dst.Set(src) 156 } else if config.AppendSlice { 157 if src.Type() != dst.Type() { 158 return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type()) 159 } 160 dst.Set(reflect.AppendSlice(dst, src)) 161 } 162 case reflect.Ptr: 163 fallthrough 164 case reflect.Interface: 165 if src.IsNil() { 166 break 167 } 168 if src.Kind() != reflect.Interface { 169 if dst.IsNil() || overwrite { 170 if dst.CanSet() && (overwrite || isEmptyValue(dst)) { 171 dst.Set(src) 172 } 173 } else if src.Kind() == reflect.Ptr { 174 if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { 175 return 176 } 177 } else if dst.Elem().Type() == src.Type() { 178 if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil { 179 return 180 } 181 } else { 182 return ErrDifferentArgumentsTypes 183 } 184 break 185 } 186 if dst.IsNil() || overwrite { 187 if dst.CanSet() && (overwrite || isEmptyValue(dst)) { 188 dst.Set(src) 189 } 190 } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { 191 return 192 } 193 default: 194 if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) { 195 dst.Set(src) 196 } 197 } 198 return 199} 200 201// Merge will fill any empty for value type attributes on the dst struct using corresponding 202// src attributes if they themselves are not empty. dst and src must be valid same-type structs 203// and dst must be a pointer to struct. 204// It won't merge unexported (private) fields and will do recursively any exported field. 205func Merge(dst, src interface{}, opts ...func(*Config)) error { 206 return merge(dst, src, opts...) 207} 208 209// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by 210// non-empty src attribute values. 211// Deprecated: use Merge(…) with WithOverride 212func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error { 213 return merge(dst, src, append(opts, WithOverride)...) 214} 215 216// WithTransformers adds transformers to merge, allowing to customize the merging of some types. 217func WithTransformers(transformers Transformers) func(*Config) { 218 return func(config *Config) { 219 config.Transformers = transformers 220 } 221} 222 223// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values. 224func WithOverride(config *Config) { 225 config.Overwrite = true 226} 227 228// WithAppendSlice will make merge append slices instead of overwriting it 229func WithAppendSlice(config *Config) { 230 config.AppendSlice = true 231} 232 233func merge(dst, src interface{}, opts ...func(*Config)) error { 234 var ( 235 vDst, vSrc reflect.Value 236 err error 237 ) 238 239 config := &Config{} 240 241 for _, opt := range opts { 242 opt(config) 243 } 244 245 if vDst, vSrc, err = resolveValues(dst, src); err != nil { 246 return err 247 } 248 if vDst.Type() != vSrc.Type() { 249 return ErrDifferentArgumentsTypes 250 } 251 return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config) 252} 253