123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- // 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"
- )
- // 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, overwrite bool) (err error) {
- 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}
- }
- switch dst.Kind() {
- case reflect.Struct:
- for i, n := 0, dst.NumField(); i < n; i++ {
- if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, overwrite); err != nil {
- return
- }
- }
- case reflect.Map:
- 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.Ptr, reflect.Interface, reflect.Slice:
- if srcElement.IsNil() {
- continue
- }
- fallthrough
- default:
- switch reflect.TypeOf(srcElement.Interface()).Kind() {
- case reflect.Struct:
- fallthrough
- case reflect.Ptr:
- fallthrough
- case reflect.Map:
- if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
- return
- }
- }
- }
- if !isEmptyValue(srcElement) && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) {
- if dst.IsNil() {
- dst.Set(reflect.MakeMap(dst.Type()))
- }
- dst.SetMapIndex(key, srcElement)
- }
- }
- case reflect.Ptr:
- fallthrough
- case reflect.Interface:
- if src.IsNil() {
- break
- } else if dst.IsNil() {
- if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
- dst.Set(src)
- }
- } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, overwrite); err != nil {
- return
- }
- default:
- if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) {
- dst.Set(src)
- }
- }
- return
- }
- // Merge sets fields' values in dst from src if they have a zero
- // value of their type.
- // 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{}) error {
- return merge(dst, src, false)
- }
- func MergeWithOverwrite(dst, src interface{}) error {
- return merge(dst, src, true)
- }
- func merge(dst, src interface{}, overwrite bool) error {
- var (
- vDst, vSrc reflect.Value
- err error
- )
- 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, overwrite)
- }
|