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  }