Skip to content

Commit

Permalink
Add ability to adjust merging behavior of configs per fields defined …
Browse files Browse the repository at this point in the history
…in configurations (#152)

* First pass at adding the ability to adjust the merging of configuration per field.

* Fix array override to be nested into array not above it.

* Add support for unknown depth selector, add changelog entry.

* Add extra tests that ensures that the unknown depth selector works even when the fields are at level 0 depth.

* Use Config as a precomputed tree of how to perform merging. Add support for referencing entries in arrays by index.

* Remove unneeded if replace branch in mergeArr path. Add optimizations to only allocation options when absolutely required.

* Add fieldHandleTree API on top of Config.

* Emtpy commit for github to notice.

* Update changelog.
  • Loading branch information
blakerouse authored Feb 20, 2020
1 parent ab69586 commit 5f065f8
Show file tree
Hide file tree
Showing 4 changed files with 673 additions and 2 deletions.
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

0 comments on commit 5f065f8

Please sign in to comment.