in reify.go [434:511]
func reifyMergeValue(
opts fieldOptions,
oldValue reflect.Value, val value,
) (reflect.Value, Error) {
old := chaseValueInterfaces(oldValue)
t := old.Type()
old = chaseValuePointers(old)
if (old.Kind() == reflect.Ptr || old.Kind() == reflect.Interface) && old.IsNil() {
return reifyValue(opts, t, val)
}
baseType := chaseTypePointers(old.Type())
if tConfig.ConvertibleTo(baseType) {
sub, err := val.toConfig(opts.opts)
if err != nil {
return reflect.Value{}, raiseExpectedObject(opts.opts, val)
}
if t == baseType {
// no pointer -> return type mismatch
return reflect.Value{}, raisePointerRequired(oldValue)
}
// check if old is nil -> copy reference only
if old.Kind() == reflect.Ptr && old.IsNil() {
v, err := val.reflect(opts.opts)
if err != nil {
ctx := val.Context()
return reflect.Value{}, raisePathErr(err, val.meta(), "", ctx.path("."))
}
v = v.Convert(reflect.PtrTo(baseType))
return pointerize(t, baseType, v), nil
}
// check if old == value
subOld := chaseValuePointers(old).Addr().Convert(tConfigPtr).Interface().(*Config)
if sub == subOld {
return oldValue, nil
}
// old != value -> merge value into old
return oldValue, mergeFieldConfig(opts, subOld, sub)
}
if v, ok := valueIsUnpacker(old); ok {
err := unpackWith(opts.opts, v, val)
if err != nil {
return reflect.Value{}, err
}
return old, nil
}
switch baseType.Kind() {
case reflect.Map:
sub, err := val.toConfig(opts.opts)
if err != nil {
return reflect.Value{}, raiseExpectedObject(opts.opts, val)
}
return old, reifyMap(opts.opts, old, sub, opts.validators)
case reflect.Struct:
sub, err := val.toConfig(opts.opts)
if err != nil {
return reflect.Value{}, raiseExpectedObject(opts.opts, val)
}
return oldValue, reifyStruct(opts.opts, old, sub)
case reflect.Array:
return reifyArray(opts, old, baseType, val)
case reflect.Slice:
return reifySliceMerge(opts, old, baseType, val)
}
return reifyPrimitive(opts, val, t, baseType)
}