mirror of https://github.com/databricks/cli.git
Track multiple locations associated with a `dyn.Value` (#1510)
## Changes This PR changes the location metadata associated with a `dyn.Value` to a slice of locations. This will allow us to keep track of location metadata across merges and overrides. The convention is to treat the first location in the slice as the primary location. Also, the semantics are the same as before if there's only one location associated with a value, that is: 1. For complex values (maps, sequences) the location of the v1 is primary in Merge(v1, v2) 2. For primitive values the location of v2 is primary in Merge(v1, v2) ## Tests Modifying existing merge unit tests. Other existing unit tests and integration tests pass. --------- Co-authored-by: Pieter Noordhuis <pieter.noordhuis@databricks.com>
This commit is contained in:
parent
39c2633773
commit
8ed9964482
|
@ -22,7 +22,7 @@ func ConvertJobToValue(job *jobs.Job) (dyn.Value, error) {
|
||||||
tasks = append(tasks, v)
|
tasks = append(tasks, v)
|
||||||
}
|
}
|
||||||
// We're using location lines to define the order of keys in exported YAML.
|
// We're using location lines to define the order of keys in exported YAML.
|
||||||
value["tasks"] = dyn.NewValue(tasks, dyn.Location{Line: jobOrder.Get("tasks")})
|
value["tasks"] = dyn.NewValue(tasks, []dyn.Location{{Line: jobOrder.Get("tasks")}})
|
||||||
}
|
}
|
||||||
|
|
||||||
return yamlsaver.ConvertToMapValue(job.Settings, jobOrder, []string{"format", "new_cluster", "existing_cluster_id"}, value)
|
return yamlsaver.ConvertToMapValue(job.Settings, jobOrder, []string{"format", "new_cluster", "existing_cluster_id"}, value)
|
||||||
|
|
|
@ -59,7 +59,7 @@ func (m *expandPipelineGlobPaths) expandLibrary(v dyn.Value) ([]dyn.Value, error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
nv, err := dyn.SetByPath(v, p, dyn.NewValue(m, pv.Location()))
|
nv, err := dyn.SetByPath(v, p, dyn.NewValue(m, pv.Locations()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ func (m *expandPipelineGlobPaths) expandSequence(p dyn.Path, v dyn.Value) (dyn.V
|
||||||
vs = append(vs, v...)
|
vs = append(vs, v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(vs, v.Location()), nil
|
return dyn.NewValue(vs, v.Locations()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *expandPipelineGlobPaths) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics {
|
func (m *expandPipelineGlobPaths) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics {
|
||||||
|
|
|
@ -305,8 +305,8 @@ type createOverrideVisitorTestCase struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateOverrideVisitor(t *testing.T) {
|
func TestCreateOverrideVisitor(t *testing.T) {
|
||||||
left := dyn.NewValue(42, dyn.Location{})
|
left := dyn.V(42)
|
||||||
right := dyn.NewValue(1337, dyn.Location{})
|
right := dyn.V(1337)
|
||||||
|
|
||||||
testCases := []createOverrideVisitorTestCase{
|
testCases := []createOverrideVisitorTestCase{
|
||||||
{
|
{
|
||||||
|
@ -470,21 +470,21 @@ func TestCreateOverrideVisitor_omitempty(t *testing.T) {
|
||||||
// this is not happening, but adding for completeness
|
// this is not happening, but adding for completeness
|
||||||
name: "undo delete of empty variables",
|
name: "undo delete of empty variables",
|
||||||
path: dyn.MustPathFromString("variables"),
|
path: dyn.MustPathFromString("variables"),
|
||||||
left: dyn.NewValue([]dyn.Value{}, location),
|
left: dyn.NewValue([]dyn.Value{}, []dyn.Location{location}),
|
||||||
expectedErr: merge.ErrOverrideUndoDelete,
|
expectedErr: merge.ErrOverrideUndoDelete,
|
||||||
phases: allPhases,
|
phases: allPhases,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "undo delete of empty job clusters",
|
name: "undo delete of empty job clusters",
|
||||||
path: dyn.MustPathFromString("resources.jobs.job0.job_clusters"),
|
path: dyn.MustPathFromString("resources.jobs.job0.job_clusters"),
|
||||||
left: dyn.NewValue([]dyn.Value{}, location),
|
left: dyn.NewValue([]dyn.Value{}, []dyn.Location{location}),
|
||||||
expectedErr: merge.ErrOverrideUndoDelete,
|
expectedErr: merge.ErrOverrideUndoDelete,
|
||||||
phases: allPhases,
|
phases: allPhases,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "allow delete of non-empty job clusters",
|
name: "allow delete of non-empty job clusters",
|
||||||
path: dyn.MustPathFromString("resources.jobs.job0.job_clusters"),
|
path: dyn.MustPathFromString("resources.jobs.job0.job_clusters"),
|
||||||
left: dyn.NewValue([]dyn.Value{dyn.NewValue("abc", location)}, location),
|
left: dyn.NewValue([]dyn.Value{dyn.NewValue("abc", []dyn.Location{location})}, []dyn.Location{location}),
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
// deletions aren't allowed in 'load' phase
|
// deletions aren't allowed in 'load' phase
|
||||||
phases: []phase{PythonMutatorPhaseInit},
|
phases: []phase{PythonMutatorPhaseInit},
|
||||||
|
@ -492,17 +492,15 @@ func TestCreateOverrideVisitor_omitempty(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "undo delete of empty tags",
|
name: "undo delete of empty tags",
|
||||||
path: dyn.MustPathFromString("resources.jobs.job0.tags"),
|
path: dyn.MustPathFromString("resources.jobs.job0.tags"),
|
||||||
left: dyn.NewValue(map[string]dyn.Value{}, location),
|
left: dyn.NewValue(map[string]dyn.Value{}, []dyn.Location{location}),
|
||||||
expectedErr: merge.ErrOverrideUndoDelete,
|
expectedErr: merge.ErrOverrideUndoDelete,
|
||||||
phases: allPhases,
|
phases: allPhases,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "allow delete of non-empty tags",
|
name: "allow delete of non-empty tags",
|
||||||
path: dyn.MustPathFromString("resources.jobs.job0.tags"),
|
path: dyn.MustPathFromString("resources.jobs.job0.tags"),
|
||||||
left: dyn.NewValue(
|
left: dyn.NewValue(map[string]dyn.Value{"dev": dyn.NewValue("true", []dyn.Location{location})}, []dyn.Location{location}),
|
||||||
map[string]dyn.Value{"dev": dyn.NewValue("true", location)},
|
|
||||||
location,
|
|
||||||
),
|
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
// deletions aren't allowed in 'load' phase
|
// deletions aren't allowed in 'load' phase
|
||||||
phases: []phase{PythonMutatorPhaseInit},
|
phases: []phase{PythonMutatorPhaseInit},
|
||||||
|
@ -510,7 +508,7 @@ func TestCreateOverrideVisitor_omitempty(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "undo delete of nil",
|
name: "undo delete of nil",
|
||||||
path: dyn.MustPathFromString("resources.jobs.job0.tags"),
|
path: dyn.MustPathFromString("resources.jobs.job0.tags"),
|
||||||
left: dyn.NilValue.WithLocation(location),
|
left: dyn.NilValue.WithLocations([]dyn.Location{location}),
|
||||||
expectedErr: merge.ErrOverrideUndoDelete,
|
expectedErr: merge.ErrOverrideUndoDelete,
|
||||||
phases: allPhases,
|
phases: allPhases,
|
||||||
},
|
},
|
||||||
|
|
|
@ -38,7 +38,7 @@ func (m *rewriteSyncPaths) makeRelativeTo(root string) dyn.MapFunc {
|
||||||
return dyn.InvalidValue, err
|
return dyn.InvalidValue, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(filepath.Join(rel, v.MustString()), v.Location()), nil
|
return dyn.NewValue(filepath.Join(rel, v.MustString()), v.Locations()), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -182,7 +182,7 @@ func (t *translateContext) rewriteValue(p dyn.Path, v dyn.Value, fn rewriteFunc,
|
||||||
return dyn.InvalidValue, err
|
return dyn.InvalidValue, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(out, v.Location()), nil
|
return dyn.NewValue(out, v.Locations()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *translateContext) rewriteRelativeTo(p dyn.Path, v dyn.Value, fn rewriteFunc, dir, fallback string) (dyn.Value, error) {
|
func (t *translateContext) rewriteRelativeTo(p dyn.Path, v dyn.Value, fn rewriteFunc, dir, fallback string) (dyn.Value, error) {
|
||||||
|
|
|
@ -378,7 +378,7 @@ func (r *Root) MergeTargetOverrides(name string) error {
|
||||||
|
|
||||||
// Below, we're setting fields on the bundle key, so make sure it exists.
|
// Below, we're setting fields on the bundle key, so make sure it exists.
|
||||||
if root.Get("bundle").Kind() == dyn.KindInvalid {
|
if root.Get("bundle").Kind() == dyn.KindInvalid {
|
||||||
root, err = dyn.Set(root, "bundle", dyn.NewValue(map[string]dyn.Value{}, dyn.Location{}))
|
root, err = dyn.Set(root, "bundle", dyn.V(map[string]dyn.Value{}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -404,7 +404,7 @@ func (r *Root) MergeTargetOverrides(name string) error {
|
||||||
if v := target.Get("git"); v.Kind() != dyn.KindInvalid {
|
if v := target.Get("git"); v.Kind() != dyn.KindInvalid {
|
||||||
ref, err := dyn.GetByPath(root, dyn.NewPath(dyn.Key("bundle"), dyn.Key("git")))
|
ref, err := dyn.GetByPath(root, dyn.NewPath(dyn.Key("bundle"), dyn.Key("git")))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ref = dyn.NewValue(map[string]dyn.Value{}, dyn.Location{})
|
ref = dyn.V(map[string]dyn.Value{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge the override into the reference.
|
// Merge the override into the reference.
|
||||||
|
@ -415,7 +415,7 @@ func (r *Root) MergeTargetOverrides(name string) error {
|
||||||
|
|
||||||
// If the branch was overridden, we need to clear the inferred flag.
|
// If the branch was overridden, we need to clear the inferred flag.
|
||||||
if branch := v.Get("branch"); branch.Kind() != dyn.KindInvalid {
|
if branch := v.Get("branch"); branch.Kind() != dyn.KindInvalid {
|
||||||
out, err = dyn.SetByPath(out, dyn.NewPath(dyn.Key("inferred")), dyn.NewValue(false, dyn.Location{}))
|
out, err = dyn.SetByPath(out, dyn.NewPath(dyn.Key("inferred")), dyn.V(false))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -456,7 +456,7 @@ func rewriteShorthands(v dyn.Value) (dyn.Value, error) {
|
||||||
// configuration will convert this to a string if necessary.
|
// configuration will convert this to a string if necessary.
|
||||||
return dyn.NewValue(map[string]dyn.Value{
|
return dyn.NewValue(map[string]dyn.Value{
|
||||||
"default": variable,
|
"default": variable,
|
||||||
}, variable.Location()), nil
|
}, variable.Locations()), nil
|
||||||
|
|
||||||
case dyn.KindMap, dyn.KindSequence:
|
case dyn.KindMap, dyn.KindSequence:
|
||||||
// Check if the original definition of variable has a type field.
|
// Check if the original definition of variable has a type field.
|
||||||
|
@ -469,7 +469,7 @@ func rewriteShorthands(v dyn.Value) (dyn.Value, error) {
|
||||||
return dyn.NewValue(map[string]dyn.Value{
|
return dyn.NewValue(map[string]dyn.Value{
|
||||||
"type": typeV,
|
"type": typeV,
|
||||||
"default": variable,
|
"default": variable,
|
||||||
}, variable.Location()), nil
|
}, variable.Locations()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return variable, nil
|
return variable, nil
|
||||||
|
|
|
@ -14,9 +14,9 @@ func SetLocation(b *bundle.Bundle, prefix string, filePath string) {
|
||||||
return dyn.Walk(root, func(p dyn.Path, v dyn.Value) (dyn.Value, error) {
|
return dyn.Walk(root, func(p dyn.Path, v dyn.Value) (dyn.Value, error) {
|
||||||
// If the path has the given prefix, set the location.
|
// If the path has the given prefix, set the location.
|
||||||
if p.HasPrefix(start) {
|
if p.HasPrefix(start) {
|
||||||
return v.WithLocation(dyn.Location{
|
return v.WithLocations([]dyn.Location{{
|
||||||
File: filePath,
|
File: filePath,
|
||||||
}), nil
|
}}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// The path is not nested under the given prefix.
|
// The path is not nested under the given prefix.
|
||||||
|
|
|
@ -42,7 +42,7 @@ func fromTyped(src any, ref dyn.Value, options ...fromTypedOptions) (dyn.Value,
|
||||||
// Dereference pointer if necessary
|
// Dereference pointer if necessary
|
||||||
for srcv.Kind() == reflect.Pointer {
|
for srcv.Kind() == reflect.Pointer {
|
||||||
if srcv.IsNil() {
|
if srcv.IsNil() {
|
||||||
return dyn.NilValue.WithLocation(ref.Location()), nil
|
return dyn.NilValue.WithLocations(ref.Locations()), nil
|
||||||
}
|
}
|
||||||
srcv = srcv.Elem()
|
srcv = srcv.Elem()
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ func fromTyped(src any, ref dyn.Value, options ...fromTypedOptions) (dyn.Value,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dyn.InvalidValue, err
|
return dyn.InvalidValue, err
|
||||||
}
|
}
|
||||||
return v.WithLocation(ref.Location()), err
|
return v.WithLocations(ref.Locations()), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func fromTypedStruct(src reflect.Value, ref dyn.Value, options ...fromTypedOptions) (dyn.Value, error) {
|
func fromTypedStruct(src reflect.Value, ref dyn.Value, options ...fromTypedOptions) (dyn.Value, error) {
|
||||||
|
|
|
@ -115,16 +115,16 @@ func TestFromTypedStructSetFieldsRetainLocation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ref := dyn.V(map[string]dyn.Value{
|
ref := dyn.V(map[string]dyn.Value{
|
||||||
"foo": dyn.NewValue("bar", dyn.Location{File: "foo"}),
|
"foo": dyn.NewValue("bar", []dyn.Location{{File: "foo"}}),
|
||||||
"bar": dyn.NewValue("baz", dyn.Location{File: "bar"}),
|
"bar": dyn.NewValue("baz", []dyn.Location{{File: "bar"}}),
|
||||||
})
|
})
|
||||||
|
|
||||||
nv, err := FromTyped(src, ref)
|
nv, err := FromTyped(src, ref)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Assert foo and bar have retained their location.
|
// Assert foo and bar have retained their location.
|
||||||
assert.Equal(t, dyn.NewValue("bar", dyn.Location{File: "foo"}), nv.Get("foo"))
|
assert.Equal(t, dyn.NewValue("bar", []dyn.Location{{File: "foo"}}), nv.Get("foo"))
|
||||||
assert.Equal(t, dyn.NewValue("qux", dyn.Location{File: "bar"}), nv.Get("bar"))
|
assert.Equal(t, dyn.NewValue("qux", []dyn.Location{{File: "bar"}}), nv.Get("bar"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromTypedStringMapWithZeroValue(t *testing.T) {
|
func TestFromTypedStringMapWithZeroValue(t *testing.T) {
|
||||||
|
@ -359,16 +359,16 @@ func TestFromTypedMapNonEmptyRetainLocation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ref := dyn.V(map[string]dyn.Value{
|
ref := dyn.V(map[string]dyn.Value{
|
||||||
"foo": dyn.NewValue("bar", dyn.Location{File: "foo"}),
|
"foo": dyn.NewValue("bar", []dyn.Location{{File: "foo"}}),
|
||||||
"bar": dyn.NewValue("baz", dyn.Location{File: "bar"}),
|
"bar": dyn.NewValue("baz", []dyn.Location{{File: "bar"}}),
|
||||||
})
|
})
|
||||||
|
|
||||||
nv, err := FromTyped(src, ref)
|
nv, err := FromTyped(src, ref)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Assert foo and bar have retained their locations.
|
// Assert foo and bar have retained their locations.
|
||||||
assert.Equal(t, dyn.NewValue("bar", dyn.Location{File: "foo"}), nv.Get("foo"))
|
assert.Equal(t, dyn.NewValue("bar", []dyn.Location{{File: "foo"}}), nv.Get("foo"))
|
||||||
assert.Equal(t, dyn.NewValue("qux", dyn.Location{File: "bar"}), nv.Get("bar"))
|
assert.Equal(t, dyn.NewValue("qux", []dyn.Location{{File: "bar"}}), nv.Get("bar"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromTypedMapFieldWithZeroValue(t *testing.T) {
|
func TestFromTypedMapFieldWithZeroValue(t *testing.T) {
|
||||||
|
@ -432,16 +432,16 @@ func TestFromTypedSliceNonEmptyRetainLocation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ref := dyn.V([]dyn.Value{
|
ref := dyn.V([]dyn.Value{
|
||||||
dyn.NewValue("foo", dyn.Location{File: "foo"}),
|
dyn.NewValue("foo", []dyn.Location{{File: "foo"}}),
|
||||||
dyn.NewValue("bar", dyn.Location{File: "bar"}),
|
dyn.NewValue("bar", []dyn.Location{{File: "bar"}}),
|
||||||
})
|
})
|
||||||
|
|
||||||
nv, err := FromTyped(src, ref)
|
nv, err := FromTyped(src, ref)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Assert foo and bar have retained their locations.
|
// Assert foo and bar have retained their locations.
|
||||||
assert.Equal(t, dyn.NewValue("foo", dyn.Location{File: "foo"}), nv.Index(0))
|
assert.Equal(t, dyn.NewValue("foo", []dyn.Location{{File: "foo"}}), nv.Index(0))
|
||||||
assert.Equal(t, dyn.NewValue("bar", dyn.Location{File: "bar"}), nv.Index(1))
|
assert.Equal(t, dyn.NewValue("bar", []dyn.Location{{File: "bar"}}), nv.Index(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromTypedStringEmpty(t *testing.T) {
|
func TestFromTypedStringEmpty(t *testing.T) {
|
||||||
|
@ -477,19 +477,19 @@ func TestFromTypedStringNonEmptyOverwrite(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromTypedStringRetainsLocations(t *testing.T) {
|
func TestFromTypedStringRetainsLocations(t *testing.T) {
|
||||||
var ref = dyn.NewValue("foo", dyn.Location{File: "foo"})
|
var ref = dyn.NewValue("foo", []dyn.Location{{File: "foo"}})
|
||||||
|
|
||||||
// case: value has not been changed
|
// case: value has not been changed
|
||||||
var src string = "foo"
|
var src string = "foo"
|
||||||
nv, err := FromTyped(src, ref)
|
nv, err := FromTyped(src, ref)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, dyn.NewValue("foo", dyn.Location{File: "foo"}), nv)
|
assert.Equal(t, dyn.NewValue("foo", []dyn.Location{{File: "foo"}}), nv)
|
||||||
|
|
||||||
// case: value has been changed
|
// case: value has been changed
|
||||||
src = "bar"
|
src = "bar"
|
||||||
nv, err = FromTyped(src, ref)
|
nv, err = FromTyped(src, ref)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, dyn.NewValue("bar", dyn.Location{File: "foo"}), nv)
|
assert.Equal(t, dyn.NewValue("bar", []dyn.Location{{File: "foo"}}), nv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromTypedStringTypeError(t *testing.T) {
|
func TestFromTypedStringTypeError(t *testing.T) {
|
||||||
|
@ -532,19 +532,19 @@ func TestFromTypedBoolNonEmptyOverwrite(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromTypedBoolRetainsLocations(t *testing.T) {
|
func TestFromTypedBoolRetainsLocations(t *testing.T) {
|
||||||
var ref = dyn.NewValue(true, dyn.Location{File: "foo"})
|
var ref = dyn.NewValue(true, []dyn.Location{{File: "foo"}})
|
||||||
|
|
||||||
// case: value has not been changed
|
// case: value has not been changed
|
||||||
var src bool = true
|
var src bool = true
|
||||||
nv, err := FromTyped(src, ref)
|
nv, err := FromTyped(src, ref)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, dyn.NewValue(true, dyn.Location{File: "foo"}), nv)
|
assert.Equal(t, dyn.NewValue(true, []dyn.Location{{File: "foo"}}), nv)
|
||||||
|
|
||||||
// case: value has been changed
|
// case: value has been changed
|
||||||
src = false
|
src = false
|
||||||
nv, err = FromTyped(src, ref)
|
nv, err = FromTyped(src, ref)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, dyn.NewValue(false, dyn.Location{File: "foo"}), nv)
|
assert.Equal(t, dyn.NewValue(false, []dyn.Location{{File: "foo"}}), nv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromTypedBoolVariableReference(t *testing.T) {
|
func TestFromTypedBoolVariableReference(t *testing.T) {
|
||||||
|
@ -595,19 +595,19 @@ func TestFromTypedIntNonEmptyOverwrite(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromTypedIntRetainsLocations(t *testing.T) {
|
func TestFromTypedIntRetainsLocations(t *testing.T) {
|
||||||
var ref = dyn.NewValue(1234, dyn.Location{File: "foo"})
|
var ref = dyn.NewValue(1234, []dyn.Location{{File: "foo"}})
|
||||||
|
|
||||||
// case: value has not been changed
|
// case: value has not been changed
|
||||||
var src int = 1234
|
var src int = 1234
|
||||||
nv, err := FromTyped(src, ref)
|
nv, err := FromTyped(src, ref)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, dyn.NewValue(1234, dyn.Location{File: "foo"}), nv)
|
assert.Equal(t, dyn.NewValue(1234, []dyn.Location{{File: "foo"}}), nv)
|
||||||
|
|
||||||
// case: value has been changed
|
// case: value has been changed
|
||||||
src = 1235
|
src = 1235
|
||||||
nv, err = FromTyped(src, ref)
|
nv, err = FromTyped(src, ref)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, dyn.NewValue(int64(1235), dyn.Location{File: "foo"}), nv)
|
assert.Equal(t, dyn.NewValue(int64(1235), []dyn.Location{{File: "foo"}}), nv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromTypedIntVariableReference(t *testing.T) {
|
func TestFromTypedIntVariableReference(t *testing.T) {
|
||||||
|
@ -659,19 +659,19 @@ func TestFromTypedFloatNonEmptyOverwrite(t *testing.T) {
|
||||||
|
|
||||||
func TestFromTypedFloatRetainsLocations(t *testing.T) {
|
func TestFromTypedFloatRetainsLocations(t *testing.T) {
|
||||||
var src float64
|
var src float64
|
||||||
var ref = dyn.NewValue(1.23, dyn.Location{File: "foo"})
|
var ref = dyn.NewValue(1.23, []dyn.Location{{File: "foo"}})
|
||||||
|
|
||||||
// case: value has not been changed
|
// case: value has not been changed
|
||||||
src = 1.23
|
src = 1.23
|
||||||
nv, err := FromTyped(src, ref)
|
nv, err := FromTyped(src, ref)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, dyn.NewValue(1.23, dyn.Location{File: "foo"}), nv)
|
assert.Equal(t, dyn.NewValue(1.23, []dyn.Location{{File: "foo"}}), nv)
|
||||||
|
|
||||||
// case: value has been changed
|
// case: value has been changed
|
||||||
src = 1.24
|
src = 1.24
|
||||||
nv, err = FromTyped(src, ref)
|
nv, err = FromTyped(src, ref)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, dyn.NewValue(1.24, dyn.Location{File: "foo"}), nv)
|
assert.Equal(t, dyn.NewValue(1.24, []dyn.Location{{File: "foo"}}), nv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromTypedFloatVariableReference(t *testing.T) {
|
func TestFromTypedFloatVariableReference(t *testing.T) {
|
||||||
|
@ -740,27 +740,27 @@ func TestFromTypedNilPointerRetainsLocations(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var src *Tmp
|
var src *Tmp
|
||||||
ref := dyn.NewValue(nil, dyn.Location{File: "foobar"})
|
ref := dyn.NewValue(nil, []dyn.Location{{File: "foobar"}})
|
||||||
|
|
||||||
nv, err := FromTyped(src, ref)
|
nv, err := FromTyped(src, ref)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, dyn.NewValue(nil, dyn.Location{File: "foobar"}), nv)
|
assert.Equal(t, dyn.NewValue(nil, []dyn.Location{{File: "foobar"}}), nv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromTypedNilMapRetainsLocation(t *testing.T) {
|
func TestFromTypedNilMapRetainsLocation(t *testing.T) {
|
||||||
var src map[string]string
|
var src map[string]string
|
||||||
ref := dyn.NewValue(nil, dyn.Location{File: "foobar"})
|
ref := dyn.NewValue(nil, []dyn.Location{{File: "foobar"}})
|
||||||
|
|
||||||
nv, err := FromTyped(src, ref)
|
nv, err := FromTyped(src, ref)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, dyn.NewValue(nil, dyn.Location{File: "foobar"}), nv)
|
assert.Equal(t, dyn.NewValue(nil, []dyn.Location{{File: "foobar"}}), nv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromTypedNilSliceRetainsLocation(t *testing.T) {
|
func TestFromTypedNilSliceRetainsLocation(t *testing.T) {
|
||||||
var src []string
|
var src []string
|
||||||
ref := dyn.NewValue(nil, dyn.Location{File: "foobar"})
|
ref := dyn.NewValue(nil, []dyn.Location{{File: "foobar"}})
|
||||||
|
|
||||||
nv, err := FromTyped(src, ref)
|
nv, err := FromTyped(src, ref)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, dyn.NewValue(nil, dyn.Location{File: "foobar"}), nv)
|
assert.Equal(t, dyn.NewValue(nil, []dyn.Location{{File: "foobar"}}), nv)
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ func (n normalizeOptions) normalizeStruct(typ reflect.Type, src dyn.Value, seen
|
||||||
|
|
||||||
// Return the normalized value if missing fields are not included.
|
// Return the normalized value if missing fields are not included.
|
||||||
if !n.includeMissingFields {
|
if !n.includeMissingFields {
|
||||||
return dyn.NewValue(out, src.Location()), diags
|
return dyn.NewValue(out, src.Locations()), diags
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate missing fields with their zero values.
|
// Populate missing fields with their zero values.
|
||||||
|
@ -165,7 +165,7 @@ func (n normalizeOptions) normalizeStruct(typ reflect.Type, src dyn.Value, seen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(out, src.Location()), diags
|
return dyn.NewValue(out, src.Locations()), diags
|
||||||
case dyn.KindNil:
|
case dyn.KindNil:
|
||||||
return src, diags
|
return src, diags
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ func (n normalizeOptions) normalizeMap(typ reflect.Type, src dyn.Value, seen []r
|
||||||
out.Set(pk, nv)
|
out.Set(pk, nv)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(out, src.Location()), diags
|
return dyn.NewValue(out, src.Locations()), diags
|
||||||
case dyn.KindNil:
|
case dyn.KindNil:
|
||||||
return src, diags
|
return src, diags
|
||||||
|
|
||||||
|
@ -238,7 +238,7 @@ func (n normalizeOptions) normalizeSlice(typ reflect.Type, src dyn.Value, seen [
|
||||||
out = append(out, v)
|
out = append(out, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(out, src.Location()), diags
|
return dyn.NewValue(out, src.Locations()), diags
|
||||||
case dyn.KindNil:
|
case dyn.KindNil:
|
||||||
return src, diags
|
return src, diags
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ func (n normalizeOptions) normalizeString(typ reflect.Type, src dyn.Value, path
|
||||||
return dyn.InvalidValue, diags.Append(typeMismatch(dyn.KindString, src, path))
|
return dyn.InvalidValue, diags.Append(typeMismatch(dyn.KindString, src, path))
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(out, src.Location()), diags
|
return dyn.NewValue(out, src.Locations()), diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n normalizeOptions) normalizeBool(typ reflect.Type, src dyn.Value, path dyn.Path) (dyn.Value, diag.Diagnostics) {
|
func (n normalizeOptions) normalizeBool(typ reflect.Type, src dyn.Value, path dyn.Path) (dyn.Value, diag.Diagnostics) {
|
||||||
|
@ -306,7 +306,7 @@ func (n normalizeOptions) normalizeBool(typ reflect.Type, src dyn.Value, path dy
|
||||||
return dyn.InvalidValue, diags.Append(typeMismatch(dyn.KindBool, src, path))
|
return dyn.InvalidValue, diags.Append(typeMismatch(dyn.KindBool, src, path))
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(out, src.Location()), diags
|
return dyn.NewValue(out, src.Locations()), diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n normalizeOptions) normalizeInt(typ reflect.Type, src dyn.Value, path dyn.Path) (dyn.Value, diag.Diagnostics) {
|
func (n normalizeOptions) normalizeInt(typ reflect.Type, src dyn.Value, path dyn.Path) (dyn.Value, diag.Diagnostics) {
|
||||||
|
@ -349,7 +349,7 @@ func (n normalizeOptions) normalizeInt(typ reflect.Type, src dyn.Value, path dyn
|
||||||
return dyn.InvalidValue, diags.Append(typeMismatch(dyn.KindInt, src, path))
|
return dyn.InvalidValue, diags.Append(typeMismatch(dyn.KindInt, src, path))
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(out, src.Location()), diags
|
return dyn.NewValue(out, src.Locations()), diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n normalizeOptions) normalizeFloat(typ reflect.Type, src dyn.Value, path dyn.Path) (dyn.Value, diag.Diagnostics) {
|
func (n normalizeOptions) normalizeFloat(typ reflect.Type, src dyn.Value, path dyn.Path) (dyn.Value, diag.Diagnostics) {
|
||||||
|
@ -392,7 +392,7 @@ func (n normalizeOptions) normalizeFloat(typ reflect.Type, src dyn.Value, path d
|
||||||
return dyn.InvalidValue, diags.Append(typeMismatch(dyn.KindFloat, src, path))
|
return dyn.InvalidValue, diags.Append(typeMismatch(dyn.KindFloat, src, path))
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(out, src.Location()), diags
|
return dyn.NewValue(out, src.Locations()), diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n normalizeOptions) normalizeInterface(typ reflect.Type, src dyn.Value, path dyn.Path) (dyn.Value, diag.Diagnostics) {
|
func (n normalizeOptions) normalizeInterface(typ reflect.Type, src dyn.Value, path dyn.Path) (dyn.Value, diag.Diagnostics) {
|
||||||
|
|
|
@ -229,7 +229,7 @@ func TestNormalizeStructVariableReference(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var typ Tmp
|
var typ Tmp
|
||||||
vin := dyn.NewValue("${var.foo}", dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue("${var.foo}", []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
vout, err := Normalize(typ, vin)
|
vout, err := Normalize(typ, vin)
|
||||||
assert.Empty(t, err)
|
assert.Empty(t, err)
|
||||||
assert.Equal(t, vin, vout)
|
assert.Equal(t, vin, vout)
|
||||||
|
@ -241,7 +241,7 @@ func TestNormalizeStructRandomStringError(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var typ Tmp
|
var typ Tmp
|
||||||
vin := dyn.NewValue("var foo", dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue("var foo", []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
_, err := Normalize(typ, vin)
|
_, err := Normalize(typ, vin)
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
assert.Equal(t, diag.Diagnostic{
|
assert.Equal(t, diag.Diagnostic{
|
||||||
|
@ -258,7 +258,7 @@ func TestNormalizeStructIntError(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var typ Tmp
|
var typ Tmp
|
||||||
vin := dyn.NewValue(1, dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue(1, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
_, err := Normalize(typ, vin)
|
_, err := Normalize(typ, vin)
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
assert.Equal(t, diag.Diagnostic{
|
assert.Equal(t, diag.Diagnostic{
|
||||||
|
@ -360,7 +360,7 @@ func TestNormalizeMapNestedError(t *testing.T) {
|
||||||
|
|
||||||
func TestNormalizeMapVariableReference(t *testing.T) {
|
func TestNormalizeMapVariableReference(t *testing.T) {
|
||||||
var typ map[string]string
|
var typ map[string]string
|
||||||
vin := dyn.NewValue("${var.foo}", dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue("${var.foo}", []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
vout, err := Normalize(typ, vin)
|
vout, err := Normalize(typ, vin)
|
||||||
assert.Empty(t, err)
|
assert.Empty(t, err)
|
||||||
assert.Equal(t, vin, vout)
|
assert.Equal(t, vin, vout)
|
||||||
|
@ -368,7 +368,7 @@ func TestNormalizeMapVariableReference(t *testing.T) {
|
||||||
|
|
||||||
func TestNormalizeMapRandomStringError(t *testing.T) {
|
func TestNormalizeMapRandomStringError(t *testing.T) {
|
||||||
var typ map[string]string
|
var typ map[string]string
|
||||||
vin := dyn.NewValue("var foo", dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue("var foo", []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
_, err := Normalize(typ, vin)
|
_, err := Normalize(typ, vin)
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
assert.Equal(t, diag.Diagnostic{
|
assert.Equal(t, diag.Diagnostic{
|
||||||
|
@ -381,7 +381,7 @@ func TestNormalizeMapRandomStringError(t *testing.T) {
|
||||||
|
|
||||||
func TestNormalizeMapIntError(t *testing.T) {
|
func TestNormalizeMapIntError(t *testing.T) {
|
||||||
var typ map[string]string
|
var typ map[string]string
|
||||||
vin := dyn.NewValue(1, dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue(1, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
_, err := Normalize(typ, vin)
|
_, err := Normalize(typ, vin)
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
assert.Equal(t, diag.Diagnostic{
|
assert.Equal(t, diag.Diagnostic{
|
||||||
|
@ -482,7 +482,7 @@ func TestNormalizeSliceNestedError(t *testing.T) {
|
||||||
|
|
||||||
func TestNormalizeSliceVariableReference(t *testing.T) {
|
func TestNormalizeSliceVariableReference(t *testing.T) {
|
||||||
var typ []string
|
var typ []string
|
||||||
vin := dyn.NewValue("${var.foo}", dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue("${var.foo}", []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
vout, err := Normalize(typ, vin)
|
vout, err := Normalize(typ, vin)
|
||||||
assert.Empty(t, err)
|
assert.Empty(t, err)
|
||||||
assert.Equal(t, vin, vout)
|
assert.Equal(t, vin, vout)
|
||||||
|
@ -490,7 +490,7 @@ func TestNormalizeSliceVariableReference(t *testing.T) {
|
||||||
|
|
||||||
func TestNormalizeSliceRandomStringError(t *testing.T) {
|
func TestNormalizeSliceRandomStringError(t *testing.T) {
|
||||||
var typ []string
|
var typ []string
|
||||||
vin := dyn.NewValue("var foo", dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue("var foo", []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
_, err := Normalize(typ, vin)
|
_, err := Normalize(typ, vin)
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
assert.Equal(t, diag.Diagnostic{
|
assert.Equal(t, diag.Diagnostic{
|
||||||
|
@ -503,7 +503,7 @@ func TestNormalizeSliceRandomStringError(t *testing.T) {
|
||||||
|
|
||||||
func TestNormalizeSliceIntError(t *testing.T) {
|
func TestNormalizeSliceIntError(t *testing.T) {
|
||||||
var typ []string
|
var typ []string
|
||||||
vin := dyn.NewValue(1, dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue(1, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
_, err := Normalize(typ, vin)
|
_, err := Normalize(typ, vin)
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
assert.Equal(t, diag.Diagnostic{
|
assert.Equal(t, diag.Diagnostic{
|
||||||
|
@ -524,7 +524,7 @@ func TestNormalizeString(t *testing.T) {
|
||||||
|
|
||||||
func TestNormalizeStringNil(t *testing.T) {
|
func TestNormalizeStringNil(t *testing.T) {
|
||||||
var typ string
|
var typ string
|
||||||
vin := dyn.NewValue(nil, dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue(nil, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
_, err := Normalize(&typ, vin)
|
_, err := Normalize(&typ, vin)
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
assert.Equal(t, diag.Diagnostic{
|
assert.Equal(t, diag.Diagnostic{
|
||||||
|
@ -537,26 +537,26 @@ func TestNormalizeStringNil(t *testing.T) {
|
||||||
|
|
||||||
func TestNormalizeStringFromBool(t *testing.T) {
|
func TestNormalizeStringFromBool(t *testing.T) {
|
||||||
var typ string
|
var typ string
|
||||||
vin := dyn.NewValue(true, dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue(true, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
vout, err := Normalize(&typ, vin)
|
vout, err := Normalize(&typ, vin)
|
||||||
assert.Empty(t, err)
|
assert.Empty(t, err)
|
||||||
assert.Equal(t, dyn.NewValue("true", vin.Location()), vout)
|
assert.Equal(t, dyn.NewValue("true", vin.Locations()), vout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNormalizeStringFromInt(t *testing.T) {
|
func TestNormalizeStringFromInt(t *testing.T) {
|
||||||
var typ string
|
var typ string
|
||||||
vin := dyn.NewValue(123, dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue(123, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
vout, err := Normalize(&typ, vin)
|
vout, err := Normalize(&typ, vin)
|
||||||
assert.Empty(t, err)
|
assert.Empty(t, err)
|
||||||
assert.Equal(t, dyn.NewValue("123", vin.Location()), vout)
|
assert.Equal(t, dyn.NewValue("123", vin.Locations()), vout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNormalizeStringFromFloat(t *testing.T) {
|
func TestNormalizeStringFromFloat(t *testing.T) {
|
||||||
var typ string
|
var typ string
|
||||||
vin := dyn.NewValue(1.20, dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue(1.20, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
vout, err := Normalize(&typ, vin)
|
vout, err := Normalize(&typ, vin)
|
||||||
assert.Empty(t, err)
|
assert.Empty(t, err)
|
||||||
assert.Equal(t, dyn.NewValue("1.2", vin.Location()), vout)
|
assert.Equal(t, dyn.NewValue("1.2", vin.Locations()), vout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNormalizeStringError(t *testing.T) {
|
func TestNormalizeStringError(t *testing.T) {
|
||||||
|
@ -582,7 +582,7 @@ func TestNormalizeBool(t *testing.T) {
|
||||||
|
|
||||||
func TestNormalizeBoolNil(t *testing.T) {
|
func TestNormalizeBoolNil(t *testing.T) {
|
||||||
var typ bool
|
var typ bool
|
||||||
vin := dyn.NewValue(nil, dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue(nil, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
_, err := Normalize(&typ, vin)
|
_, err := Normalize(&typ, vin)
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
assert.Equal(t, diag.Diagnostic{
|
assert.Equal(t, diag.Diagnostic{
|
||||||
|
@ -658,7 +658,7 @@ func TestNormalizeInt(t *testing.T) {
|
||||||
|
|
||||||
func TestNormalizeIntNil(t *testing.T) {
|
func TestNormalizeIntNil(t *testing.T) {
|
||||||
var typ int
|
var typ int
|
||||||
vin := dyn.NewValue(nil, dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue(nil, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
_, err := Normalize(&typ, vin)
|
_, err := Normalize(&typ, vin)
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
assert.Equal(t, diag.Diagnostic{
|
assert.Equal(t, diag.Diagnostic{
|
||||||
|
@ -742,7 +742,7 @@ func TestNormalizeFloat(t *testing.T) {
|
||||||
|
|
||||||
func TestNormalizeFloatNil(t *testing.T) {
|
func TestNormalizeFloatNil(t *testing.T) {
|
||||||
var typ float64
|
var typ float64
|
||||||
vin := dyn.NewValue(nil, dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue(nil, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
_, err := Normalize(&typ, vin)
|
_, err := Normalize(&typ, vin)
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
assert.Equal(t, diag.Diagnostic{
|
assert.Equal(t, diag.Diagnostic{
|
||||||
|
@ -842,26 +842,26 @@ func TestNormalizeAnchors(t *testing.T) {
|
||||||
|
|
||||||
func TestNormalizeBoolToAny(t *testing.T) {
|
func TestNormalizeBoolToAny(t *testing.T) {
|
||||||
var typ any
|
var typ any
|
||||||
vin := dyn.NewValue(false, dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue(false, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
vout, err := Normalize(&typ, vin)
|
vout, err := Normalize(&typ, vin)
|
||||||
assert.Len(t, err, 0)
|
assert.Len(t, err, 0)
|
||||||
assert.Equal(t, dyn.NewValue(false, dyn.Location{File: "file", Line: 1, Column: 1}), vout)
|
assert.Equal(t, dyn.NewValue(false, []dyn.Location{{File: "file", Line: 1, Column: 1}}), vout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNormalizeIntToAny(t *testing.T) {
|
func TestNormalizeIntToAny(t *testing.T) {
|
||||||
var typ any
|
var typ any
|
||||||
vin := dyn.NewValue(10, dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue(10, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
vout, err := Normalize(&typ, vin)
|
vout, err := Normalize(&typ, vin)
|
||||||
assert.Len(t, err, 0)
|
assert.Len(t, err, 0)
|
||||||
assert.Equal(t, dyn.NewValue(10, dyn.Location{File: "file", Line: 1, Column: 1}), vout)
|
assert.Equal(t, dyn.NewValue(10, []dyn.Location{{File: "file", Line: 1, Column: 1}}), vout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNormalizeSliceToAny(t *testing.T) {
|
func TestNormalizeSliceToAny(t *testing.T) {
|
||||||
var typ any
|
var typ any
|
||||||
v1 := dyn.NewValue(1, dyn.Location{File: "file", Line: 1, Column: 1})
|
v1 := dyn.NewValue(1, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
v2 := dyn.NewValue(2, dyn.Location{File: "file", Line: 1, Column: 1})
|
v2 := dyn.NewValue(2, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
vin := dyn.NewValue([]dyn.Value{v1, v2}, dyn.Location{File: "file", Line: 1, Column: 1})
|
vin := dyn.NewValue([]dyn.Value{v1, v2}, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
vout, err := Normalize(&typ, vin)
|
vout, err := Normalize(&typ, vin)
|
||||||
assert.Len(t, err, 0)
|
assert.Len(t, err, 0)
|
||||||
assert.Equal(t, dyn.NewValue([]dyn.Value{v1, v2}, dyn.Location{File: "file", Line: 1, Column: 1}), vout)
|
assert.Equal(t, dyn.NewValue([]dyn.Value{v1, v2}, []dyn.Location{{File: "file", Line: 1, Column: 1}}), vout)
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,7 +155,7 @@ func (r *resolver) resolveRef(ref ref, seen []string) (dyn.Value, error) {
|
||||||
// of where it is used. This also means that relative path resolution is done
|
// of where it is used. This also means that relative path resolution is done
|
||||||
// relative to where a variable is used, not where it is defined.
|
// relative to where a variable is used, not where it is defined.
|
||||||
//
|
//
|
||||||
return dyn.NewValue(resolved[0].Value(), ref.value.Location()), nil
|
return dyn.NewValue(resolved[0].Value(), ref.value.Locations()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not pure; perform string interpolation.
|
// Not pure; perform string interpolation.
|
||||||
|
@ -178,7 +178,7 @@ func (r *resolver) resolveRef(ref ref, seen []string) (dyn.Value, error) {
|
||||||
ref.str = strings.Replace(ref.str, ref.matches[j][0], s, 1)
|
ref.str = strings.Replace(ref.str, ref.matches[j][0], s, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(ref.str, ref.value.Location()), nil
|
return dyn.NewValue(ref.str, ref.value.Locations()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resolver) resolveKey(key string, seen []string) (dyn.Value, error) {
|
func (r *resolver) resolveKey(key string, seen []string) (dyn.Value, error) {
|
||||||
|
|
|
@ -52,7 +52,7 @@ func (e elementsByKey) Map(_ dyn.Path, v dyn.Value) (dyn.Value, error) {
|
||||||
out = append(out, nv)
|
out = append(out, nv)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(out, v.Location()), nil
|
return dyn.NewValue(out, v.Locations()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ElementsByKey returns a [dyn.MapFunc] that operates on a sequence
|
// ElementsByKey returns a [dyn.MapFunc] that operates on a sequence
|
||||||
|
|
|
@ -12,6 +12,26 @@ import (
|
||||||
// * Merging x with nil or nil with x always yields x.
|
// * Merging x with nil or nil with x always yields x.
|
||||||
// * Merging maps a and b means entries from map b take precedence.
|
// * Merging maps a and b means entries from map b take precedence.
|
||||||
// * Merging sequences a and b means concatenating them.
|
// * Merging sequences a and b means concatenating them.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
func Merge(a, b dyn.Value) (dyn.Value, error) {
|
func Merge(a, b dyn.Value) (dyn.Value, error) {
|
||||||
return merge(a, b)
|
return merge(a, b)
|
||||||
}
|
}
|
||||||
|
@ -22,12 +42,12 @@ func merge(a, b dyn.Value) (dyn.Value, error) {
|
||||||
|
|
||||||
// If a is nil, return b.
|
// If a is nil, return b.
|
||||||
if ak == dyn.KindNil {
|
if ak == dyn.KindNil {
|
||||||
return b, nil
|
return b.AppendLocationsFromValue(a), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If b is nil, return a.
|
// If b is nil, return a.
|
||||||
if bk == dyn.KindNil {
|
if bk == dyn.KindNil {
|
||||||
return a, nil
|
return a.AppendLocationsFromValue(b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the appropriate merge function based on the kind of a and b.
|
// Call the appropriate merge function based on the kind of a and b.
|
||||||
|
@ -75,8 +95,8 @@ func mergeMap(a, b dyn.Value) (dyn.Value, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preserve the location of the first value.
|
// Preserve the location of the first value. Accumulate the locations of the second value.
|
||||||
return dyn.NewValue(out, a.Location()), nil
|
return dyn.NewValue(out, a.Locations()).AppendLocationsFromValue(b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeSequence(a, b dyn.Value) (dyn.Value, error) {
|
func mergeSequence(a, b dyn.Value) (dyn.Value, error) {
|
||||||
|
@ -88,11 +108,10 @@ func mergeSequence(a, b dyn.Value) (dyn.Value, error) {
|
||||||
copy(out[:], as)
|
copy(out[:], as)
|
||||||
copy(out[len(as):], bs)
|
copy(out[len(as):], bs)
|
||||||
|
|
||||||
// Preserve the location of the first value.
|
// Preserve the location of the first value. Accumulate the locations of the second value.
|
||||||
return dyn.NewValue(out, a.Location()), nil
|
return dyn.NewValue(out, a.Locations()).AppendLocationsFromValue(b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergePrimitive(a, b dyn.Value) (dyn.Value, error) {
|
func mergePrimitive(a, b dyn.Value) (dyn.Value, error) {
|
||||||
// Merging primitive values means using the incoming value.
|
// Merging primitive values means using the incoming value.
|
||||||
return b, nil
|
return b.AppendLocationsFromValue(a), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,15 +8,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMergeMaps(t *testing.T) {
|
func TestMergeMaps(t *testing.T) {
|
||||||
v1 := dyn.V(map[string]dyn.Value{
|
l1 := dyn.Location{File: "file1", Line: 1, Column: 2}
|
||||||
"foo": dyn.V("bar"),
|
v1 := dyn.NewValue(map[string]dyn.Value{
|
||||||
"bar": dyn.V("baz"),
|
"foo": dyn.NewValue("bar", []dyn.Location{l1}),
|
||||||
})
|
"bar": dyn.NewValue("baz", []dyn.Location{l1}),
|
||||||
|
}, []dyn.Location{l1})
|
||||||
|
|
||||||
v2 := dyn.V(map[string]dyn.Value{
|
l2 := dyn.Location{File: "file2", Line: 3, Column: 4}
|
||||||
"bar": dyn.V("qux"),
|
v2 := dyn.NewValue(map[string]dyn.Value{
|
||||||
"qux": dyn.V("foo"),
|
"bar": dyn.NewValue("qux", []dyn.Location{l2}),
|
||||||
})
|
"qux": dyn.NewValue("foo", []dyn.Location{l2}),
|
||||||
|
}, []dyn.Location{l2})
|
||||||
|
|
||||||
// Merge v2 into v1.
|
// Merge v2 into v1.
|
||||||
{
|
{
|
||||||
|
@ -27,6 +29,23 @@ func TestMergeMaps(t *testing.T) {
|
||||||
"bar": "qux",
|
"bar": "qux",
|
||||||
"qux": "foo",
|
"qux": "foo",
|
||||||
}, out.AsAny())
|
}, out.AsAny())
|
||||||
|
|
||||||
|
// Locations of both values should be preserved.
|
||||||
|
assert.Equal(t, []dyn.Location{l1, l2}, out.Locations())
|
||||||
|
assert.Equal(t, []dyn.Location{l2, l1}, out.Get("bar").Locations())
|
||||||
|
assert.Equal(t, []dyn.Location{l1}, out.Get("foo").Locations())
|
||||||
|
assert.Equal(t, []dyn.Location{l2}, out.Get("qux").Locations())
|
||||||
|
|
||||||
|
// Location of the merged value should be the location of v1.
|
||||||
|
assert.Equal(t, l1, out.Location())
|
||||||
|
|
||||||
|
// Value of bar is "qux" which comes from v2. This .Location() should
|
||||||
|
// return the location of v2.
|
||||||
|
assert.Equal(t, l2, out.Get("bar").Location())
|
||||||
|
|
||||||
|
// Original locations of keys that were not overwritten should be preserved.
|
||||||
|
assert.Equal(t, l1, out.Get("foo").Location())
|
||||||
|
assert.Equal(t, l2, out.Get("qux").Location())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge v1 into v2.
|
// Merge v1 into v2.
|
||||||
|
@ -38,30 +57,64 @@ func TestMergeMaps(t *testing.T) {
|
||||||
"bar": "baz",
|
"bar": "baz",
|
||||||
"qux": "foo",
|
"qux": "foo",
|
||||||
}, out.AsAny())
|
}, out.AsAny())
|
||||||
|
|
||||||
|
// Locations of both values should be preserved.
|
||||||
|
assert.Equal(t, []dyn.Location{l2, l1}, out.Locations())
|
||||||
|
assert.Equal(t, []dyn.Location{l1, l2}, out.Get("bar").Locations())
|
||||||
|
assert.Equal(t, []dyn.Location{l1}, out.Get("foo").Locations())
|
||||||
|
assert.Equal(t, []dyn.Location{l2}, out.Get("qux").Locations())
|
||||||
|
|
||||||
|
// Location of the merged value should be the location of v2.
|
||||||
|
assert.Equal(t, l2, out.Location())
|
||||||
|
|
||||||
|
// Value of bar is "baz" which comes from v1. This .Location() should
|
||||||
|
// return the location of v1.
|
||||||
|
assert.Equal(t, l1, out.Get("bar").Location())
|
||||||
|
|
||||||
|
// Original locations of keys that were not overwritten should be preserved.
|
||||||
|
assert.Equal(t, l1, out.Get("foo").Location())
|
||||||
|
assert.Equal(t, l2, out.Get("qux").Location())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMergeMapsNil(t *testing.T) {
|
func TestMergeMapsNil(t *testing.T) {
|
||||||
v := dyn.V(map[string]dyn.Value{
|
l := dyn.Location{File: "file", Line: 1, Column: 2}
|
||||||
|
v := dyn.NewValue(map[string]dyn.Value{
|
||||||
"foo": dyn.V("bar"),
|
"foo": dyn.V("bar"),
|
||||||
})
|
}, []dyn.Location{l})
|
||||||
|
|
||||||
|
nilL := dyn.Location{File: "file", Line: 3, Column: 4}
|
||||||
|
nilV := dyn.NewValue(nil, []dyn.Location{nilL})
|
||||||
|
|
||||||
// Merge nil into v.
|
// Merge nil into v.
|
||||||
{
|
{
|
||||||
out, err := Merge(v, dyn.NilValue)
|
out, err := Merge(v, nilV)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, map[string]any{
|
assert.Equal(t, map[string]any{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
}, out.AsAny())
|
}, out.AsAny())
|
||||||
|
|
||||||
|
// Locations of both values should be preserved.
|
||||||
|
assert.Equal(t, []dyn.Location{l, nilL}, out.Locations())
|
||||||
|
|
||||||
|
// Location of the non-nil value should be returned by .Location().
|
||||||
|
assert.Equal(t, l, out.Location())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge v into nil.
|
// Merge v into nil.
|
||||||
{
|
{
|
||||||
out, err := Merge(dyn.NilValue, v)
|
out, err := Merge(nilV, v)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, map[string]any{
|
assert.Equal(t, map[string]any{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
}, out.AsAny())
|
}, out.AsAny())
|
||||||
|
|
||||||
|
// Locations of both values should be preserved.
|
||||||
|
assert.Equal(t, []dyn.Location{l, nilL}, out.Locations())
|
||||||
|
|
||||||
|
// Location of the non-nil value should be returned by .Location().
|
||||||
|
assert.Equal(t, l, out.Location())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,15 +134,18 @@ func TestMergeMapsError(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMergeSequences(t *testing.T) {
|
func TestMergeSequences(t *testing.T) {
|
||||||
v1 := dyn.V([]dyn.Value{
|
l1 := dyn.Location{File: "file1", Line: 1, Column: 2}
|
||||||
dyn.V("bar"),
|
v1 := dyn.NewValue([]dyn.Value{
|
||||||
dyn.V("baz"),
|
dyn.NewValue("bar", []dyn.Location{l1}),
|
||||||
})
|
dyn.NewValue("baz", []dyn.Location{l1}),
|
||||||
|
}, []dyn.Location{l1})
|
||||||
|
|
||||||
v2 := dyn.V([]dyn.Value{
|
l2 := dyn.Location{File: "file2", Line: 3, Column: 4}
|
||||||
dyn.V("qux"),
|
l3 := dyn.Location{File: "file3", Line: 5, Column: 6}
|
||||||
dyn.V("foo"),
|
v2 := dyn.NewValue([]dyn.Value{
|
||||||
})
|
dyn.NewValue("qux", []dyn.Location{l2}),
|
||||||
|
dyn.NewValue("foo", []dyn.Location{l3}),
|
||||||
|
}, []dyn.Location{l2, l3})
|
||||||
|
|
||||||
// Merge v2 into v1.
|
// Merge v2 into v1.
|
||||||
{
|
{
|
||||||
|
@ -101,6 +157,18 @@ func TestMergeSequences(t *testing.T) {
|
||||||
"qux",
|
"qux",
|
||||||
"foo",
|
"foo",
|
||||||
}, out.AsAny())
|
}, out.AsAny())
|
||||||
|
|
||||||
|
// Locations of both values should be preserved.
|
||||||
|
assert.Equal(t, []dyn.Location{l1, l2, l3}, out.Locations())
|
||||||
|
|
||||||
|
// Location of the merged value should be the location of v1.
|
||||||
|
assert.Equal(t, l1, out.Location())
|
||||||
|
|
||||||
|
// Location of the individual values should be preserved.
|
||||||
|
assert.Equal(t, l1, out.Index(0).Location()) // "bar"
|
||||||
|
assert.Equal(t, l1, out.Index(1).Location()) // "baz"
|
||||||
|
assert.Equal(t, l2, out.Index(2).Location()) // "qux"
|
||||||
|
assert.Equal(t, l3, out.Index(3).Location()) // "foo"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge v1 into v2.
|
// Merge v1 into v2.
|
||||||
|
@ -113,6 +181,18 @@ func TestMergeSequences(t *testing.T) {
|
||||||
"bar",
|
"bar",
|
||||||
"baz",
|
"baz",
|
||||||
}, out.AsAny())
|
}, out.AsAny())
|
||||||
|
|
||||||
|
// Locations of both values should be preserved.
|
||||||
|
assert.Equal(t, []dyn.Location{l2, l3, l1}, out.Locations())
|
||||||
|
|
||||||
|
// Location of the merged value should be the location of v2.
|
||||||
|
assert.Equal(t, l2, out.Location())
|
||||||
|
|
||||||
|
// Location of the individual values should be preserved.
|
||||||
|
assert.Equal(t, l2, out.Index(0).Location()) // "qux"
|
||||||
|
assert.Equal(t, l3, out.Index(1).Location()) // "foo"
|
||||||
|
assert.Equal(t, l1, out.Index(2).Location()) // "bar"
|
||||||
|
assert.Equal(t, l1, out.Index(3).Location()) // "baz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,14 +236,22 @@ func TestMergeSequencesError(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMergePrimitives(t *testing.T) {
|
func TestMergePrimitives(t *testing.T) {
|
||||||
v1 := dyn.V("bar")
|
l1 := dyn.Location{File: "file1", Line: 1, Column: 2}
|
||||||
v2 := dyn.V("baz")
|
l2 := dyn.Location{File: "file2", Line: 3, Column: 4}
|
||||||
|
v1 := dyn.NewValue("bar", []dyn.Location{l1})
|
||||||
|
v2 := dyn.NewValue("baz", []dyn.Location{l2})
|
||||||
|
|
||||||
// Merge v2 into v1.
|
// Merge v2 into v1.
|
||||||
{
|
{
|
||||||
out, err := Merge(v1, v2)
|
out, err := Merge(v1, v2)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "baz", out.AsAny())
|
assert.Equal(t, "baz", out.AsAny())
|
||||||
|
|
||||||
|
// Locations of both values should be preserved.
|
||||||
|
assert.Equal(t, []dyn.Location{l2, l1}, out.Locations())
|
||||||
|
|
||||||
|
// Location of the merged value should be the location of v2, the second value.
|
||||||
|
assert.Equal(t, l2, out.Location())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge v1 into v2.
|
// Merge v1 into v2.
|
||||||
|
@ -171,6 +259,12 @@ func TestMergePrimitives(t *testing.T) {
|
||||||
out, err := Merge(v2, v1)
|
out, err := Merge(v2, v1)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "bar", out.AsAny())
|
assert.Equal(t, "bar", out.AsAny())
|
||||||
|
|
||||||
|
// Locations of both values should be preserved.
|
||||||
|
assert.Equal(t, []dyn.Location{l1, l2}, out.Locations())
|
||||||
|
|
||||||
|
// Location of the merged value should be the location of v1, the second value.
|
||||||
|
assert.Equal(t, l1, out.Location())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ func override(basePath dyn.Path, left dyn.Value, right dyn.Value, visitor Overri
|
||||||
return dyn.InvalidValue, err
|
return dyn.InvalidValue, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(merged, left.Location()), nil
|
return dyn.NewValue(merged, left.Locations()), nil
|
||||||
|
|
||||||
case dyn.KindSequence:
|
case dyn.KindSequence:
|
||||||
// some sequences are keyed, and we can detect which elements are added/removed/updated,
|
// some sequences are keyed, and we can detect which elements are added/removed/updated,
|
||||||
|
@ -62,7 +62,7 @@ func override(basePath dyn.Path, left dyn.Value, right dyn.Value, visitor Overri
|
||||||
return dyn.InvalidValue, err
|
return dyn.InvalidValue, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(merged, left.Location()), nil
|
return dyn.NewValue(merged, left.Locations()), nil
|
||||||
|
|
||||||
case dyn.KindString:
|
case dyn.KindString:
|
||||||
if left.MustString() == right.MustString() {
|
if left.MustString() == right.MustString() {
|
||||||
|
|
|
@ -27,79 +27,79 @@ func TestOverride_Primitive(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "string (updated)",
|
name: "string (updated)",
|
||||||
state: visitorState{updated: []string{"root"}},
|
state: visitorState{updated: []string{"root"}},
|
||||||
left: dyn.NewValue("a", leftLocation),
|
left: dyn.NewValue("a", []dyn.Location{leftLocation}),
|
||||||
right: dyn.NewValue("b", rightLocation),
|
right: dyn.NewValue("b", []dyn.Location{rightLocation}),
|
||||||
expected: dyn.NewValue("b", rightLocation),
|
expected: dyn.NewValue("b", []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "string (not updated)",
|
name: "string (not updated)",
|
||||||
state: visitorState{},
|
state: visitorState{},
|
||||||
left: dyn.NewValue("a", leftLocation),
|
left: dyn.NewValue("a", []dyn.Location{leftLocation}),
|
||||||
right: dyn.NewValue("a", rightLocation),
|
right: dyn.NewValue("a", []dyn.Location{rightLocation}),
|
||||||
expected: dyn.NewValue("a", leftLocation),
|
expected: dyn.NewValue("a", []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bool (updated)",
|
name: "bool (updated)",
|
||||||
state: visitorState{updated: []string{"root"}},
|
state: visitorState{updated: []string{"root"}},
|
||||||
left: dyn.NewValue(true, leftLocation),
|
left: dyn.NewValue(true, []dyn.Location{leftLocation}),
|
||||||
right: dyn.NewValue(false, rightLocation),
|
right: dyn.NewValue(false, []dyn.Location{rightLocation}),
|
||||||
expected: dyn.NewValue(false, rightLocation),
|
expected: dyn.NewValue(false, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bool (not updated)",
|
name: "bool (not updated)",
|
||||||
state: visitorState{},
|
state: visitorState{},
|
||||||
left: dyn.NewValue(true, leftLocation),
|
left: dyn.NewValue(true, []dyn.Location{leftLocation}),
|
||||||
right: dyn.NewValue(true, rightLocation),
|
right: dyn.NewValue(true, []dyn.Location{rightLocation}),
|
||||||
expected: dyn.NewValue(true, leftLocation),
|
expected: dyn.NewValue(true, []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "int (updated)",
|
name: "int (updated)",
|
||||||
state: visitorState{updated: []string{"root"}},
|
state: visitorState{updated: []string{"root"}},
|
||||||
left: dyn.NewValue(1, leftLocation),
|
left: dyn.NewValue(1, []dyn.Location{leftLocation}),
|
||||||
right: dyn.NewValue(2, rightLocation),
|
right: dyn.NewValue(2, []dyn.Location{rightLocation}),
|
||||||
expected: dyn.NewValue(2, rightLocation),
|
expected: dyn.NewValue(2, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "int (not updated)",
|
name: "int (not updated)",
|
||||||
state: visitorState{},
|
state: visitorState{},
|
||||||
left: dyn.NewValue(int32(1), leftLocation),
|
left: dyn.NewValue(int32(1), []dyn.Location{leftLocation}),
|
||||||
right: dyn.NewValue(int64(1), rightLocation),
|
right: dyn.NewValue(int64(1), []dyn.Location{rightLocation}),
|
||||||
expected: dyn.NewValue(int32(1), leftLocation),
|
expected: dyn.NewValue(int32(1), []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "float (updated)",
|
name: "float (updated)",
|
||||||
state: visitorState{updated: []string{"root"}},
|
state: visitorState{updated: []string{"root"}},
|
||||||
left: dyn.NewValue(1.0, leftLocation),
|
left: dyn.NewValue(1.0, []dyn.Location{leftLocation}),
|
||||||
right: dyn.NewValue(2.0, rightLocation),
|
right: dyn.NewValue(2.0, []dyn.Location{rightLocation}),
|
||||||
expected: dyn.NewValue(2.0, rightLocation),
|
expected: dyn.NewValue(2.0, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "float (not updated)",
|
name: "float (not updated)",
|
||||||
state: visitorState{},
|
state: visitorState{},
|
||||||
left: dyn.NewValue(float32(1.0), leftLocation),
|
left: dyn.NewValue(float32(1.0), []dyn.Location{leftLocation}),
|
||||||
right: dyn.NewValue(float64(1.0), rightLocation),
|
right: dyn.NewValue(float64(1.0), []dyn.Location{rightLocation}),
|
||||||
expected: dyn.NewValue(float32(1.0), leftLocation),
|
expected: dyn.NewValue(float32(1.0), []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "time (updated)",
|
name: "time (updated)",
|
||||||
state: visitorState{updated: []string{"root"}},
|
state: visitorState{updated: []string{"root"}},
|
||||||
left: dyn.NewValue(time.UnixMilli(10000), leftLocation),
|
left: dyn.NewValue(time.UnixMilli(10000), []dyn.Location{leftLocation}),
|
||||||
right: dyn.NewValue(time.UnixMilli(10001), rightLocation),
|
right: dyn.NewValue(time.UnixMilli(10001), []dyn.Location{rightLocation}),
|
||||||
expected: dyn.NewValue(time.UnixMilli(10001), rightLocation),
|
expected: dyn.NewValue(time.UnixMilli(10001), []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "time (not updated)",
|
name: "time (not updated)",
|
||||||
state: visitorState{},
|
state: visitorState{},
|
||||||
left: dyn.NewValue(time.UnixMilli(10000), leftLocation),
|
left: dyn.NewValue(time.UnixMilli(10000), []dyn.Location{leftLocation}),
|
||||||
right: dyn.NewValue(time.UnixMilli(10000), rightLocation),
|
right: dyn.NewValue(time.UnixMilli(10000), []dyn.Location{rightLocation}),
|
||||||
expected: dyn.NewValue(time.UnixMilli(10000), leftLocation),
|
expected: dyn.NewValue(time.UnixMilli(10000), []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "different types (updated)",
|
name: "different types (updated)",
|
||||||
state: visitorState{updated: []string{"root"}},
|
state: visitorState{updated: []string{"root"}},
|
||||||
left: dyn.NewValue("a", leftLocation),
|
left: dyn.NewValue("a", []dyn.Location{leftLocation}),
|
||||||
right: dyn.NewValue(42, rightLocation),
|
right: dyn.NewValue(42, []dyn.Location{rightLocation}),
|
||||||
expected: dyn.NewValue(42, rightLocation),
|
expected: dyn.NewValue(42, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "map - remove 'a', update 'b'",
|
name: "map - remove 'a', update 'b'",
|
||||||
|
@ -109,23 +109,22 @@ func TestOverride_Primitive(t *testing.T) {
|
||||||
},
|
},
|
||||||
left: dyn.NewValue(
|
left: dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"a": dyn.NewValue(42, leftLocation),
|
"a": dyn.NewValue(42, []dyn.Location{leftLocation}),
|
||||||
"b": dyn.NewValue(10, leftLocation),
|
"b": dyn.NewValue(10, []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation}),
|
||||||
),
|
|
||||||
right: dyn.NewValue(
|
right: dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"b": dyn.NewValue(20, rightLocation),
|
"b": dyn.NewValue(20, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
rightLocation,
|
[]dyn.Location{rightLocation}),
|
||||||
),
|
|
||||||
expected: dyn.NewValue(
|
expected: dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"b": dyn.NewValue(20, rightLocation),
|
"b": dyn.NewValue(20, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation}),
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "map - add 'a'",
|
name: "map - add 'a'",
|
||||||
|
@ -134,24 +133,26 @@ func TestOverride_Primitive(t *testing.T) {
|
||||||
},
|
},
|
||||||
left: dyn.NewValue(
|
left: dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"b": dyn.NewValue(10, leftLocation),
|
"b": dyn.NewValue(10, []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
|
|
||||||
right: dyn.NewValue(
|
right: dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"a": dyn.NewValue(42, rightLocation),
|
"a": dyn.NewValue(42, []dyn.Location{rightLocation}),
|
||||||
"b": dyn.NewValue(10, rightLocation),
|
"b": dyn.NewValue(10, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
|
|
||||||
expected: dyn.NewValue(
|
expected: dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"a": dyn.NewValue(42, rightLocation),
|
"a": dyn.NewValue(42, []dyn.Location{rightLocation}),
|
||||||
// location hasn't changed because value hasn't changed
|
// location hasn't changed because value hasn't changed
|
||||||
"b": dyn.NewValue(10, leftLocation),
|
"b": dyn.NewValue(10, []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -161,23 +162,25 @@ func TestOverride_Primitive(t *testing.T) {
|
||||||
},
|
},
|
||||||
left: dyn.NewValue(
|
left: dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"a": dyn.NewValue(42, leftLocation),
|
"a": dyn.NewValue(42, []dyn.Location{leftLocation}),
|
||||||
"b": dyn.NewValue(10, leftLocation),
|
"b": dyn.NewValue(10, []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
|
|
||||||
right: dyn.NewValue(
|
right: dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"b": dyn.NewValue(10, rightLocation),
|
"b": dyn.NewValue(10, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
|
|
||||||
expected: dyn.NewValue(
|
expected: dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
// location hasn't changed because value hasn't changed
|
// location hasn't changed because value hasn't changed
|
||||||
"b": dyn.NewValue(10, leftLocation),
|
"b": dyn.NewValue(10, []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -189,36 +192,38 @@ func TestOverride_Primitive(t *testing.T) {
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"jobs": dyn.NewValue(
|
"jobs": dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"job_0": dyn.NewValue(42, leftLocation),
|
"job_0": dyn.NewValue(42, []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
|
|
||||||
right: dyn.NewValue(
|
right: dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"jobs": dyn.NewValue(
|
"jobs": dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"job_0": dyn.NewValue(42, rightLocation),
|
"job_0": dyn.NewValue(42, []dyn.Location{rightLocation}),
|
||||||
"job_1": dyn.NewValue(1337, rightLocation),
|
"job_1": dyn.NewValue(1337, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
rightLocation,
|
[]dyn.Location{rightLocation},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
rightLocation,
|
[]dyn.Location{rightLocation},
|
||||||
),
|
),
|
||||||
|
|
||||||
expected: dyn.NewValue(
|
expected: dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"jobs": dyn.NewValue(
|
"jobs": dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"job_0": dyn.NewValue(42, leftLocation),
|
"job_0": dyn.NewValue(42, []dyn.Location{leftLocation}),
|
||||||
"job_1": dyn.NewValue(1337, rightLocation),
|
"job_1": dyn.NewValue(1337, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -228,35 +233,35 @@ func TestOverride_Primitive(t *testing.T) {
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"jobs": dyn.NewValue(
|
"jobs": dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"job_0": dyn.NewValue(42, leftLocation),
|
"job_0": dyn.NewValue(42, []dyn.Location{leftLocation}),
|
||||||
"job_1": dyn.NewValue(1337, rightLocation),
|
"job_1": dyn.NewValue(1337, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
right: dyn.NewValue(
|
right: dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"jobs": dyn.NewValue(
|
"jobs": dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"job_0": dyn.NewValue(42, rightLocation),
|
"job_0": dyn.NewValue(42, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
rightLocation,
|
[]dyn.Location{rightLocation},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
rightLocation,
|
[]dyn.Location{rightLocation},
|
||||||
),
|
),
|
||||||
expected: dyn.NewValue(
|
expected: dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"jobs": dyn.NewValue(
|
"jobs": dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"job_0": dyn.NewValue(42, leftLocation),
|
"job_0": dyn.NewValue(42, []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -264,23 +269,23 @@ func TestOverride_Primitive(t *testing.T) {
|
||||||
state: visitorState{added: []string{"root[1]"}},
|
state: visitorState{added: []string{"root[1]"}},
|
||||||
left: dyn.NewValue(
|
left: dyn.NewValue(
|
||||||
[]dyn.Value{
|
[]dyn.Value{
|
||||||
dyn.NewValue(42, leftLocation),
|
dyn.NewValue(42, []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
right: dyn.NewValue(
|
right: dyn.NewValue(
|
||||||
[]dyn.Value{
|
[]dyn.Value{
|
||||||
dyn.NewValue(42, rightLocation),
|
dyn.NewValue(42, []dyn.Location{rightLocation}),
|
||||||
dyn.NewValue(10, rightLocation),
|
dyn.NewValue(10, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
rightLocation,
|
[]dyn.Location{rightLocation},
|
||||||
),
|
),
|
||||||
expected: dyn.NewValue(
|
expected: dyn.NewValue(
|
||||||
[]dyn.Value{
|
[]dyn.Value{
|
||||||
dyn.NewValue(42, leftLocation),
|
dyn.NewValue(42, []dyn.Location{leftLocation}),
|
||||||
dyn.NewValue(10, rightLocation),
|
dyn.NewValue(10, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -288,67 +293,67 @@ func TestOverride_Primitive(t *testing.T) {
|
||||||
state: visitorState{removed: []string{"root[1]"}},
|
state: visitorState{removed: []string{"root[1]"}},
|
||||||
left: dyn.NewValue(
|
left: dyn.NewValue(
|
||||||
[]dyn.Value{
|
[]dyn.Value{
|
||||||
dyn.NewValue(42, leftLocation),
|
dyn.NewValue(42, []dyn.Location{leftLocation}),
|
||||||
dyn.NewValue(10, leftLocation),
|
dyn.NewValue(10, []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
right: dyn.NewValue(
|
right: dyn.NewValue(
|
||||||
[]dyn.Value{
|
[]dyn.Value{
|
||||||
dyn.NewValue(42, rightLocation),
|
dyn.NewValue(42, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
rightLocation,
|
[]dyn.Location{rightLocation},
|
||||||
),
|
),
|
||||||
expected: dyn.NewValue(
|
expected: dyn.NewValue(
|
||||||
[]dyn.Value{
|
[]dyn.Value{
|
||||||
// location hasn't changed because value hasn't changed
|
dyn.NewValue(42, []dyn.Location{leftLocation}),
|
||||||
dyn.NewValue(42, leftLocation),
|
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
|
// location hasn't changed because value hasn't changed
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "sequence (not updated)",
|
name: "sequence (not updated)",
|
||||||
state: visitorState{},
|
state: visitorState{},
|
||||||
left: dyn.NewValue(
|
left: dyn.NewValue(
|
||||||
[]dyn.Value{
|
[]dyn.Value{
|
||||||
dyn.NewValue(42, leftLocation),
|
dyn.NewValue(42, []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
right: dyn.NewValue(
|
right: dyn.NewValue(
|
||||||
[]dyn.Value{
|
[]dyn.Value{
|
||||||
dyn.NewValue(42, rightLocation),
|
dyn.NewValue(42, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
rightLocation,
|
[]dyn.Location{rightLocation},
|
||||||
),
|
),
|
||||||
expected: dyn.NewValue(
|
expected: dyn.NewValue(
|
||||||
[]dyn.Value{
|
[]dyn.Value{
|
||||||
dyn.NewValue(42, leftLocation),
|
dyn.NewValue(42, []dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
leftLocation,
|
[]dyn.Location{leftLocation},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nil (not updated)",
|
name: "nil (not updated)",
|
||||||
state: visitorState{},
|
state: visitorState{},
|
||||||
left: dyn.NilValue.WithLocation(leftLocation),
|
left: dyn.NilValue.WithLocations([]dyn.Location{leftLocation}),
|
||||||
right: dyn.NilValue.WithLocation(rightLocation),
|
right: dyn.NilValue.WithLocations([]dyn.Location{rightLocation}),
|
||||||
expected: dyn.NilValue.WithLocation(leftLocation),
|
expected: dyn.NilValue.WithLocations([]dyn.Location{leftLocation}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nil (updated)",
|
name: "nil (updated)",
|
||||||
state: visitorState{updated: []string{"root"}},
|
state: visitorState{updated: []string{"root"}},
|
||||||
left: dyn.NilValue,
|
left: dyn.NilValue,
|
||||||
right: dyn.NewValue(42, rightLocation),
|
right: dyn.NewValue(42, []dyn.Location{rightLocation}),
|
||||||
expected: dyn.NewValue(42, rightLocation),
|
expected: dyn.NewValue(42, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "change kind (updated)",
|
name: "change kind (updated)",
|
||||||
state: visitorState{updated: []string{"root"}},
|
state: visitorState{updated: []string{"root"}},
|
||||||
left: dyn.NewValue(42.0, leftLocation),
|
left: dyn.NewValue(42.0, []dyn.Location{leftLocation}),
|
||||||
right: dyn.NewValue(42, rightLocation),
|
right: dyn.NewValue(42, []dyn.Location{rightLocation}),
|
||||||
expected: dyn.NewValue(42, rightLocation),
|
expected: dyn.NewValue(42, []dyn.Location{rightLocation}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +380,7 @@ func TestOverride_Primitive(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run(tc.name+" - visitor overrides value", func(t *testing.T) {
|
t.Run(tc.name+" - visitor overrides value", func(t *testing.T) {
|
||||||
expected := dyn.NewValue("return value", dyn.Location{})
|
expected := dyn.V("return value")
|
||||||
s, visitor := createVisitor(visitorOpts{returnValue: &expected})
|
s, visitor := createVisitor(visitorOpts{returnValue: &expected})
|
||||||
out, err := override(dyn.EmptyPath, tc.left, tc.right, visitor)
|
out, err := override(dyn.EmptyPath, tc.left, tc.right, visitor)
|
||||||
|
|
||||||
|
@ -427,17 +432,17 @@ func TestOverride_PreserveMappingKeys(t *testing.T) {
|
||||||
rightValueLocation := dyn.Location{File: "right.yml", Line: 3, Column: 1}
|
rightValueLocation := dyn.Location{File: "right.yml", Line: 3, Column: 1}
|
||||||
|
|
||||||
left := dyn.NewMapping()
|
left := dyn.NewMapping()
|
||||||
left.Set(dyn.NewValue("a", leftKeyLocation), dyn.NewValue(42, leftValueLocation))
|
left.Set(dyn.NewValue("a", []dyn.Location{leftKeyLocation}), dyn.NewValue(42, []dyn.Location{leftValueLocation}))
|
||||||
|
|
||||||
right := dyn.NewMapping()
|
right := dyn.NewMapping()
|
||||||
right.Set(dyn.NewValue("a", rightKeyLocation), dyn.NewValue(7, rightValueLocation))
|
right.Set(dyn.NewValue("a", []dyn.Location{rightKeyLocation}), dyn.NewValue(7, []dyn.Location{rightValueLocation}))
|
||||||
|
|
||||||
state, visitor := createVisitor(visitorOpts{})
|
state, visitor := createVisitor(visitorOpts{})
|
||||||
|
|
||||||
out, err := override(
|
out, err := override(
|
||||||
dyn.EmptyPath,
|
dyn.EmptyPath,
|
||||||
dyn.NewValue(left, leftLocation),
|
dyn.NewValue(left, []dyn.Location{leftLocation}),
|
||||||
dyn.NewValue(right, rightLocation),
|
dyn.NewValue(right, []dyn.Location{rightLocation}),
|
||||||
visitor,
|
visitor,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ func (c anyKeyComponent) visit(v Value, prefix Path, suffix Pattern, opts visitO
|
||||||
m.Set(pk, nv)
|
m.Set(pk, nv)
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewValue(m, v.Location()), nil
|
return NewValue(m, v.Locations()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type anyIndexComponent struct{}
|
type anyIndexComponent struct{}
|
||||||
|
@ -103,5 +103,5 @@ func (c anyIndexComponent) visit(v Value, prefix Path, suffix Pattern, opts visi
|
||||||
s[i] = nv
|
s[i] = nv
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewValue(s, v.Location()), nil
|
return NewValue(s, v.Locations()), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,18 @@ package dyn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Value struct {
|
type Value struct {
|
||||||
v any
|
v any
|
||||||
|
|
||||||
k Kind
|
k Kind
|
||||||
l Location
|
|
||||||
|
// List of locations this value is defined at. The first location in the slice
|
||||||
|
// is the location returned by the `.Location()` method and is typically used
|
||||||
|
// for reporting errors and warnings associated with the value.
|
||||||
|
l []Location
|
||||||
|
|
||||||
// Whether or not this value is an anchor.
|
// Whether or not this value is an anchor.
|
||||||
// If this node doesn't map to a type, we don't need to warn about it.
|
// If this node doesn't map to a type, we don't need to warn about it.
|
||||||
|
@ -27,11 +32,11 @@ var NilValue = Value{
|
||||||
|
|
||||||
// V constructs a new Value with the given value.
|
// V constructs a new Value with the given value.
|
||||||
func V(v any) Value {
|
func V(v any) Value {
|
||||||
return NewValue(v, Location{})
|
return NewValue(v, []Location{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewValue constructs a new Value with the given value and location.
|
// NewValue constructs a new Value with the given value and location.
|
||||||
func NewValue(v any, loc Location) Value {
|
func NewValue(v any, loc []Location) Value {
|
||||||
switch vin := v.(type) {
|
switch vin := v.(type) {
|
||||||
case map[string]Value:
|
case map[string]Value:
|
||||||
v = newMappingFromGoMap(vin)
|
v = newMappingFromGoMap(vin)
|
||||||
|
@ -40,16 +45,30 @@ func NewValue(v any, loc Location) Value {
|
||||||
return Value{
|
return Value{
|
||||||
v: v,
|
v: v,
|
||||||
k: kindOf(v),
|
k: kindOf(v),
|
||||||
l: loc,
|
|
||||||
|
// create a copy of the locations, so that mutations to the original slice
|
||||||
|
// don't affect new value.
|
||||||
|
l: slices.Clone(loc),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithLocation returns a new Value with its location set to the given value.
|
// WithLocations returns a new Value with its location set to the given value.
|
||||||
func (v Value) WithLocation(loc Location) Value {
|
func (v Value) WithLocations(loc []Location) Value {
|
||||||
return Value{
|
return Value{
|
||||||
v: v.v,
|
v: v.v,
|
||||||
k: v.k,
|
k: v.k,
|
||||||
l: loc,
|
|
||||||
|
// create a copy of the locations, so that mutations to the original slice
|
||||||
|
// don't affect new value.
|
||||||
|
l: slices.Clone(loc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Value) AppendLocationsFromValue(w Value) Value {
|
||||||
|
return Value{
|
||||||
|
v: v.v,
|
||||||
|
k: v.k,
|
||||||
|
l: append(v.l, w.l...),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,10 +80,18 @@ func (v Value) Value() any {
|
||||||
return v.v
|
return v.v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) Location() Location {
|
func (v Value) Locations() []Location {
|
||||||
return v.l
|
return v.l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v Value) Location() Location {
|
||||||
|
if len(v.l) == 0 {
|
||||||
|
return Location{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.l[0]
|
||||||
|
}
|
||||||
|
|
||||||
func (v Value) IsValid() bool {
|
func (v Value) IsValid() bool {
|
||||||
return v.k != KindInvalid
|
return v.k != KindInvalid
|
||||||
}
|
}
|
||||||
|
@ -153,7 +180,10 @@ func (v Value) IsAnchor() bool {
|
||||||
// We need a custom implementation because maps and slices
|
// We need a custom implementation because maps and slices
|
||||||
// cannot be compared with the regular == operator.
|
// cannot be compared with the regular == operator.
|
||||||
func (v Value) eq(w Value) bool {
|
func (v Value) eq(w Value) bool {
|
||||||
if v.k != w.k || v.l != w.l {
|
if v.k != w.k {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !slices.Equal(v.l, w.l) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,16 +25,19 @@ func TestValueAsMap(t *testing.T) {
|
||||||
_, ok := zeroValue.AsMap()
|
_, ok := zeroValue.AsMap()
|
||||||
assert.False(t, ok)
|
assert.False(t, ok)
|
||||||
|
|
||||||
var intValue = dyn.NewValue(1, dyn.Location{})
|
var intValue = dyn.V(1)
|
||||||
_, ok = intValue.AsMap()
|
_, ok = intValue.AsMap()
|
||||||
assert.False(t, ok)
|
assert.False(t, ok)
|
||||||
|
|
||||||
var mapValue = dyn.NewValue(
|
var mapValue = dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"key": dyn.NewValue("value", dyn.Location{File: "file", Line: 1, Column: 2}),
|
"key": dyn.NewValue(
|
||||||
|
"value",
|
||||||
|
[]dyn.Location{{File: "file", Line: 1, Column: 2}}),
|
||||||
},
|
},
|
||||||
dyn.Location{File: "file", Line: 1, Column: 2},
|
[]dyn.Location{{File: "file", Line: 1, Column: 2}},
|
||||||
)
|
)
|
||||||
|
|
||||||
m, ok := mapValue.AsMap()
|
m, ok := mapValue.AsMap()
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
assert.Equal(t, 1, m.Len())
|
assert.Equal(t, 1, m.Len())
|
||||||
|
@ -43,6 +46,6 @@ func TestValueAsMap(t *testing.T) {
|
||||||
func TestValueIsValid(t *testing.T) {
|
func TestValueIsValid(t *testing.T) {
|
||||||
var zeroValue dyn.Value
|
var zeroValue dyn.Value
|
||||||
assert.False(t, zeroValue.IsValid())
|
assert.False(t, zeroValue.IsValid())
|
||||||
var intValue = dyn.NewValue(1, dyn.Location{})
|
var intValue = dyn.V(1)
|
||||||
assert.True(t, intValue.IsValid())
|
assert.True(t, intValue.IsValid())
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
func TestValueUnderlyingMap(t *testing.T) {
|
func TestValueUnderlyingMap(t *testing.T) {
|
||||||
v := dyn.V(
|
v := dyn.V(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"key": dyn.NewValue("value", dyn.Location{File: "file", Line: 1, Column: 2}),
|
"key": dyn.NewValue("value", []dyn.Location{{File: "file", Line: 1, Column: 2}}),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ func TestValueUnderlyingMap(t *testing.T) {
|
||||||
func TestValueUnderlyingSequence(t *testing.T) {
|
func TestValueUnderlyingSequence(t *testing.T) {
|
||||||
v := dyn.V(
|
v := dyn.V(
|
||||||
[]dyn.Value{
|
[]dyn.Value{
|
||||||
dyn.NewValue("value", dyn.Location{File: "file", Line: 1, Column: 2}),
|
dyn.NewValue("value", []dyn.Location{{File: "file", Line: 1, Column: 2}}),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ func Foreach(fn MapFunc) MapFunc {
|
||||||
}
|
}
|
||||||
m.Set(pk, nv)
|
m.Set(pk, nv)
|
||||||
}
|
}
|
||||||
return NewValue(m, v.Location()), nil
|
return NewValue(m, v.Locations()), nil
|
||||||
case KindSequence:
|
case KindSequence:
|
||||||
s := slices.Clone(v.MustSequence())
|
s := slices.Clone(v.MustSequence())
|
||||||
for i, value := range s {
|
for i, value := range s {
|
||||||
|
@ -37,7 +37,7 @@ func Foreach(fn MapFunc) MapFunc {
|
||||||
return InvalidValue, err
|
return InvalidValue, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NewValue(s, v.Location()), nil
|
return NewValue(s, v.Locations()), nil
|
||||||
default:
|
default:
|
||||||
return InvalidValue, fmt.Errorf("expected a map or sequence, found %s", v.Kind())
|
return InvalidValue, fmt.Errorf("expected a map or sequence, found %s", v.Kind())
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ func (d *loader) loadSequence(node *yaml.Node, loc dyn.Location) (dyn.Value, err
|
||||||
acc[i] = v
|
acc[i] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(acc, loc), nil
|
return dyn.NewValue(acc, []dyn.Location{loc}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *loader) loadMapping(node *yaml.Node, loc dyn.Location) (dyn.Value, error) {
|
func (d *loader) loadMapping(node *yaml.Node, loc dyn.Location) (dyn.Value, error) {
|
||||||
|
@ -130,7 +130,7 @@ func (d *loader) loadMapping(node *yaml.Node, loc dyn.Location) (dyn.Value, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
if merge == nil {
|
if merge == nil {
|
||||||
return dyn.NewValue(acc, loc), nil
|
return dyn.NewValue(acc, []dyn.Location{loc}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build location for the merge node.
|
// Build location for the merge node.
|
||||||
|
@ -171,20 +171,20 @@ func (d *loader) loadMapping(node *yaml.Node, loc dyn.Location) (dyn.Value, erro
|
||||||
out.Merge(m)
|
out.Merge(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.NewValue(out, loc), nil
|
return dyn.NewValue(out, []dyn.Location{loc}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *loader) loadScalar(node *yaml.Node, loc dyn.Location) (dyn.Value, error) {
|
func (d *loader) loadScalar(node *yaml.Node, loc dyn.Location) (dyn.Value, error) {
|
||||||
st := node.ShortTag()
|
st := node.ShortTag()
|
||||||
switch st {
|
switch st {
|
||||||
case "!!str":
|
case "!!str":
|
||||||
return dyn.NewValue(node.Value, loc), nil
|
return dyn.NewValue(node.Value, []dyn.Location{loc}), nil
|
||||||
case "!!bool":
|
case "!!bool":
|
||||||
switch strings.ToLower(node.Value) {
|
switch strings.ToLower(node.Value) {
|
||||||
case "true":
|
case "true":
|
||||||
return dyn.NewValue(true, loc), nil
|
return dyn.NewValue(true, []dyn.Location{loc}), nil
|
||||||
case "false":
|
case "false":
|
||||||
return dyn.NewValue(false, loc), nil
|
return dyn.NewValue(false, []dyn.Location{loc}), nil
|
||||||
default:
|
default:
|
||||||
return dyn.InvalidValue, errorf(loc, "invalid bool value: %v", node.Value)
|
return dyn.InvalidValue, errorf(loc, "invalid bool value: %v", node.Value)
|
||||||
}
|
}
|
||||||
|
@ -195,17 +195,17 @@ func (d *loader) loadScalar(node *yaml.Node, loc dyn.Location) (dyn.Value, error
|
||||||
}
|
}
|
||||||
// Use regular int type instead of int64 if possible.
|
// Use regular int type instead of int64 if possible.
|
||||||
if i64 >= math.MinInt32 && i64 <= math.MaxInt32 {
|
if i64 >= math.MinInt32 && i64 <= math.MaxInt32 {
|
||||||
return dyn.NewValue(int(i64), loc), nil
|
return dyn.NewValue(int(i64), []dyn.Location{loc}), nil
|
||||||
}
|
}
|
||||||
return dyn.NewValue(i64, loc), nil
|
return dyn.NewValue(i64, []dyn.Location{loc}), nil
|
||||||
case "!!float":
|
case "!!float":
|
||||||
f64, err := strconv.ParseFloat(node.Value, 64)
|
f64, err := strconv.ParseFloat(node.Value, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dyn.InvalidValue, errorf(loc, "invalid float value: %v", node.Value)
|
return dyn.InvalidValue, errorf(loc, "invalid float value: %v", node.Value)
|
||||||
}
|
}
|
||||||
return dyn.NewValue(f64, loc), nil
|
return dyn.NewValue(f64, []dyn.Location{loc}), nil
|
||||||
case "!!null":
|
case "!!null":
|
||||||
return dyn.NewValue(nil, loc), nil
|
return dyn.NewValue(nil, []dyn.Location{loc}), nil
|
||||||
case "!!timestamp":
|
case "!!timestamp":
|
||||||
// Try a couple of layouts
|
// Try a couple of layouts
|
||||||
for _, layout := range []string{
|
for _, layout := range []string{
|
||||||
|
@ -216,7 +216,7 @@ func (d *loader) loadScalar(node *yaml.Node, loc dyn.Location) (dyn.Value, error
|
||||||
} {
|
} {
|
||||||
t, terr := time.Parse(layout, node.Value)
|
t, terr := time.Parse(layout, node.Value)
|
||||||
if terr == nil {
|
if terr == nil {
|
||||||
return dyn.NewValue(t, loc), nil
|
return dyn.NewValue(t, []dyn.Location{loc}), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dyn.InvalidValue, errorf(loc, "invalid timestamp value: %v", node.Value)
|
return dyn.InvalidValue, errorf(loc, "invalid timestamp value: %v", node.Value)
|
||||||
|
|
|
@ -19,7 +19,7 @@ func TestMarshalNilValue(t *testing.T) {
|
||||||
|
|
||||||
func TestMarshalIntValue(t *testing.T) {
|
func TestMarshalIntValue(t *testing.T) {
|
||||||
s := NewSaver()
|
s := NewSaver()
|
||||||
var intValue = dyn.NewValue(1, dyn.Location{})
|
var intValue = dyn.V(1)
|
||||||
v, err := s.toYamlNode(intValue)
|
v, err := s.toYamlNode(intValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "1", v.Value)
|
assert.Equal(t, "1", v.Value)
|
||||||
|
@ -28,7 +28,7 @@ func TestMarshalIntValue(t *testing.T) {
|
||||||
|
|
||||||
func TestMarshalFloatValue(t *testing.T) {
|
func TestMarshalFloatValue(t *testing.T) {
|
||||||
s := NewSaver()
|
s := NewSaver()
|
||||||
var floatValue = dyn.NewValue(1.0, dyn.Location{})
|
var floatValue = dyn.V(1.0)
|
||||||
v, err := s.toYamlNode(floatValue)
|
v, err := s.toYamlNode(floatValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "1", v.Value)
|
assert.Equal(t, "1", v.Value)
|
||||||
|
@ -37,7 +37,7 @@ func TestMarshalFloatValue(t *testing.T) {
|
||||||
|
|
||||||
func TestMarshalBoolValue(t *testing.T) {
|
func TestMarshalBoolValue(t *testing.T) {
|
||||||
s := NewSaver()
|
s := NewSaver()
|
||||||
var boolValue = dyn.NewValue(true, dyn.Location{})
|
var boolValue = dyn.V(true)
|
||||||
v, err := s.toYamlNode(boolValue)
|
v, err := s.toYamlNode(boolValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "true", v.Value)
|
assert.Equal(t, "true", v.Value)
|
||||||
|
@ -46,7 +46,7 @@ func TestMarshalBoolValue(t *testing.T) {
|
||||||
|
|
||||||
func TestMarshalTimeValue(t *testing.T) {
|
func TestMarshalTimeValue(t *testing.T) {
|
||||||
s := NewSaver()
|
s := NewSaver()
|
||||||
var timeValue = dyn.NewValue(time.Unix(0, 0), dyn.Location{})
|
var timeValue = dyn.V(time.Unix(0, 0))
|
||||||
v, err := s.toYamlNode(timeValue)
|
v, err := s.toYamlNode(timeValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "1970-01-01 00:00:00 +0000 UTC", v.Value)
|
assert.Equal(t, "1970-01-01 00:00:00 +0000 UTC", v.Value)
|
||||||
|
@ -57,10 +57,10 @@ func TestMarshalSequenceValue(t *testing.T) {
|
||||||
s := NewSaver()
|
s := NewSaver()
|
||||||
var sequenceValue = dyn.NewValue(
|
var sequenceValue = dyn.NewValue(
|
||||||
[]dyn.Value{
|
[]dyn.Value{
|
||||||
dyn.NewValue("value1", dyn.Location{File: "file", Line: 1, Column: 2}),
|
dyn.NewValue("value1", []dyn.Location{{File: "file", Line: 1, Column: 2}}),
|
||||||
dyn.NewValue("value2", dyn.Location{File: "file", Line: 2, Column: 2}),
|
dyn.NewValue("value2", []dyn.Location{{File: "file", Line: 2, Column: 2}}),
|
||||||
},
|
},
|
||||||
dyn.Location{File: "file", Line: 1, Column: 2},
|
[]dyn.Location{{File: "file", Line: 1, Column: 2}},
|
||||||
)
|
)
|
||||||
v, err := s.toYamlNode(sequenceValue)
|
v, err := s.toYamlNode(sequenceValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -71,7 +71,7 @@ func TestMarshalSequenceValue(t *testing.T) {
|
||||||
|
|
||||||
func TestMarshalStringValue(t *testing.T) {
|
func TestMarshalStringValue(t *testing.T) {
|
||||||
s := NewSaver()
|
s := NewSaver()
|
||||||
var stringValue = dyn.NewValue("value", dyn.Location{})
|
var stringValue = dyn.V("value")
|
||||||
v, err := s.toYamlNode(stringValue)
|
v, err := s.toYamlNode(stringValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "value", v.Value)
|
assert.Equal(t, "value", v.Value)
|
||||||
|
@ -82,12 +82,13 @@ func TestMarshalMapValue(t *testing.T) {
|
||||||
s := NewSaver()
|
s := NewSaver()
|
||||||
var mapValue = dyn.NewValue(
|
var mapValue = dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"key3": dyn.NewValue("value3", dyn.Location{File: "file", Line: 3, Column: 2}),
|
"key3": dyn.NewValue("value3", []dyn.Location{{File: "file", Line: 3, Column: 2}}),
|
||||||
"key2": dyn.NewValue("value2", dyn.Location{File: "file", Line: 2, Column: 2}),
|
"key2": dyn.NewValue("value2", []dyn.Location{{File: "file", Line: 2, Column: 2}}),
|
||||||
"key1": dyn.NewValue("value1", dyn.Location{File: "file", Line: 1, Column: 2}),
|
"key1": dyn.NewValue("value1", []dyn.Location{{File: "file", Line: 1, Column: 2}}),
|
||||||
},
|
},
|
||||||
dyn.Location{File: "file", Line: 1, Column: 2},
|
[]dyn.Location{{File: "file", Line: 1, Column: 2}},
|
||||||
)
|
)
|
||||||
|
|
||||||
v, err := s.toYamlNode(mapValue)
|
v, err := s.toYamlNode(mapValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, yaml.MappingNode, v.Kind)
|
assert.Equal(t, yaml.MappingNode, v.Kind)
|
||||||
|
@ -107,12 +108,12 @@ func TestMarshalNestedValues(t *testing.T) {
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"key1": dyn.NewValue(
|
"key1": dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"key2": dyn.NewValue("value", dyn.Location{File: "file", Line: 1, Column: 2}),
|
"key2": dyn.NewValue("value", []dyn.Location{{File: "file", Line: 1, Column: 2}}),
|
||||||
},
|
},
|
||||||
dyn.Location{File: "file", Line: 1, Column: 2},
|
[]dyn.Location{{File: "file", Line: 1, Column: 2}},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
dyn.Location{File: "file", Line: 1, Column: 2},
|
[]dyn.Location{{File: "file", Line: 1, Column: 2}},
|
||||||
)
|
)
|
||||||
v, err := s.toYamlNode(mapValue)
|
v, err := s.toYamlNode(mapValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -125,14 +126,14 @@ func TestMarshalNestedValues(t *testing.T) {
|
||||||
|
|
||||||
func TestMarshalHexadecimalValueIsQuoted(t *testing.T) {
|
func TestMarshalHexadecimalValueIsQuoted(t *testing.T) {
|
||||||
s := NewSaver()
|
s := NewSaver()
|
||||||
var hexValue = dyn.NewValue(0x123, dyn.Location{})
|
var hexValue = dyn.V(0x123)
|
||||||
v, err := s.toYamlNode(hexValue)
|
v, err := s.toYamlNode(hexValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "291", v.Value)
|
assert.Equal(t, "291", v.Value)
|
||||||
assert.Equal(t, yaml.Style(0), v.Style)
|
assert.Equal(t, yaml.Style(0), v.Style)
|
||||||
assert.Equal(t, yaml.ScalarNode, v.Kind)
|
assert.Equal(t, yaml.ScalarNode, v.Kind)
|
||||||
|
|
||||||
var stringValue = dyn.NewValue("0x123", dyn.Location{})
|
var stringValue = dyn.V("0x123")
|
||||||
v, err = s.toYamlNode(stringValue)
|
v, err = s.toYamlNode(stringValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "0x123", v.Value)
|
assert.Equal(t, "0x123", v.Value)
|
||||||
|
@ -142,14 +143,14 @@ func TestMarshalHexadecimalValueIsQuoted(t *testing.T) {
|
||||||
|
|
||||||
func TestMarshalBinaryValueIsQuoted(t *testing.T) {
|
func TestMarshalBinaryValueIsQuoted(t *testing.T) {
|
||||||
s := NewSaver()
|
s := NewSaver()
|
||||||
var binaryValue = dyn.NewValue(0b101, dyn.Location{})
|
var binaryValue = dyn.V(0b101)
|
||||||
v, err := s.toYamlNode(binaryValue)
|
v, err := s.toYamlNode(binaryValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "5", v.Value)
|
assert.Equal(t, "5", v.Value)
|
||||||
assert.Equal(t, yaml.Style(0), v.Style)
|
assert.Equal(t, yaml.Style(0), v.Style)
|
||||||
assert.Equal(t, yaml.ScalarNode, v.Kind)
|
assert.Equal(t, yaml.ScalarNode, v.Kind)
|
||||||
|
|
||||||
var stringValue = dyn.NewValue("0b101", dyn.Location{})
|
var stringValue = dyn.V("0b101")
|
||||||
v, err = s.toYamlNode(stringValue)
|
v, err = s.toYamlNode(stringValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "0b101", v.Value)
|
assert.Equal(t, "0b101", v.Value)
|
||||||
|
@ -159,14 +160,14 @@ func TestMarshalBinaryValueIsQuoted(t *testing.T) {
|
||||||
|
|
||||||
func TestMarshalOctalValueIsQuoted(t *testing.T) {
|
func TestMarshalOctalValueIsQuoted(t *testing.T) {
|
||||||
s := NewSaver()
|
s := NewSaver()
|
||||||
var octalValue = dyn.NewValue(0123, dyn.Location{})
|
var octalValue = dyn.V(0123)
|
||||||
v, err := s.toYamlNode(octalValue)
|
v, err := s.toYamlNode(octalValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "83", v.Value)
|
assert.Equal(t, "83", v.Value)
|
||||||
assert.Equal(t, yaml.Style(0), v.Style)
|
assert.Equal(t, yaml.Style(0), v.Style)
|
||||||
assert.Equal(t, yaml.ScalarNode, v.Kind)
|
assert.Equal(t, yaml.ScalarNode, v.Kind)
|
||||||
|
|
||||||
var stringValue = dyn.NewValue("0123", dyn.Location{})
|
var stringValue = dyn.V("0123")
|
||||||
v, err = s.toYamlNode(stringValue)
|
v, err = s.toYamlNode(stringValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "0123", v.Value)
|
assert.Equal(t, "0123", v.Value)
|
||||||
|
@ -176,14 +177,14 @@ func TestMarshalOctalValueIsQuoted(t *testing.T) {
|
||||||
|
|
||||||
func TestMarshalFloatValueIsQuoted(t *testing.T) {
|
func TestMarshalFloatValueIsQuoted(t *testing.T) {
|
||||||
s := NewSaver()
|
s := NewSaver()
|
||||||
var floatValue = dyn.NewValue(1.0, dyn.Location{})
|
var floatValue = dyn.V(1.0)
|
||||||
v, err := s.toYamlNode(floatValue)
|
v, err := s.toYamlNode(floatValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "1", v.Value)
|
assert.Equal(t, "1", v.Value)
|
||||||
assert.Equal(t, yaml.Style(0), v.Style)
|
assert.Equal(t, yaml.Style(0), v.Style)
|
||||||
assert.Equal(t, yaml.ScalarNode, v.Kind)
|
assert.Equal(t, yaml.ScalarNode, v.Kind)
|
||||||
|
|
||||||
var stringValue = dyn.NewValue("1.0", dyn.Location{})
|
var stringValue = dyn.V("1.0")
|
||||||
v, err = s.toYamlNode(stringValue)
|
v, err = s.toYamlNode(stringValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "1.0", v.Value)
|
assert.Equal(t, "1.0", v.Value)
|
||||||
|
@ -193,14 +194,14 @@ func TestMarshalFloatValueIsQuoted(t *testing.T) {
|
||||||
|
|
||||||
func TestMarshalBoolValueIsQuoted(t *testing.T) {
|
func TestMarshalBoolValueIsQuoted(t *testing.T) {
|
||||||
s := NewSaver()
|
s := NewSaver()
|
||||||
var boolValue = dyn.NewValue(true, dyn.Location{})
|
var boolValue = dyn.V(true)
|
||||||
v, err := s.toYamlNode(boolValue)
|
v, err := s.toYamlNode(boolValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "true", v.Value)
|
assert.Equal(t, "true", v.Value)
|
||||||
assert.Equal(t, yaml.Style(0), v.Style)
|
assert.Equal(t, yaml.Style(0), v.Style)
|
||||||
assert.Equal(t, yaml.ScalarNode, v.Kind)
|
assert.Equal(t, yaml.ScalarNode, v.Kind)
|
||||||
|
|
||||||
var stringValue = dyn.NewValue("true", dyn.Location{})
|
var stringValue = dyn.V("true")
|
||||||
v, err = s.toYamlNode(stringValue)
|
v, err = s.toYamlNode(stringValue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "true", v.Value)
|
assert.Equal(t, "true", v.Value)
|
||||||
|
@ -215,18 +216,18 @@ func TestCustomStylingWithNestedMap(t *testing.T) {
|
||||||
|
|
||||||
var styledMap = dyn.NewValue(
|
var styledMap = dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"key1": dyn.NewValue("value1", dyn.Location{File: "file", Line: 1, Column: 2}),
|
"key1": dyn.NewValue("value1", []dyn.Location{{File: "file", Line: 1, Column: 2}}),
|
||||||
"key2": dyn.NewValue("value2", dyn.Location{File: "file", Line: 2, Column: 2}),
|
"key2": dyn.NewValue("value2", []dyn.Location{{File: "file", Line: 2, Column: 2}}),
|
||||||
},
|
},
|
||||||
dyn.Location{File: "file", Line: -2, Column: 2},
|
[]dyn.Location{{File: "file", Line: -2, Column: 2}},
|
||||||
)
|
)
|
||||||
|
|
||||||
var unstyledMap = dyn.NewValue(
|
var unstyledMap = dyn.NewValue(
|
||||||
map[string]dyn.Value{
|
map[string]dyn.Value{
|
||||||
"key3": dyn.NewValue("value3", dyn.Location{File: "file", Line: 1, Column: 2}),
|
"key3": dyn.NewValue("value3", []dyn.Location{{File: "file", Line: 1, Column: 2}}),
|
||||||
"key4": dyn.NewValue("value4", dyn.Location{File: "file", Line: 2, Column: 2}),
|
"key4": dyn.NewValue("value4", []dyn.Location{{File: "file", Line: 2, Column: 2}}),
|
||||||
},
|
},
|
||||||
dyn.Location{File: "file", Line: -1, Column: 2},
|
[]dyn.Location{{File: "file", Line: -1, Column: 2}},
|
||||||
)
|
)
|
||||||
|
|
||||||
var val = dyn.NewValue(
|
var val = dyn.NewValue(
|
||||||
|
@ -234,7 +235,7 @@ func TestCustomStylingWithNestedMap(t *testing.T) {
|
||||||
"styled": styledMap,
|
"styled": styledMap,
|
||||||
"unstyled": unstyledMap,
|
"unstyled": unstyledMap,
|
||||||
},
|
},
|
||||||
dyn.Location{File: "file", Line: 1, Column: 2},
|
[]dyn.Location{{File: "file", Line: 1, Column: 2}},
|
||||||
)
|
)
|
||||||
|
|
||||||
mv, err := s.toYamlNode(val)
|
mv, err := s.toYamlNode(val)
|
||||||
|
|
|
@ -44,7 +44,7 @@ func skipAndOrder(mv dyn.Value, order *Order, skipFields []string, dst map[strin
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
dst[k] = dyn.NewValue(v.Value(), dyn.Location{Line: order.Get(k)})
|
dst[k] = dyn.NewValue(v.Value(), []dyn.Location{{Line: order.Get(k)}})
|
||||||
}
|
}
|
||||||
|
|
||||||
return dyn.V(dst), nil
|
return dyn.V(dst), nil
|
||||||
|
|
|
@ -33,16 +33,25 @@ func TestConvertToMapValueWithOrder(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, dyn.V(map[string]dyn.Value{
|
assert.Equal(t, dyn.V(map[string]dyn.Value{
|
||||||
"list": dyn.NewValue([]dyn.Value{
|
"list": dyn.NewValue(
|
||||||
|
[]dyn.Value{
|
||||||
dyn.V("a"),
|
dyn.V("a"),
|
||||||
dyn.V("b"),
|
dyn.V("b"),
|
||||||
dyn.V("c"),
|
dyn.V("c"),
|
||||||
}, dyn.Location{Line: -3}),
|
},
|
||||||
"name": dyn.NewValue("test", dyn.Location{Line: -2}),
|
[]dyn.Location{{Line: -3}},
|
||||||
"map": dyn.NewValue(map[string]dyn.Value{
|
),
|
||||||
|
"name": dyn.NewValue(
|
||||||
|
"test",
|
||||||
|
[]dyn.Location{{Line: -2}},
|
||||||
|
),
|
||||||
|
"map": dyn.NewValue(
|
||||||
|
map[string]dyn.Value{
|
||||||
"key1": dyn.V("value1"),
|
"key1": dyn.V("value1"),
|
||||||
"key2": dyn.V("value2"),
|
"key2": dyn.V("value2"),
|
||||||
}, dyn.Location{Line: -1}),
|
},
|
||||||
"long_name_field": dyn.NewValue("long name goes here", dyn.Location{Line: 1}),
|
[]dyn.Location{{Line: -1}},
|
||||||
|
),
|
||||||
|
"long_name_field": dyn.NewValue("long name goes here", []dyn.Location{{Line: 1}}),
|
||||||
}), result)
|
}), result)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue