cuelang.org/go@v0.10.1/internal/core/validate/validate.go (about) 1 // Copyright 2020 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 // http://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 15 // Package validate collects errors from an evaluated Vertex. 16 package validate 17 18 import ( 19 "cuelang.org/go/internal/core/adt" 20 ) 21 22 type Config struct { 23 // Concrete, if true, requires that all values be concrete. 24 Concrete bool 25 26 // Final, if true, checks that there are no required fields left. 27 Final bool 28 29 // DisallowCycles indicates that there may not be cycles. 30 DisallowCycles bool 31 32 // AllErrors continues descending into a Vertex, even if errors are found. 33 AllErrors bool 34 35 // TODO: omitOptional, if this is becomes relevant. 36 } 37 38 // Validate checks that a value has certain properties. The value must have 39 // been evaluated. 40 func Validate(ctx *adt.OpContext, v *adt.Vertex, cfg *Config) *adt.Bottom { 41 if cfg == nil { 42 cfg = &Config{} 43 } 44 x := validator{Config: *cfg, ctx: ctx} 45 x.validate(v) 46 return x.err 47 } 48 49 type validator struct { 50 Config 51 ctx *adt.OpContext 52 err *adt.Bottom 53 inDefinition int 54 } 55 56 func (v *validator) checkConcrete() bool { 57 return v.Concrete && v.inDefinition == 0 58 } 59 60 func (v *validator) add(b *adt.Bottom) { 61 if !v.AllErrors { 62 v.err = adt.CombineErrors(nil, v.err, b) 63 return 64 } 65 if !b.ChildError { 66 v.err = adt.CombineErrors(nil, v.err, b) 67 } 68 } 69 70 func (v *validator) validate(x *adt.Vertex) { 71 defer v.ctx.PopArc(v.ctx.PushArc(x)) 72 73 // Dereference values, but only those that are non-rooted. This includes let 74 // values. This prevents us from processing structure-shared nodes more than 75 // once and prevents potential cycles. 76 x = x.DerefNonRooted() 77 if b := x.Bottom(); b != nil { 78 switch b.Code { 79 case adt.CycleError: 80 if v.checkConcrete() || v.DisallowCycles { 81 v.add(b) 82 } 83 84 case adt.IncompleteError: 85 if v.checkConcrete() { 86 v.add(b) 87 } 88 89 default: 90 v.add(b) 91 } 92 if !b.HasRecursive { 93 return 94 } 95 96 } else if v.checkConcrete() { 97 x = x.Default() 98 if !adt.IsConcrete(x) { 99 x := x.Value() 100 v.add(&adt.Bottom{ 101 Code: adt.IncompleteError, 102 Err: v.ctx.Newf("incomplete value %v", x), 103 }) 104 } 105 } 106 107 for _, a := range x.Arcs { 108 if a.ArcType == adt.ArcRequired && v.Final && v.inDefinition == 0 { 109 v.add(adt.NewRequiredNotPresentError(v.ctx, a)) 110 continue 111 } 112 113 if a.Label.IsLet() || !a.IsDefined(v.ctx) { 114 continue 115 } 116 if !v.AllErrors && v.err != nil { 117 break 118 } 119 if a.Label.IsRegular() { 120 v.validate(a) 121 } else { 122 v.inDefinition++ 123 v.validate(a) 124 v.inDefinition-- 125 } 126 } 127 }