1// Copyright 2025 CUE Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package yaml
16
17import (
18 "errors"
19 "io"
20
21 "cuelang.org/go/cue"
22 "cuelang.org/go/internal/core/adt"
23 "cuelang.org/go/internal/pkg"
24 "cuelang.org/go/internal/value"
25)
26
27// Validate validates YAML and confirms it is an instance of schema.
28// If the YAML source is a stream, every object must match v.
29//
30// If Validate is called in a broader context, like a validation or function
31// call, the cycle context of n should be accumulated in c before this call.
32// This can be done by using the Expr method on the CallContext.
33func Validate(c *adt.OpContext, b []byte, v cue.Value) (bool, error) {
34 d := NewDecoder("yaml.Validate", b)
35 r := v.Context()
36 for {
37 expr, err := d.Decode()
38 if err != nil {
39 if err == io.EOF {
40 return true, nil
41 }
42 return false, err
43 }
44
45 x := r.BuildExpr(expr)
46 if err := x.Err(); err != nil {
47 return false, err
48 }
49
50 // TODO: consider using subsumption again here.
51 // Alternatives:
52 // - allow definition of non-concrete list,
53 // like list.Of(int), or []int.
54 // - Introduce ! in addition to ?, allowing:
55 // list!: [...]
56 // if err := v.Subsume(inst.Value(), cue.Final()); err != nil {
57 // return false, err
58 // }
59 vx := adt.Unify(c, value.Vertex(x), value.Vertex(v))
60 x = value.Make(c, vx)
61 if err := x.Err(); err != nil {
62 return false, err
63 }
64
65 if err := x.Validate(cue.Concrete(true)); err != nil {
66 // Strip error codes: incomplete errors are terminal in this case.
67 var b pkg.Bottomer
68 if errors.As(err, &b) {
69 err = b.Bottom().Err
70 }
71 return false, err
72 }
73 }
74}
75
76// ValidatePartial validates YAML and confirms it matches the constraints
77// specified by v using unification. This means that b must be consistent with,
78// but does not have to be an instance of v. If the YAML source is a stream,
79// every object must match v.
80func ValidatePartial(c *adt.OpContext, b []byte, v cue.Value) (bool, error) {
81 d := NewDecoder("yaml.ValidatePartial", b)
82 r := v.Context()
83 for {
84 expr, err := d.Decode()
85 if err != nil {
86 if err == io.EOF {
87 return true, nil
88 }
89 return false, err
90 }
91
92 x := r.BuildExpr(expr)
93 if err := x.Err(); err != nil {
94 return false, err
95 }
96
97 vx := adt.Unify(c, value.Vertex(x), value.Vertex(v))
98 x = value.Make(c, vx)
99 if err := x.Err(); err != nil {
100 return false, err
101 }
102 }
103}