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