Skip to content

Commit

Permalink
feat: conf inherit (#2568)
Browse files Browse the repository at this point in the history
* feat: add ValuerWithParent

* feat: make etcd config inherit from parents

* chore: add more tests

* chore: add more tests

* chore: add more comments

* chore: refactor

* chore: add more comments

* fix: fix duplicated code and refactor

* fix: remove unnecessary code

* fix: fix test case for removing print

* feat: support partial inherit
  • Loading branch information
kevwan committed Nov 8, 2022
1 parent dac3600 commit 1677a4d
Show file tree
Hide file tree
Showing 7 changed files with 472 additions and 49 deletions.
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
111 changes: 69 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,22 @@ 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)
}

return u.processNamedFieldWithoutValue(field, value, opts, fullName)
return u.processNamedFieldWithValue(field, value, valueWithParent{
value: mapValue,
parent: valuer,
}, key, opts, fullName)
}

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 +387,7 @@ 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, vp, opts, fullName)
default:
if u.opts.fromString || opts.fromString() {
valueKind := reflect.TypeOf(mapValue).Kind()
Expand All @@ -403,7 +406,7 @@ 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, vp, opts, fullName)
}
}

Expand Down Expand Up @@ -431,18 +434,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 +747,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 +828,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

0 comments on commit 1677a4d

Please sign in to comment.