diff --git a/encoding/yaml/yaml.go b/encoding/yaml/yaml.go index 118f086b86a..b6cc621b364 100644 --- a/encoding/yaml/yaml.go +++ b/encoding/yaml/yaml.go @@ -23,6 +23,7 @@ import ( "cuelang.org/go/cue" "cuelang.org/go/cue/ast" cueyaml "cuelang.org/go/internal/encoding/yaml" + "cuelang.org/go/internal/pkg" "cuelang.org/go/internal/source" pkgyaml "cuelang.org/go/pkg/encoding/yaml" ) @@ -109,6 +110,6 @@ func EncodeStream(iter cue.Iterator) ([]byte, error) { // Validate validates the YAML and confirms it matches the constraints // specified by v. For YAML streams, all values must match v. func Validate(b []byte, v cue.Value) error { - _, err := pkgyaml.Validate(b, v) + _, err := pkgyaml.Validate(b, pkg.Schema(v)) return err } diff --git a/internal/core/compile/builtin.go b/internal/core/compile/builtin.go index 2a8f4372ee3..9a2045dd932 100644 --- a/internal/core/compile/builtin.go +++ b/internal/core/compile/builtin.go @@ -81,7 +81,7 @@ var closeBuiltin = &adt.Builtin{ Name: "close", Params: []adt.Param{structParam}, Result: adt.StructKind, - // Noncrete: true, // TODO: should probably be noncrete + // NonConcrete: true, // TODO: should probably be noncrete Func: func(c *adt.OpContext, args []adt.Value) adt.Expr { s, ok := args[0].(*adt.Vertex) if !ok { diff --git a/internal/pkg/types.go b/internal/pkg/types.go index cf7ba162680..a8ea8bdd8f7 100644 --- a/internal/pkg/types.go +++ b/internal/pkg/types.go @@ -21,6 +21,8 @@ import ( // A Schema represents an arbitrary cue.Value that can hold non-concrete values. // By default function arguments are checked to be concrete. +// +// TODO(mvdan,mpvl): consider using type Schema = cue.Value. type Schema cue.Value func (s Schema) Value() cue.Value { diff --git a/pkg/encoding/json/manual.go b/pkg/encoding/json/manual.go index b78b3dd41b2..0b69c1eb8a8 100644 --- a/pkg/encoding/json/manual.go +++ b/pkg/encoding/json/manual.go @@ -28,6 +28,7 @@ import ( "cuelang.org/go/cue/token" cuejson "cuelang.org/go/encoding/json" internaljson "cuelang.org/go/internal/encoding/json" + "cuelang.org/go/internal/pkg" ) // Compact generates the JSON-encoded src with insignificant space characters @@ -130,8 +131,8 @@ func Unmarshal(b []byte) (ast.Expr, error) { // Validate validates JSON and confirms it matches the constraints // specified by v. -func Validate(b []byte, v cue.Value) (bool, error) { - err := cuejson.Validate(b, v) +func Validate(b []byte, v pkg.Schema) (bool, error) { + err := cuejson.Validate(b, v.Value()) if err != nil { return false, err } diff --git a/pkg/encoding/json/pkg.go b/pkg/encoding/json/pkg.go index dce3ef731c9..189709db18c 100644 --- a/pkg/encoding/json/pkg.go +++ b/pkg/encoding/json/pkg.go @@ -118,9 +118,10 @@ var p = &pkg.Package{ {Kind: adt.BytesKind | adt.StringKind}, {Kind: adt.TopKind}, }, - Result: adt.BoolKind, + Result: adt.BoolKind, + NonConcrete: true, Func: func(c *pkg.CallCtxt) { - b, v := c.Bytes(0), c.Value(1) + b, v := c.Bytes(0), c.Schema(1) if c.Do() { c.Ret, c.Err = Validate(b, v) } diff --git a/pkg/encoding/json/testdata/gen.txtar b/pkg/encoding/json/testdata/gen.txtar index 5f960ba9725..8a9218af6c2 100644 --- a/pkg/encoding/json/testdata/gen.txtar +++ b/pkg/encoding/json/testdata/gen.txtar @@ -125,7 +125,7 @@ validate: { } | { b!: int } - result: json.Validate(str, schema) + result: true } disjunctionClosed: { str: *"{\"a\":10}" | string @@ -134,7 +134,7 @@ validate: { } | { b: int } - result: json.Validate(str, schema) + result: true } // Issue #2395 @@ -406,7 +406,7 @@ validate: { } | { b!: int } - result: json.Validate(str, schema) + result: true } disjunctionClosed: { str: *"{\"a\":10}" | string @@ -415,7 +415,7 @@ validate: { } | { b: int } - result: json.Validate(str, schema) + result: true } // Issue #2395 diff --git a/pkg/encoding/yaml/manual.go b/pkg/encoding/yaml/manual.go index c65c3621eb4..13bb93cabfd 100644 --- a/pkg/encoding/yaml/manual.go +++ b/pkg/encoding/yaml/manual.go @@ -84,10 +84,11 @@ func UnmarshalStream(data []byte) (ast.Expr, error) { return ast.NewList(a...), nil } -// Validate validates YAML and confirms it is an instance of the schema -// specified by v. If the YAML source is a stream, every object must match v. -func Validate(b []byte, v cue.Value) (bool, error) { +// Validate validates YAML and confirms it is an instance of schema. +// If the YAML source is a stream, every object must match v. +func Validate(b []byte, schema pkg.Schema) (bool, error) { d := cueyaml.NewDecoder("yaml.Validate", b) + v := schema.Value() r := v.Context() for { expr, err := d.Decode() @@ -132,8 +133,9 @@ func Validate(b []byte, v cue.Value) (bool, error) { // specified by v using unification. This means that b must be consistent with, // but does not have to be an instance of v. If the YAML source is a stream, // every object must match v. -func ValidatePartial(b []byte, v cue.Value) (bool, error) { +func ValidatePartial(b []byte, schema pkg.Schema) (bool, error) { d := cueyaml.NewDecoder("yaml.ValidatePartial", b) + v := schema.Value() r := v.Context() for { expr, err := d.Decode() diff --git a/pkg/encoding/yaml/pkg.go b/pkg/encoding/yaml/pkg.go index 94ed4d5a042..68c78ff39f4 100644 --- a/pkg/encoding/yaml/pkg.go +++ b/pkg/encoding/yaml/pkg.go @@ -68,11 +68,12 @@ var p = &pkg.Package{ {Kind: adt.BytesKind | adt.StringKind}, {Kind: adt.TopKind}, }, - Result: adt.BoolKind, + Result: adt.BoolKind, + NonConcrete: true, Func: func(c *pkg.CallCtxt) { - b, v := c.Bytes(0), c.Value(1) + b, schema := c.Bytes(0), c.Schema(1) if c.Do() { - c.Ret, c.Err = Validate(b, v) + c.Ret, c.Err = Validate(b, schema) } }, }, { @@ -81,11 +82,12 @@ var p = &pkg.Package{ {Kind: adt.BytesKind | adt.StringKind}, {Kind: adt.TopKind}, }, - Result: adt.BoolKind, + Result: adt.BoolKind, + NonConcrete: true, Func: func(c *pkg.CallCtxt) { - b, v := c.Bytes(0), c.Value(1) + b, schema := c.Bytes(0), c.Schema(1) if c.Do() { - c.Ret, c.Err = ValidatePartial(b, v) + c.Ret, c.Err = ValidatePartial(b, schema) } }, }}, diff --git a/pkg/encoding/yaml/testdata/validate.txtar b/pkg/encoding/yaml/testdata/validate.txtar index dd0247762b7..0c88caa9c0e 100644 --- a/pkg/encoding/yaml/testdata/validate.txtar +++ b/pkg/encoding/yaml/testdata/validate.txtar @@ -21,15 +21,119 @@ import "encoding/yaml" validate: #test & { fn: yaml.Validate + + // TODO: fix this test: the second disjunct should be eliminated, so there + // should not be a concreteness error. + t1: _ } validatePartial: #test & { fn: yaml.ValidatePartial } +-- out/yaml-v3 -- +Errors: +#test.t1.ok1: cannot call non-function fn (type _): + ./in.cue:8:11 +validate.t1.ok1: invalid value "a: 2" (does not satisfy encoding/yaml.Validate({a!:int} | {b!:int})): error in call to encoding/yaml.Validate: incomplete value {a:2} | {a:2,b!:int}: + ./in.cue:8:11 + ./in.cue:6:9 +#test.t1.ok2: cannot call non-function fn (type _): + ./in.cue:9:11 +#test.t1.ok3: cannot call non-function fn (type _): + ./in.cue:13:11 +#test.t2.ok1: cannot call non-function fn (type _): + ./in.cue:17:11 +#test.t2.ok2: cannot call non-function fn (type _): + ./in.cue:18:11 + +Result: +import "encoding/yaml" + +#test: { + fn: _ + data1: "a: 2" + t1: { + ok1: _|_ // #test.t1.ok1: cannot call non-function fn (type _) + ok2: _|_ // #test.t1.ok2: cannot call non-function fn (type _) + ok3: _|_ // #test.t1.ok3: cannot call non-function fn (type _) + } + #A: { + a: int + } + #B: { + b: int + } + data2: "'foo'" + t2: { + ok1: _|_ // #test.t2.ok1: cannot call non-function fn (type _) + ok2: _|_ // #test.t2.ok2: cannot call non-function fn (type _) + } +} +validate: { + fn: yaml.Validate + data1: "a: 2" + + // TODO: fix this test: the second disjunct should be eliminated, so there + // should not be a concreteness error. + t1: { + ok1: _|_ // validate.t1.ok1: invalid value "a: 2" (does not satisfy encoding/yaml.Validate({a!:int} | {b!:int})): validate.t1.ok1: error in call to encoding/yaml.Validate: incomplete value {a:2} | {a:2,b!:int} + ok2: "a: 2" + ok3: "a: 2" + } + #A: { + a: int + } + #B: { + b: int + } + data2: "'foo'" + t2: { + ok1: "'foo'" + ok2: "'foo'" + } +} +validatePartial: { + fn: yaml.ValidatePartial + data1: "a: 2" + t1: { + ok1: "a: 2" + ok2: "a: 2" + ok3: "a: 2" + } + #A: { + a: int + } + #B: { + b: int + } + data2: "'foo'" + t2: { + ok1: "'foo'" + ok2: "'foo'" + } +} +-- diff/-out/yaml-v3<==>+out/yaml -- +diff old new +--- old ++++ new +@@ -4,7 +4,6 @@ + validate.t1.ok1: invalid value "a: 2" (does not satisfy encoding/yaml.Validate({a!:int} | {b!:int})): error in call to encoding/yaml.Validate: incomplete value {a:2} | {a:2,b!:int}: + ./in.cue:8:11 + ./in.cue:6:9 +- ./in.cue:7:16 + #test.t1.ok2: cannot call non-function fn (type _): + ./in.cue:9:11 + #test.t1.ok3: cannot call non-function fn (type _): +-- diff/todo -- +missing position -- out/yaml -- Errors: #test.t1.ok1: cannot call non-function fn (type _): ./in.cue:8:11 +validate.t1.ok1: invalid value "a: 2" (does not satisfy encoding/yaml.Validate({a!:int} | {b!:int})): error in call to encoding/yaml.Validate: incomplete value {a:2} | {a:2,b!:int}: + ./in.cue:8:11 + ./in.cue:6:9 + ./in.cue:7:16 #test.t1.ok2: cannot call non-function fn (type _): ./in.cue:9:11 #test.t1.ok3: cannot call non-function fn (type _): @@ -65,18 +169,13 @@ import "encoding/yaml" validate: { fn: yaml.Validate data1: "a: 2" + + // TODO: fix this test: the second disjunct should be eliminated, so there + // should not be a concreteness error. t1: { - ok1: fn({ - a!: int - } | { - b!: int - }) & data1 - ok2: fn(close({ - a: int - }) | close({ - b: int - })) & data1 - ok3: fn(#A | #B) & data1 + ok1: _|_ // validate.t1.ok1: invalid value "a: 2" (does not satisfy encoding/yaml.Validate({a!:int} | {b!:int})): validate.t1.ok1: error in call to encoding/yaml.Validate: incomplete value {a:2} | {a:2,b!:int} + ok2: "a: 2" + ok3: "a: 2" } #A: { a: int @@ -86,25 +185,17 @@ validate: { } data2: "'foo'" t2: { - ok1: fn(*int | string) & data2 - ok2: fn(string) & data2 + ok1: "'foo'" + ok2: "'foo'" } } validatePartial: { fn: yaml.ValidatePartial data1: "a: 2" t1: { - ok1: fn({ - a!: int - } | { - b!: int - }) & data1 - ok2: fn(close({ - a: int - }) | close({ - b: int - })) & data1 - ok3: fn(#A | #B) & data1 + ok1: "a: 2" + ok2: "a: 2" + ok3: "a: 2" } #A: { a: int @@ -114,7 +205,7 @@ validatePartial: { } data2: "'foo'" t2: { - ok1: fn(*int | string) & data2 - ok2: fn(string) & data2 + ok1: "'foo'" + ok2: "'foo'" } }