| // Copyright 2020 CUE Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Package validate collects errors from an evaluated Vertex. |
| package validate |
| |
| import ( |
| "cuelang.org/go/internal/core/adt" |
| ) |
| |
| type Config struct { |
| // Concrete, if true, requires that all values be concrete. |
| Concrete bool |
| |
| // DisallowCycles indicates that there may not be cycles. |
| DisallowCycles bool |
| |
| // AllErrors continues descending into a Vertex, even if errors are found. |
| AllErrors bool |
| |
| // TODO: omitOptional, if this is becomes relevant. |
| } |
| |
| // Validate checks that a value has certain properties. The value must have |
| // been evaluated. |
| func Validate(ctx *adt.OpContext, v *adt.Vertex, cfg *Config) *adt.Bottom { |
| if cfg == nil { |
| cfg = &Config{} |
| } |
| x := validator{Config: *cfg, ctx: ctx} |
| x.validate(v) |
| return x.err |
| } |
| |
| type validator struct { |
| Config |
| ctx *adt.OpContext |
| err *adt.Bottom |
| inDefinition int |
| } |
| |
| func (v *validator) checkConcrete() bool { |
| return v.Concrete && v.inDefinition == 0 |
| } |
| |
| func (v *validator) add(b *adt.Bottom) { |
| if !v.AllErrors { |
| v.err = adt.CombineErrors(nil, v.err, b) |
| return |
| } |
| if !b.ChildError { |
| v.err = adt.CombineErrors(nil, v.err, b) |
| } |
| } |
| |
| func (v *validator) validate(x *adt.Vertex) { |
| defer v.ctx.PopArc(v.ctx.PushArc(x)) |
| |
| if b, _ := x.BaseValue.(*adt.Bottom); b != nil { |
| switch b.Code { |
| case adt.CycleError: |
| if v.checkConcrete() || v.DisallowCycles { |
| v.add(b) |
| } |
| |
| case adt.IncompleteError, adt.NotExistError: |
| if v.checkConcrete() { |
| v.add(b) |
| } |
| |
| default: |
| v.add(b) |
| } |
| if !b.HasRecursive { |
| return |
| } |
| |
| } else if v.checkConcrete() { |
| x = x.Default() |
| if !adt.IsConcrete(x) { |
| x := x.Value() |
| v.add(&adt.Bottom{ |
| Code: adt.IncompleteError, |
| Err: v.ctx.Newf("incomplete value %v", v.ctx.Str(x)), |
| }) |
| } |
| } |
| |
| for _, a := range x.Arcs { |
| if !v.AllErrors && v.err != nil { |
| break |
| } |
| if a.Label.IsRegular() { |
| v.validate(a) |
| } else { |
| v.inDefinition++ |
| v.validate(a) |
| v.inDefinition-- |
| } |
| } |
| } |