Skip to content
This repository has been archived by the owner on Nov 18, 2021. It is now read-only.

Commit

Permalink
cue: add FillPath
Browse files Browse the repository at this point in the history
Note that the arguments of FillPath are opposite of that of Fill.
This reads more naturally, as the key comes before the value.
This inconsistency is acceptable, as the other Fill is deprecated.

Change-Id: Ia7d7131773a81ff512d94ac1913994329bb1ad4c
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9162
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
  • Loading branch information
mpvl committed Mar 29, 2021
1 parent 2e934c0 commit 7ca3968
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 11 deletions.
12 changes: 11 additions & 1 deletion cue/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ func MakePath(selectors ...Selector) Path {
//
// Unlike with normal CUE expressions, the first element of the path may be
// a string literal.
//
// A path may not contain hidden fields. To create a path with hidden fields,
// use MakePath and Ident.
func ParsePath(s string) Path {
if s == "" {
return Path{}
Expand All @@ -69,7 +72,14 @@ func ParsePath(s string) Path {
return MakePath(Selector{pathError{errors.Promote(err, "invalid path")}})
}

return Path{path: toSelectors(expr)}
p := Path{path: toSelectors(expr)}
for _, sel := range p.path {
if sel.sel.kind().IsHidden() {
return MakePath(Selector{pathError{errors.Newf(token.NoPos,
"invalid path: hidden fields not allowed in path %s", s)}})
}
}
return p
}

// Selectors reports the individual selectors of a path.
Expand Down
5 changes: 5 additions & 0 deletions cue/path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ func Test(t *testing.T) {
path: ParsePath(`c."#Foo"`),
str: `c."#Foo"`,
out: "7",
}, {
path: ParsePath("foo._foo"),
str: "_|_",
err: true,
out: `_|_ // invalid path: hidden fields not allowed in path foo._foo`,
}, {
path: ParsePath(`c."#Foo`),
str: "_|_",
Expand Down
45 changes: 37 additions & 8 deletions cue/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1633,20 +1633,49 @@ func (v Value) LookupField(name string) (FieldInfo, error) {
//
// Any reference in v referring to the value at the given path will resolve
// to x in the newly created value. The resulting value is not validated.
//
// Deprecated: use FillPath.
func (v Value) Fill(x interface{}, path ...string) Value {
if v.v == nil {
return v
}
ctx := v.ctx()
for i := len(path) - 1; i >= 0; i-- {
x = map[string]interface{}{path[i]: x}
selectors := make([]Selector, len(path))
for i, p := range path {
selectors[i] = Str(p)
}
var value = convert.GoValueToValue(ctx.opCtx, x, true)
n, _ := value.(*adt.Vertex)
if n == nil {
n = &adt.Vertex{}
n.AddConjunct(adt.MakeRootConjunct(nil, value))
return v.FillPath(MakePath(selectors...), x)
}

// FillPath creates a new value by unifying v with the value of x at the given
// path.
//
// Values may be any Go value that can be converted to CUE, an ast.Expr or a
// Value. In the latter case, it will panic if the Value is not from the same
// Runtime.
//
// Any reference in v referring to the value at the given path will resolve to x
// in the newly created value. The resulting value is not validated.
//
// List paths are not supported at this time.
func (v Value) FillPath(p Path, x interface{}) Value {
if v.v == nil {
return v
}
ctx := v.ctx()
if err := p.Err(); err != nil {
return newErrValue(v, ctx.mkErr(nil, 0, "invalid path: %v", err))
}
var expr adt.Expr = convert.GoValueToValue(ctx.opCtx, x, true)
for i := len(p.path) - 1; i >= 0; i-- {
expr = &adt.StructLit{Decls: []adt.Decl{
&adt.Field{
Label: p.path[i].sel.feature(v.idx),
Value: expr,
},
}}
}
n := &adt.Vertex{}
n.AddConjunct(adt.MakeRootConjunct(nil, expr))
n.Finalize(ctx.opCtx)
w := makeValue(v.idx, n)
return v.Unify(w)
Expand Down
94 changes: 94 additions & 0 deletions cue/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,100 @@ func TestFill2(t *testing.T) {
}
}

func TestFillPath(t *testing.T) {
r := &Runtime{}

inst, err := r.CompileExpr(ast.NewStruct("bar", ast.NewString("baz")))
if err != nil {
t.Fatal(err)
}

testCases := []struct {
in string
x interface{}
path Path
out string
}{{
in: `
foo: int
bar: foo
`,
x: 3,
path: ParsePath("foo"),
out: `
foo: 3
bar: 3
`,
}, {
in: `
X="#foo": int
bar: X
`,
x: 3,
path: ParsePath(`"#foo"`),
out: `
"#foo": 3
bar: 3
`,
}, {
in: `
X="#foo": foo: int
bar: X.foo
`,
x: 3,
path: ParsePath(`"#foo".foo`),
out: `
"#foo": foo: 3
bar: 3
`,
}, {
in: `
foo: #foo: int
bar: foo.#foo
`,
x: 3,
path: ParsePath("foo.#foo"),
out: `
foo: {
#foo: 3
}
bar: 3
`,
}, {
in: `
string
`,
x: "foo",
path: ParsePath(""),
out: `
"foo"
`,
}, {
in: `
foo: _
`,
x: inst.Value(),
path: ParsePath("foo"),
out: `
{foo: {bar: "baz"}}
`,
}}

for _, tc := range testCases {
t.Run("", func(t *testing.T) {
v := compileT(t, r, tc.in).Value()
v = v.FillPath(tc.path, tc.x)

w := compileT(t, r, tc.out).Value()

if !cmp.Equal(goValue(v), goValue(w)) {
t.Error(cmp.Diff(goValue(v), goValue(w)))
t.Errorf("\ngot: %s\nwant: %s", v, w)
}
})
}
}

func TestFillFloat(t *testing.T) {
// This tests panics for issue #749

Expand Down
12 changes: 10 additions & 2 deletions internal/core/adt/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,14 @@ const (
indexShift = 3
)

func (f FeatureType) IsDef() bool {
return f&DefinitionLabel == DefinitionLabel
}

func (f FeatureType) IsHidden() bool {
return f&HiddenLabel == HiddenLabel
}

// IsValid reports whether f is a valid label.
func (f Feature) IsValid() bool { return f != InvalidLabel }

Expand All @@ -276,7 +284,7 @@ func (f Feature) IsDef() bool {
// TODO(perf): do more mask trickery to avoid this branch.
return false
}
return f.Typ()&DefinitionLabel == DefinitionLabel
return f.Typ().IsDef()
}

// IsInt reports whether this is an integer index.
Expand All @@ -289,7 +297,7 @@ func (f Feature) IsHidden() bool {
// TODO(perf): do more mask trickery to avoid this branch.
return false
}
return f.Typ()&HiddenLabel == HiddenLabel
return f.Typ().IsHidden()
}

// Index reports the abstract index associated with f.
Expand Down

0 comments on commit 7ca3968

Please sign in to comment.