package common import ( "errors" "reflect" "unsafe" ) const ( mergeAllFields = true mergeExportedFields = false ) // used to avoid zero value risk type EnableType int const ( Unset EnableType = 0 Enabled EnableType = 1 Disabled EnableType = 2 ) func (e EnableType) IsSet() bool { return e != Unset } func (e EnableType) Enabled() bool { return e == Enabled } func (e EnableType) Disabled() bool { return e == Disabled } // dst and src need addressable func MergeStructAllFields(dst, src interface{}) error { return mergeStruct(dst, src, mergeAllFields) } // dst need addressable func MergeStructExportedFields(dst, src interface{}) error { return mergeStruct(dst, src, mergeExportedFields) } func mergeStruct(dst, src interface{}, isAll bool) (err error) { var vDst, vSrc reflect.Value if vDst, vSrc, err = reflectValue(dst, src); err != nil { return err } if vDst.Type() != vSrc.Type() { return errors.New("src and dst must be of same type") } return deepMergeStruct(vDst, vSrc, isAll) } func deepMergeStruct(dst, src reflect.Value, isAll bool) (err error) { if !src.IsValid() { return errors.New("src is invalid") } if dst.Kind() == reflect.Struct { for i, n := 0, dst.NumField(); i < n; i++ { setNotZeroValue(dst.Field(i), src.Field(i), isAll) } } return } func reflectValue(dst, src interface{}) (vDst, vSrc reflect.Value, err error) { if dst == nil || src == nil { err = errors.New("src and dst must not be nil") return } vDst = reflect.ValueOf(dst).Elem() if vDst.Kind() != reflect.Struct { err = errors.New("only structs are supported") return } vSrc = reflect.ValueOf(src) if vSrc.Kind() == reflect.Ptr { vSrc = vSrc.Elem() } return } func setNotZeroValue(dst, src reflect.Value, isAll bool) { if src.IsZero() { return } if dst.CanSet() { dst.Set(src) } else if isAll { dst = reflect.NewAt(dst.Type(), unsafe.Pointer(dst.UnsafeAddr())).Elem() src = reflect.NewAt(src.Type(), unsafe.Pointer(src.UnsafeAddr())).Elem() dst.Set(src) } }