Skip to content

Commit

Permalink
cue: separate closedness for lists and structs
Browse files Browse the repository at this point in the history
Before embedded scalars, it was fine to "overload" IsClosed
for Lists and Structs. With embedded scalars, however,
whether embedded list is open or closed as a list and struct
are two orthogonal concepts. The API and code needs to
reflect this.

This also holds for API users. There is now a new "Allows"
method on Value to determine if a certain selector
is "fillable" on a Value. The special selectors
AnyString and AnyIndex have been added to allow querying
the possibility of setting a value.

Once closedness of definitions is handled properly
AnyDefinition should be added as well (note that hidden
labels are always allowed).

This deprecates the IsClosed method.

Remove the internal IsClosed method to ensure that a
decision made on what to use in all cases.

Fixes #879

Change-Id: I40334c8254435503bbd59a4f5d725ca415ff03b6
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9325
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
  • Loading branch information
mpvl committed Apr 8, 2021
1 parent 4459434 commit c505c19
Show file tree
Hide file tree
Showing 38 changed files with 516 additions and 128 deletions.
71 changes: 71 additions & 0 deletions cue/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,74 @@ bar: _foo
// 1 <nil>
// 2 <nil>
}

func ExampleValue_Allows() {
const file = `
-- main.cue --
a: [1, 2, ...int]
b: #Point
#Point: {
x: int
y: int
z?: int
}
c: [string]: int
d: #C
#C: [>"m"]: int
`

v := load(file).Value()

a := v.LookupPath(cue.ParsePath("a"))
fmt.Println("a allows:")
fmt.Println(" index 4: ", a.Allows(cue.Index(4)))
fmt.Println(" any index: ", a.Allows(cue.AnyIndex))
fmt.Println(" any string: ", a.Allows(cue.AnyString))

b := v.LookupPath(cue.ParsePath("b"))
fmt.Println("b allows:")
fmt.Println(" field x: ", b.Allows(cue.Str("x")))
fmt.Println(" field z: ", b.Allows(cue.Str("z")))
fmt.Println(" field foo: ", b.Allows(cue.Str("foo")))
fmt.Println(" index 4: ", b.Allows(cue.Index(4)))
fmt.Println(" any string: ", b.Allows(cue.AnyString))

c := v.LookupPath(cue.ParsePath("c"))
fmt.Println("c allows:")
fmt.Println(" field z: ", c.Allows(cue.Str("z")))
fmt.Println(" field foo: ", c.Allows(cue.Str("foo")))
fmt.Println(" index 4: ", c.Allows(cue.Index(4)))
fmt.Println(" any string: ", c.Allows(cue.AnyString))

d := v.LookupPath(cue.ParsePath("d"))
fmt.Println("d allows:")
fmt.Println(" field z: ", d.Allows(cue.Str("z")))
fmt.Println(" field foo: ", d.Allows(cue.Str("foo")))
fmt.Println(" index 4: ", d.Allows(cue.Index(4)))
fmt.Println(" any string: ", d.Allows(cue.AnyString))

// Output:
// a allows:
// index 4: true
// any index: true
// any string: false
// b allows:
// field x: true
// field z: true
// field foo: false
// index 4: false
// any string: false
// c allows:
// field z: true
// field foo: true
// index 4: false
// any string: true
// d allows:
// field z: true
// field foo: false
// index 4: false
// any string: false
}
36 changes: 36 additions & 0 deletions cue/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,32 @@ func (sel Selector) IsString() bool {
return sel.sel.kind() == adt.StringLabel
}

var (
// AnyField can be used to ask for any single label.
//
// In paths it is used to select constraints that apply to all elements.
// AnyField = anyField
anyField = Selector{sel: anySelector(adt.AnyLabel)}

// AnyDefinition can be used to ask for any definition.
//
// In paths it is used to select constraints that apply to all elements.
// AnyDefinition = anyDefinition
anyDefinition = Selector{sel: anySelector(adt.AnyDefinition)}

// AnyIndex can be used to ask for any index.
//
// In paths it is used to select constraints that apply to all elements.
AnyIndex = anyIndex
anyIndex = Selector{sel: anySelector(adt.AnyIndex)}

// AnyString can be used to ask for any regular string field.
//
// In paths it is used to select constraints that apply to all elements.
AnyString = anyLabel
anyLabel = Selector{sel: anySelector(adt.AnyRegular)}
)

type selector interface {
String() string

Expand Down Expand Up @@ -355,6 +381,16 @@ func (s indexSelector) feature(r adt.Runtime) adt.Feature {
return adt.Feature(s)
}

// an anySelector represents a wildcard option of a particular type.
type anySelector adt.Feature

func (s anySelector) String() string { return "_" }
func (s anySelector) kind() adt.FeatureType { return adt.Feature(s).Typ() }

func (s anySelector) feature(r adt.Runtime) adt.Feature {
return adt.Feature(s)
}

// TODO: allow import paths to be represented?
//
// // ImportPath defines a lookup at the root of an instance. It must be the first
Expand Down
2 changes: 1 addition & 1 deletion cue/path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func Test(t *testing.T) {
path: ParsePath(`b[3T]`),
str: "_|_",
err: true,
out: `_|_ // int label out of range (3000000000000 not >=0 and <= 268435455)`,
out: `_|_ // int label out of range (3000000000000 not >=0 and <= 268435454)`,
}, {
path: ParsePath(`b[3.3]`),
str: "_|_",
Expand Down
2 changes: 1 addition & 1 deletion cue/testdata/benchmarks/deduparc.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ foo: #Value
foo: #Value
-- out/eval --
(struct){
#Value: (struct){ |((#struct){
#Value: (#struct){ |((#struct){
type: (string){ "float" }
}, (#struct){
type: (string){ "string" }
Expand Down
2 changes: 1 addition & 1 deletion cue/testdata/benchmarks/dedupelem.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ foo.0.type: conflicting values "string" and "float":
Result:
(_|_){
// [eval]
#Value: (struct){ |((#struct){
#Value: (#struct){ |((#struct){
type: (string){ "int" }
}, (#struct){
type: (string){ "float" }
Expand Down
6 changes: 3 additions & 3 deletions cue/testdata/builtins/closed.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Result:
inDisjunctions: (struct){
x: (struct){
socket: (#struct){
string: (struct){ |((#struct){
string: (#struct){ |((#struct){
a: (#struct){
b: (bool){ true }
}
Expand All @@ -99,7 +99,7 @@ Result:
}) }
}
syslog: (#struct){
string: (struct){ |((#struct){
string: (#struct){ |((#struct){
a: (#struct){
b: (bool){ true }
}
Expand All @@ -118,7 +118,7 @@ Result:
}
}
}) }
xxx: (struct){ |((#struct){
xxx: (#struct){ |((#struct){
a: (#struct){
b: (bool){ true }
}
Expand Down
2 changes: 1 addition & 1 deletion cue/testdata/comprehensions/issue843.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ package main
#c: (list){
}
}
#o: (struct){ |((#struct){
#o: (#struct){ |((#struct){
do: (string){ "f1" }
as: (list){
}
Expand Down
2 changes: 1 addition & 1 deletion cue/testdata/cycle/structural.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -675,7 +675,7 @@ Result:
#ref: (#struct){
ref: (string){ string }
}
x: (struct){ |((#struct){
x: (#struct){ |((#struct){
c: (#list){
0: ((string|struct)){ |((string){ string }, (#struct){
ref: (string){ string }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ v1:
}
#o2: (#struct){
}
#d1: (struct){ |((#struct){
#d1: (#struct){ |((#struct){
b: (int){ 4 }
}, (#struct){
c: (int){ 5 }
Expand Down
2 changes: 1 addition & 1 deletion cue/testdata/definitions/embed.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ Result:
}
}
reclose3: (struct){
#Step: (struct){ |((#struct){
#Step: (#struct){ |((#struct){
Name: (string){ string }
Something: (int){ int }
}, (#struct){
Expand Down
2 changes: 1 addition & 1 deletion cue/testdata/definitions/files.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ light: #theme & {
ctermbg: (string){ "254" }
}
#Config: (#struct){
console: (struct){ |(*(#struct){
console: (#struct){ |(*(#struct){
color: (string){ "light" }
ctermbg: (string){ "254" }
}, (#struct){
Expand Down
4 changes: 2 additions & 2 deletions cue/testdata/definitions/issue342.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ Z: X.test_3_XXX.ref
#Simple: (#struct){
ref: (string){ string }
}
#Complex: (struct){ |((#struct){
#Complex: (#struct){ |((#struct){
ref: (string){ string }
}, (#struct){
local: (string){ string }
}) }
var: (string){ "XXX" }
test_1: (struct){ |((#struct){
test_1: (#struct){ |((#struct){
ref: (string){ string }
}, (#struct){
local: (string){ string }
Expand Down
2 changes: 1 addition & 1 deletion cue/testdata/definitions/issue419.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ l: [
#B: (#struct){
b: (string){ string }
}
#X: (struct){ |((#struct){
#X: (#struct){ |((#struct){
a: (string){ string }
}, (#struct){
b: (string){ string }
Expand Down
2 changes: 1 addition & 1 deletion cue/testdata/definitions/issue471.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ package x
}
-- out/eval --
(struct){
#a: (struct){ |((#struct){
#a: (#struct){ |((#struct){
name: (string){ string }
age: (int){ int }
}, (#struct){
Expand Down
26 changes: 13 additions & 13 deletions cue/testdata/disjunctions/elimination.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -185,17 +185,17 @@ preserveClosedness: medium: p3: {
-- out/eval --
(struct){
disambiguateClosed: (struct){
b: (struct){ |((#struct){
b: (#struct){ |((#struct){
x: (bool){ true }
}, (#struct){
y: (bool){ true }
}) }
a: (struct){ |((#struct){
a: (#struct){ |((#struct){
x: (bool){ true }
}, (#struct){
y: (bool){ true }
}) }
#Def: (struct){ |((#struct){
#Def: (#struct){ |((#struct){
x: (bool){ true }
}, (#struct){
y: (bool){ true }
Expand Down Expand Up @@ -364,13 +364,13 @@ preserveClosedness: medium: p3: {
preserveClosedness: (struct){
small: (struct){
p1: (struct){
#A: (struct){ |(*(#struct){
#A: (#struct){ |(*(#struct){
a: (string){ string }
}, (#struct){
a: (string){ string }
b: (int){ int }
}) }
#B: (struct){ |(*(#struct){
#B: (#struct){ |(*(#struct){
}, (#struct){
b: (int){ int }
}, (#struct){
Expand All @@ -381,13 +381,13 @@ preserveClosedness: medium: p3: {
}) }
}
p2: (struct){
#A: (struct){ |(*(#struct){
#A: (#struct){ |(*(#struct){
a: (string){ string }
}, (#struct){
a: (string){ string }
b: (int){ int }
}) }
#B: (struct){ |(*(#struct){
#B: (#struct){ |(*(#struct){
}, (#struct){
a: (string){ string }
b: (int){ int }
Expand All @@ -400,7 +400,7 @@ preserveClosedness: medium: p3: {
}
medium: (struct){
p1: (struct){
#A: (struct){ |(*(#struct){
#A: (#struct){ |(*(#struct){
a: (string){ string }
}, (#struct){
a: (string){ string }
Expand All @@ -409,7 +409,7 @@ preserveClosedness: medium: p3: {
a: (string){ string }
d: (string){ string }
}) }
#B: (struct){ |(*(#struct){
#B: (#struct){ |(*(#struct){
}, (#struct){
c: (int){ int }
}, (#struct){
Expand All @@ -433,7 +433,7 @@ preserveClosedness: medium: p3: {
}) }
}
p2: (struct){
#A: (struct){ |(*(#struct){
#A: (#struct){ |(*(#struct){
a: (string){ string }
}, (#struct){
a: (string){ string }
Expand All @@ -442,7 +442,7 @@ preserveClosedness: medium: p3: {
a: (string){ string }
d: (string){ string }
}) }
#B: (struct){ |(*(#struct){
#B: (#struct){ |(*(#struct){
}, (#struct){
a: (string){ string }
c: (int){ int }
Expand All @@ -466,7 +466,7 @@ preserveClosedness: medium: p3: {
}) }
}
p3: (struct){
#A: (struct){ |(*(#struct){
#A: (#struct){ |(*(#struct){
a: (string){ string }
}, (#struct){
a: (string){ string }
Expand All @@ -475,7 +475,7 @@ preserveClosedness: medium: p3: {
a: (string){ string }
d: (string){ string }
}) }
#B: (struct){ |(*(#struct){
#B: (#struct){ |(*(#struct){
}, (#struct){
a: (string){ string }
c: (int){ int }
Expand Down

0 comments on commit c505c19

Please sign in to comment.