github.com/solo-io/cue@v0.4.7/internal/core/subsume/subsume.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 subsume defines various subsumption relations.
    16  package subsume
    17  
    18  import (
    19  	"github.com/solo-io/cue/cue/errors"
    20  	"github.com/solo-io/cue/internal"
    21  	"github.com/solo-io/cue/internal/core/adt"
    22  )
    23  
    24  // Profile configures the type of subsumption. One should typically use one
    25  // of the preconfigured profiles.
    26  type Profile struct {
    27  	// Final indicates subsumption should only consider fields that are relevant
    28  	// to data mode, and ignore definitions, hidden fields, pattern constraints
    29  	// and additional constraints.
    30  	Final bool
    31  
    32  	// Defaults indicate that default values should be used for the subsumed
    33  	// value.
    34  	Defaults bool
    35  
    36  	// Ignore optional fields.
    37  	IgnoreOptional bool
    38  
    39  	// IgnoreClosedness ignores closedness of structs and is used for comparing
    40  	// APIs.
    41  	IgnoreClosedness bool
    42  }
    43  
    44  var CUE = Profile{}
    45  
    46  // Final checks subsumption interpreting the subsumed value as data.
    47  var Final = Profile{
    48  	Final:    true,
    49  	Defaults: true,
    50  }
    51  
    52  // FinalOpen exists as an artifact of the old API. One should probably not use
    53  // this.
    54  var FinalOpen = Profile{
    55  	Final:            true,
    56  	Defaults:         true,
    57  	IgnoreClosedness: true,
    58  }
    59  
    60  // API is subsumption used for APIs.
    61  var API = Profile{
    62  	IgnoreClosedness: true,
    63  }
    64  
    65  // Value subsumes two values based on their logical (evaluated) values.
    66  func Value(ctx *adt.OpContext, a, b adt.Value) errors.Error {
    67  	return CUE.Value(ctx, a, b)
    68  }
    69  
    70  func (p *Profile) Value(ctx *adt.OpContext, a, b adt.Value) errors.Error {
    71  	s := subsumer{ctx: ctx, Profile: *p}
    72  	if !s.values(a, b) {
    73  		return s.getError()
    74  	}
    75  	return nil // ignore errors here even if there are some.
    76  }
    77  
    78  // Check reports whether b is an instance of a.
    79  func (p *Profile) Check(ctx *adt.OpContext, a, b adt.Value) bool {
    80  	s := subsumer{ctx: ctx, Profile: *p}
    81  	return s.values(a, b)
    82  }
    83  
    84  func isBottom(x adt.Node) bool {
    85  	b, _ := x.(*adt.Bottom)
    86  	return b != nil
    87  }
    88  
    89  type subsumer struct {
    90  	ctx  *adt.OpContext
    91  	errs errors.Error
    92  
    93  	Profile
    94  
    95  	inexact bool // If true, the result could be a false negative.
    96  	missing adt.Feature
    97  	gt      adt.Value
    98  	lt      adt.Value
    99  }
   100  
   101  func (s *subsumer) errf(msg string, args ...interface{}) {
   102  	b := s.ctx.NewErrf(msg, args...)
   103  	s.errs = errors.Append(s.errs, b.Err)
   104  }
   105  
   106  func unifyValue(c *adt.OpContext, a, b adt.Value) adt.Value {
   107  	v := &adt.Vertex{}
   108  	v.AddConjunct(adt.MakeRootConjunct(c.Env(0), a))
   109  	v.AddConjunct(adt.MakeRootConjunct(c.Env(0), b))
   110  	x, _ := c.Evaluate(c.Env(0), v)
   111  	return x
   112  }
   113  
   114  func (s *subsumer) getError() (err errors.Error) {
   115  	c := s.ctx
   116  	// src := binSrc(token.NoPos, opUnify, gt, lt)
   117  	if s.gt != nil && s.lt != nil {
   118  		// src := binSrc(token.NoPos, opUnify, s.gt, s.lt)
   119  		if s.missing != 0 {
   120  			s.errf("missing field %q", s.missing.SelectorString(c))
   121  		} else if b, ok := unifyValue(c, s.gt, s.lt).(*adt.Bottom); !ok {
   122  			s.errf("value not an instance")
   123  		} else {
   124  			s.errs = errors.Append(s.errs, b.Err)
   125  		}
   126  	}
   127  	if s.errs == nil {
   128  		s.errf("value not an instance")
   129  	}
   130  	err = s.errs
   131  	if s.inexact {
   132  		err = internal.DecorateError(internal.ErrInexact, err)
   133  	}
   134  	return err
   135  }