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

Add ability to adjust merging behavior of configs per fields defined in configurations #152

Merged
merged 9 commits into from
Feb 20, 2020
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]

### Added
- Added ability to adjust merging behavior based on field names in configuration. Using `ucfg.FieldMergeValues`, `ucfg.FieldReplaceValues`, `ucfg.FieldAppendValues`, and `ucfg.FieldPrependValues`. #151

### Changed

Expand Down
72 changes: 70 additions & 2 deletions merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ func mergeConfigDict(opts *options, to, from *Config) Error {
}

old, _ := to.fields.get(k)
opts, err := fieldOptsOverride(opts, k, -1)
if err != nil {
return err
}
merged, err := mergeValues(opts, old, v)
if err != nil {
return err
Expand All @@ -128,7 +132,12 @@ func mergeConfigDict(opts *options, to, from *Config) Error {
}

func mergeConfigArr(opts *options, to, from *Config) Error {
switch opts.configValueHandling {
currHandling := opts.configValueHandling
opts, err := fieldOptsOverride(opts, "*", -1)
if err != nil {
return err
}
switch currHandling {
case cfgReplaceValue:
return mergeConfigReplaceArr(opts, to, from)

Expand Down Expand Up @@ -177,8 +186,13 @@ func mergeConfigMergeArr(opts *options, to, from *Config) Error {
field: fmt.Sprintf("%v", i),
}

// possible for individual index to be replaced
idxOpts, err := fieldOptsOverride(opts, "", i)
if err != nil {
return err
}
old := to.fields.array()[i]
merged, err := mergeValues(opts, old, arr[i])
merged, err := mergeValues(idxOpts, old, arr[i])
if err != nil {
return err
}
Expand Down Expand Up @@ -515,3 +529,57 @@ func normalizeString(ctx context, opts *options, str string) (value, Error) {

return newSplice(ctx, opts.meta, varexp), nil
}

func fieldOptsOverride(opts *options, fieldName string, idx int) (*options, Error) {
if opts.fieldHandlingTree == nil {
return opts, nil
}
cfgHandling, child, ok := opts.fieldHandlingTree.fieldHandling(fieldName, idx)
child, err := includeWildcard(child, opts.fieldHandlingTree)
if err != nil {
return nil, err
}
if !ok {
// Only return a new `options` when arriving at new nested child. This
// combined with optimizations in `includeWildcard` will ensure that only
// a new opts will be created and returned when absolutely required.
if child != nil && opts.fieldHandlingTree != child {
newOpts := *opts
newOpts.fieldHandlingTree = child
opts = &newOpts
}
return opts, nil
}
// Only return a new `options` if absolutely required.
if opts.configValueHandling != cfgHandling || opts.fieldHandlingTree != child {
newOpts := *opts
newOpts.configValueHandling = cfgHandling
newOpts.fieldHandlingTree = child
opts = &newOpts
}
return opts, nil
}

func includeWildcard(child *fieldHandlingTree, parent *fieldHandlingTree) (*fieldHandlingTree, Error) {
if parent == nil {
return child, nil
}
wildcard, err := parent.wildcard()
if err != nil {
return child, nil
}
if child == nil && len(parent.fields.dict()) == 1 {
// parent is already config with just wildcard
return parent, nil
}
sub := newFieldHandlingTree()
if child != nil {
if err := sub.merge(child); err != nil {
return nil, err.(Error)
}
}
if err := sub.setWildcard(wildcard); err != nil {
return nil, err.(Error)
}
return sub, nil
}
Loading