Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: conf inherit #2568

Merged
merged 11 commits into from Nov 8, 2022
5 changes: 5 additions & 0 deletions core/mapping/fieldoptions.go
Expand Up @@ -8,6 +8,7 @@ type (
// use context and OptionalDep option to determine the value of Optional
// nothing to do with context.Context
fieldOptionsWithContext struct {
Inherit bool
FromString bool
Optional bool
Options []string
Expand Down Expand Up @@ -40,6 +41,10 @@ func (o *fieldOptionsWithContext) getDefault() (string, bool) {
return o.Default, len(o.Default) > 0
}

func (o *fieldOptionsWithContext) inherit() bool {
return o != nil && o.Inherit
}

func (o *fieldOptionsWithContext) optional() bool {
return o != nil && o.Optional
}
Expand Down
124 changes: 82 additions & 42 deletions core/mapping/unmarshaler.go
Expand Up @@ -70,15 +70,15 @@ func UnmarshalKey(m map[string]interface{}, v interface{}) error {

// Unmarshal unmarshals m into v.
func (u *Unmarshaler) Unmarshal(m map[string]interface{}, v interface{}) error {
return u.UnmarshalValuer(MapValuer(m), v)
return u.UnmarshalValuer(mapValuer(m), v)
}

// UnmarshalValuer unmarshals m into v.
func (u *Unmarshaler) UnmarshalValuer(m Valuer, v interface{}) error {
return u.unmarshalWithFullName(m, v, "")
return u.unmarshalWithFullName(simpleValuer{current: m}, v, "")
}

func (u *Unmarshaler) unmarshalWithFullName(m Valuer, v interface{}, fullName string) error {
func (u *Unmarshaler) unmarshalWithFullName(m ValuerWithParent, v interface{}, fullName string) error {
rv := reflect.ValueOf(v)
if err := ValidatePtr(&rv); err != nil {
return err
Expand All @@ -102,7 +102,7 @@ func (u *Unmarshaler) unmarshalWithFullName(m Valuer, v interface{}, fullName st
}

func (u *Unmarshaler) processAnonymousField(field reflect.StructField, value reflect.Value,
m Valuer, fullName string) error {
m ValuerWithParent, fullName string) error {
key, options, err := u.parseOptionsWithContext(field, m, fullName)
if err != nil {
return err
Expand All @@ -120,7 +120,7 @@ func (u *Unmarshaler) processAnonymousField(field reflect.StructField, value ref
}

func (u *Unmarshaler) processAnonymousFieldOptional(field reflect.StructField, value reflect.Value,
key string, m Valuer, fullName string) error {
key string, m ValuerWithParent, fullName string) error {
var filled bool
var required int
var requiredFilled int
Expand Down Expand Up @@ -161,7 +161,7 @@ func (u *Unmarshaler) processAnonymousFieldOptional(field reflect.StructField, v
}

func (u *Unmarshaler) processAnonymousFieldRequired(field reflect.StructField, value reflect.Value,
m Valuer, fullName string) error {
m ValuerWithParent, fullName string) error {
maybeNewValue(field, value)
fieldType := Deref(field.Type)
indirectValue := reflect.Indirect(value)
Expand All @@ -175,8 +175,8 @@ func (u *Unmarshaler) processAnonymousFieldRequired(field reflect.StructField, v
return nil
}

func (u *Unmarshaler) processField(field reflect.StructField, value reflect.Value, m Valuer,
fullName string) error {
func (u *Unmarshaler) processField(field reflect.StructField, value reflect.Value,
m ValuerWithParent, fullName string) error {
if usingDifferentKeys(u.key, field) {
return nil
}
Expand All @@ -189,15 +189,23 @@ func (u *Unmarshaler) processField(field reflect.StructField, value reflect.Valu
}

func (u *Unmarshaler) processFieldNotFromString(field reflect.StructField, value reflect.Value,
mapValue interface{}, opts *fieldOptionsWithContext, fullName string) error {
vp valueWithParent, opts *fieldOptionsWithContext, fullName string) error {
fieldType := field.Type
derefedFieldType := Deref(fieldType)
typeKind := derefedFieldType.Kind()
valueKind := reflect.TypeOf(mapValue).Kind()
valueKind := reflect.TypeOf(vp.value).Kind()
mapValue := vp.value

switch {
case valueKind == reflect.Map && typeKind == reflect.Struct:
return u.processFieldStruct(field, value, mapValue, fullName)
if mv, ok := mapValue.(map[string]interface{}); ok {
return u.processFieldStruct(field, value, &simpleValuer{
current: mapValuer(mv),
parent: vp.parent,
}, fullName)
} else {
return errTypeMismatch
}
case valueKind == reflect.Map && typeKind == reflect.Map:
return u.fillMap(field, value, mapValue)
case valueKind == reflect.String && typeKind == reflect.Map:
Expand Down Expand Up @@ -292,18 +300,7 @@ func (u *Unmarshaler) processFieldPrimitiveWithJSONNumber(field reflect.StructFi
}

func (u *Unmarshaler) processFieldStruct(field reflect.StructField, value reflect.Value,
mapValue interface{}, fullName string) error {
convertedValue, ok := mapValue.(map[string]interface{})
if !ok {
valueKind := reflect.TypeOf(mapValue).Kind()
return fmt.Errorf("error: field: %s, expect map[string]interface{}, actual %v", fullName, valueKind)
}

return u.processFieldStructWithMap(field, value, MapValuer(convertedValue), fullName)
}

func (u *Unmarshaler) processFieldStructWithMap(field reflect.StructField, value reflect.Value,
m Valuer, fullName string) error {
m ValuerWithParent, fullName string) error {
if field.Type.Kind() == reflect.Ptr {
baseType := Deref(field.Type)
target := reflect.New(baseType).Elem()
Expand Down Expand Up @@ -342,7 +339,7 @@ func (u *Unmarshaler) processFieldTextUnmarshaler(field reflect.StructField, val
}

func (u *Unmarshaler) processNamedField(field reflect.StructField, value reflect.Value,
m Valuer, fullName string) error {
m ValuerWithParent, fullName string) error {
key, opts, err := u.parseOptionsWithContext(field, m, fullName)
if err != nil {
return err
Expand All @@ -353,16 +350,29 @@ func (u *Unmarshaler) processNamedField(field reflect.StructField, value reflect
if u.opts.canonicalKey != nil {
canonicalKey = u.opts.canonicalKey(key)
}
mapValue, hasValue := getValue(m, canonicalKey)
if hasValue {
return u.processNamedFieldWithValue(field, value, mapValue, key, opts, fullName)

valuer := createValuer(m, opts)
mapValue, hasValue := getValue(valuer, canonicalKey)
if !hasValue {
return u.processNamedFieldWithoutValue(field, value, opts, fullName)
}

if field.Type.Kind() == reflect.Struct {
return u.processNamedFieldWithValue(field, value, valueWithParent{
value: mapValue,
parent: valuer,
}, key, opts, fullName)
}

return u.processNamedFieldWithoutValue(field, value, opts, fullName)
return u.processNamedFieldWithValue(field, value, valueWithParent{
value: mapValue,
parent: valuer,
}, key, opts, fullName)
}
kevwan marked this conversation as resolved.
Show resolved Hide resolved

func (u *Unmarshaler) processNamedFieldWithValue(field reflect.StructField, value reflect.Value,
mapValue interface{}, key string, opts *fieldOptionsWithContext, fullName string) error {
vp valueWithParent, key string, opts *fieldOptionsWithContext, fullName string) error {
mapValue := vp.value
if mapValue == nil {
if opts.optional() {
return nil
Expand All @@ -384,7 +394,10 @@ func (u *Unmarshaler) processNamedFieldWithValue(field reflect.StructField, valu
fieldKind := Deref(field.Type).Kind()
switch fieldKind {
case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct:
return u.processFieldNotFromString(field, value, mapValue, opts, fullName)
return u.processFieldNotFromString(field, value, valueWithParent{
value: mapValue,
parent: vp.parent,
}, opts, fullName)
kevwan marked this conversation as resolved.
Show resolved Hide resolved
default:
if u.opts.fromString || opts.fromString() {
valueKind := reflect.TypeOf(mapValue).Kind()
Expand All @@ -403,7 +416,10 @@ func (u *Unmarshaler) processNamedFieldWithValue(field reflect.StructField, valu
return fillPrimitive(field.Type, value, mapValue, opts, fullName)
}

return u.processFieldNotFromString(field, value, mapValue, opts, fullName)
return u.processFieldNotFromString(field, value, valueWithParent{
value: mapValue,
parent: vp.parent,
}, opts, fullName)
kevwan marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -431,18 +447,24 @@ func (u *Unmarshaler) processNamedFieldWithoutValue(field reflect.StructField, v
switch fieldKind {
case reflect.Array, reflect.Map, reflect.Slice:
if !opts.optional() {
return u.processFieldNotFromString(field, value, emptyMap, opts, fullName)
return u.processFieldNotFromString(field, value, valueWithParent{
value: emptyMap,
}, opts, fullName)
}
case reflect.Struct:
if !opts.optional() {
required, err := structValueRequired(u.key, derefedType)
if err != nil {
return err
}

if required {
return fmt.Errorf("%q is not set", fullName)
}
return u.processFieldNotFromString(field, value, emptyMap, opts, fullName)

return u.processFieldNotFromString(field, value, valueWithParent{
value: emptyMap,
}, opts, fullName)
}
default:
if !opts.optional() {
Expand Down Expand Up @@ -738,6 +760,20 @@ func WithCanonicalKeyFunc(f func(string) string) UnmarshalOption {
}
}

func createValuer(v ValuerWithParent, opts *fieldOptionsWithContext) ValuerWithParent {
if opts.inherit() {
return recursiveValuer{
current: v,
parent: v.Parent(),
}
}

return simpleValuer{
current: v,
parent: v.Parent(),
}
}

func fillDurationValue(fieldKind reflect.Kind, value reflect.Value, dur string) error {
d, err := time.ParseDuration(dur)
if err != nil {
Expand Down Expand Up @@ -805,26 +841,30 @@ func fillWithSameType(field reflect.StructField, value reflect.Value, mapValue i
}

// getValue gets the value for the specific key, the key can be in the format of parentKey.childKey
func getValue(m Valuer, key string) (interface{}, bool) {
func getValue(m ValuerWithParent, key string) (interface{}, bool) {
keys := readKeys(key)
return getValueWithChainedKeys(m, keys)
}

func getValueWithChainedKeys(m Valuer, keys []string) (interface{}, bool) {
if len(keys) == 1 {
func getValueWithChainedKeys(m ValuerWithParent, keys []string) (interface{}, bool) {
switch len(keys) {
case 0:
return nil, false
case 1:
v, ok := m.Value(keys[0])
return v, ok
}

if len(keys) > 1 {
default:
if v, ok := m.Value(keys[0]); ok {
if nextm, ok := v.(map[string]interface{}); ok {
return getValueWithChainedKeys(MapValuer(nextm), keys[1:])
return getValueWithChainedKeys(recursiveValuer{
current: mapValuer(nextm),
parent: m,
}, keys[1:])
}
}
}

return nil, false
return nil, false
}
}

func join(elem ...string) string {
Expand Down