2023-11-03 19:15:47 +00:00
|
|
|
package merge
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
2023-12-22 13:20:45 +00:00
|
|
|
"github.com/databricks/cli/libs/dyn"
|
2023-11-03 19:15:47 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Merge recursively merges the specified values.
|
|
|
|
//
|
|
|
|
// Semantics are as follows:
|
|
|
|
// * Merging x with nil or nil with x always yields x.
|
|
|
|
// * Merging maps a and b means entries from map b take precedence.
|
|
|
|
// * Merging sequences a and b means concatenating them.
|
2024-07-16 11:27:27 +00:00
|
|
|
//
|
|
|
|
// Merging retains and accumulates the locations metadata associated with the values.
|
|
|
|
// This allows users of the module to track the provenance of values across merging of
|
|
|
|
// configuration trees, which is useful for reporting errors and warnings.
|
|
|
|
//
|
|
|
|
// Semantics for location metadata in the merged value are similar to the semantics
|
|
|
|
// for the values themselves:
|
|
|
|
//
|
|
|
|
// - When merging x with nil or nil with x, the location of x is retained.
|
|
|
|
//
|
|
|
|
// - When merging maps or sequences, the combined value retains the location of a and
|
|
|
|
// accumulates the location of b. The individual elements of the map or sequence retain
|
|
|
|
// their original locations, i.e., whether they were originally defined in a or b.
|
|
|
|
//
|
|
|
|
// The rationale for retaining location of a is that we would like to return
|
|
|
|
// the first location a bit of configuration showed up when reporting errors and warnings.
|
|
|
|
//
|
|
|
|
// - Merging primitive values means using the incoming value `b`. The location of the
|
|
|
|
// incoming value is retained and the location of the existing value `a` is accumulated.
|
|
|
|
// This is because the incoming value overwrites the existing value.
|
2023-12-22 13:20:45 +00:00
|
|
|
func Merge(a, b dyn.Value) (dyn.Value, error) {
|
2023-11-03 19:15:47 +00:00
|
|
|
return merge(a, b)
|
|
|
|
}
|
|
|
|
|
2023-12-22 13:20:45 +00:00
|
|
|
func merge(a, b dyn.Value) (dyn.Value, error) {
|
2023-11-03 19:15:47 +00:00
|
|
|
ak := a.Kind()
|
|
|
|
bk := b.Kind()
|
|
|
|
|
|
|
|
// If a is nil, return b.
|
2023-12-22 13:20:45 +00:00
|
|
|
if ak == dyn.KindNil {
|
2024-07-16 11:27:27 +00:00
|
|
|
return b.AppendLocationsFromValue(a), nil
|
2023-11-03 19:15:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If b is nil, return a.
|
2023-12-22 13:20:45 +00:00
|
|
|
if bk == dyn.KindNil {
|
2024-07-16 11:27:27 +00:00
|
|
|
return a.AppendLocationsFromValue(b), nil
|
2023-11-03 19:15:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Call the appropriate merge function based on the kind of a and b.
|
|
|
|
switch ak {
|
2023-12-22 13:20:45 +00:00
|
|
|
case dyn.KindMap:
|
|
|
|
if bk != dyn.KindMap {
|
2024-06-21 14:22:42 +00:00
|
|
|
return dyn.InvalidValue, fmt.Errorf("cannot merge map with %s", bk)
|
2023-11-03 19:15:47 +00:00
|
|
|
}
|
|
|
|
return mergeMap(a, b)
|
2023-12-22 13:20:45 +00:00
|
|
|
case dyn.KindSequence:
|
|
|
|
if bk != dyn.KindSequence {
|
2024-06-21 14:22:42 +00:00
|
|
|
return dyn.InvalidValue, fmt.Errorf("cannot merge sequence with %s", bk)
|
2023-11-03 19:15:47 +00:00
|
|
|
}
|
|
|
|
return mergeSequence(a, b)
|
|
|
|
default:
|
|
|
|
if ak != bk {
|
2024-06-21 14:22:42 +00:00
|
|
|
return dyn.InvalidValue, fmt.Errorf("cannot merge %s with %s", ak, bk)
|
2023-11-03 19:15:47 +00:00
|
|
|
}
|
|
|
|
return mergePrimitive(a, b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-22 13:20:45 +00:00
|
|
|
func mergeMap(a, b dyn.Value) (dyn.Value, error) {
|
2024-03-25 11:01:09 +00:00
|
|
|
out := dyn.NewMapping()
|
2023-11-03 19:15:47 +00:00
|
|
|
am := a.MustMap()
|
|
|
|
bm := b.MustMap()
|
|
|
|
|
|
|
|
// Add the values from a into the output map.
|
2024-03-25 11:01:09 +00:00
|
|
|
out.Merge(am)
|
2023-11-03 19:15:47 +00:00
|
|
|
|
|
|
|
// Merge the values from b into the output map.
|
2024-03-25 11:01:09 +00:00
|
|
|
for _, pair := range bm.Pairs() {
|
|
|
|
pk := pair.Key
|
|
|
|
pv := pair.Value
|
|
|
|
if ov, ok := out.Get(pk); ok {
|
2023-11-03 19:15:47 +00:00
|
|
|
// If the key already exists, merge the values.
|
2024-03-25 11:01:09 +00:00
|
|
|
merged, err := merge(ov, pv)
|
2023-11-03 19:15:47 +00:00
|
|
|
if err != nil {
|
2024-06-21 14:22:42 +00:00
|
|
|
return dyn.InvalidValue, err
|
2023-11-03 19:15:47 +00:00
|
|
|
}
|
2024-03-25 11:01:09 +00:00
|
|
|
out.Set(pk, merged)
|
2023-11-03 19:15:47 +00:00
|
|
|
} else {
|
|
|
|
// Otherwise, just set the value.
|
2024-03-25 11:01:09 +00:00
|
|
|
out.Set(pk, pv)
|
2023-11-03 19:15:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-16 11:27:27 +00:00
|
|
|
// Preserve the location of the first value. Accumulate the locations of the second value.
|
|
|
|
return dyn.NewValue(out, a.Locations()).AppendLocationsFromValue(b), nil
|
2023-11-03 19:15:47 +00:00
|
|
|
}
|
|
|
|
|
2023-12-22 13:20:45 +00:00
|
|
|
func mergeSequence(a, b dyn.Value) (dyn.Value, error) {
|
2023-11-03 19:15:47 +00:00
|
|
|
as := a.MustSequence()
|
|
|
|
bs := b.MustSequence()
|
|
|
|
|
|
|
|
// Merging sequences means concatenating them.
|
2023-12-22 13:20:45 +00:00
|
|
|
out := make([]dyn.Value, len(as)+len(bs))
|
2023-11-03 19:15:47 +00:00
|
|
|
copy(out[:], as)
|
|
|
|
copy(out[len(as):], bs)
|
|
|
|
|
2024-07-16 11:27:27 +00:00
|
|
|
// Preserve the location of the first value. Accumulate the locations of the second value.
|
|
|
|
return dyn.NewValue(out, a.Locations()).AppendLocationsFromValue(b), nil
|
2023-11-03 19:15:47 +00:00
|
|
|
}
|
2023-12-22 13:20:45 +00:00
|
|
|
func mergePrimitive(a, b dyn.Value) (dyn.Value, error) {
|
2023-11-03 19:15:47 +00:00
|
|
|
// Merging primitive values means using the incoming value.
|
2024-07-16 11:27:27 +00:00
|
|
|
return b.AppendLocationsFromValue(a), nil
|
2023-11-03 19:15:47 +00:00
|
|
|
}
|