123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- // Copyright 2013 Dario Castañé. All rights reserved.
- // Copyright 2009 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // Based on src/pkg/reflect/deepequal.go from official
- // golang's stdlib.
- package mergo
- import (
- "reflect"
- )
- func hasExportedField(dst reflect.Value) (exported bool) {
- for i, n := 0, dst.NumField(); i < n; i++ {
- field := dst.Type().Field(i)
- if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
- exported = exported || hasExportedField(dst.Field(i))
- } else {
- exported = exported || len(field.PkgPath) == 0
- }
- }
- return
- }
- type Config struct {
- Overwrite bool
- AppendSlice bool
- Transformers Transformers
- }
- type Transformers interface {
- Transformer(reflect.Type) func(dst, src reflect.Value) error
- }
- // Traverses recursively both values, assigning src's fields values to dst.
- // The map argument tracks comparisons that have already been seen, which allows
- // short circuiting on recursive types.
- func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
- overwrite := config.Overwrite
- if !src.IsValid() {
- return
- }
- if dst.CanAddr() {
- addr := dst.UnsafeAddr()
- h := 17 * addr
- seen := visited[h]
- typ := dst.Type()
- for p := seen; p != nil; p = p.next {
- if p.ptr == addr && p.typ == typ {
- return nil
- }
- }
- // Remember, remember...
- visited[h] = &visit{addr, typ, seen}
- }
- if config.Transformers != nil && !isEmptyValue(dst) {
- if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
- err = fn(dst, src)
- return
- }
- }
- switch dst.Kind() {
- case reflect.Struct:
- if hasExportedField(dst) {
- for i, n := 0, dst.NumField(); i < n; i++ {
- if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
- return
- }
- }
- } else {
- if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) {
- dst.Set(src)
- }
- }
- case reflect.Map:
- if dst.IsNil() && !src.IsNil() {
- dst.Set(reflect.MakeMap(dst.Type()))
- }
- for _, key := range src.MapKeys() {
- srcElement := src.MapIndex(key)
- if !srcElement.IsValid() {
- continue
- }
- dstElement := dst.MapIndex(key)
- switch srcElement.Kind() {
- case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
- if srcElement.IsNil() {
- continue
- }
- fallthrough
- default:
- if !srcElement.CanInterface() {
- continue
- }
- switch reflect.TypeOf(srcElement.Interface()).Kind() {
- case reflect.Struct:
- fallthrough
- case reflect.Ptr:
- fallthrough
- case reflect.Map:
- srcMapElm := srcElement
- dstMapElm := dstElement
- if srcMapElm.CanInterface() {
- srcMapElm = reflect.ValueOf(srcMapElm.Interface())
- if dstMapElm.IsValid() {
- dstMapElm = reflect.ValueOf(dstMapElm.Interface())
- }
- }
- if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil {
- return
- }
- case reflect.Slice:
- srcSlice := reflect.ValueOf(srcElement.Interface())
- var dstSlice reflect.Value
- if !dstElement.IsValid() || dstElement.IsNil() {
- dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
- } else {
- dstSlice = reflect.ValueOf(dstElement.Interface())
- }
- if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
- dstSlice = srcSlice
- } else if config.AppendSlice {
- dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
- }
- dst.SetMapIndex(key, dstSlice)
- }
- }
- if dstElement.IsValid() && reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map {
- continue
- }
- if srcElement.IsValid() && (overwrite || (!dstElement.IsValid() || isEmptyValue(dstElement))) {
- if dst.IsNil() {
- dst.Set(reflect.MakeMap(dst.Type()))
- }
- dst.SetMapIndex(key, srcElement)
- }
- }
- case reflect.Slice:
- if !dst.CanSet() {
- break
- }
- if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
- dst.Set(src)
- } else if config.AppendSlice {
- dst.Set(reflect.AppendSlice(dst, src))
- }
- case reflect.Ptr:
- fallthrough
- case reflect.Interface:
- if src.IsNil() {
- break
- }
- if src.Kind() != reflect.Interface {
- if dst.IsNil() || overwrite {
- if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
- dst.Set(src)
- }
- } else if src.Kind() == reflect.Ptr {
- if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
- return
- }
- } else if dst.Elem().Type() == src.Type() {
- if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
- return
- }
- } else {
- return ErrDifferentArgumentsTypes
- }
- break
- }
- if dst.IsNil() || overwrite {
- if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
- dst.Set(src)
- }
- } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
- return
- }
- default:
- if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) {
- dst.Set(src)
- }
- }
- return
- }
- // Merge will fill any empty for value type attributes on the dst struct using corresponding
- // src attributes if they themselves are not empty. dst and src must be valid same-type structs
- // and dst must be a pointer to struct.
- // It won't merge unexported (private) fields and will do recursively any exported field.
- func Merge(dst, src interface{}, opts ...func(*Config)) error {
- return merge(dst, src, opts...)
- }
- // MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by
- // non-empty src attribute values.
- // Deprecated: use Merge(…) with WithOverride
- func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
- return merge(dst, src, append(opts, WithOverride)...)
- }
- // WithTransformers adds transformers to merge, allowing to customize the merging of some types.
- func WithTransformers(transformers Transformers) func(*Config) {
- return func(config *Config) {
- config.Transformers = transformers
- }
- }
- // WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
- func WithOverride(config *Config) {
- config.Overwrite = true
- }
- // WithAppendSlice will make merge append slices instead of overwriting it
- func WithAppendSlice(config *Config) {
- config.AppendSlice = true
- }
- func merge(dst, src interface{}, opts ...func(*Config)) error {
- var (
- vDst, vSrc reflect.Value
- err error
- )
- config := &Config{}
- for _, opt := range opts {
- opt(config)
- }
- if vDst, vSrc, err = resolveValues(dst, src); err != nil {
- return err
- }
- if vDst.Type() != vSrc.Type() {
- return ErrDifferentArgumentsTypes
- }
- return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
- }
|