// 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) (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); err != nil { return } } case reflect.Map: for _, key := range src.MapKeys() { srcElement := src.MapIndex(key) if !srcElement.IsValid() { continue } dstElement := dst.MapIndex(key) switch reflect.TypeOf(srcElement.Interface()).Kind() { case reflect.Struct: fallthrough case reflect.Map: if err = deepMerge(dstElement, srcElement, visited, depth+1); err != nil { return } if !dstElement.IsValid() { dst.SetMapIndex(key, srcElement) } default: dst.SetMapIndex(key, srcElement) } } case reflect.Ptr: fallthrough case reflect.Interface: if src.IsNil() { break } else if dst.IsNil() { if dst.CanSet() && isEmptyValue(dst) { dst.Set(src) } } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1); err != nil { return } default: if dst.CanSet() && !isEmptyValue(src) { 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 { 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) }