ceph-csi/vendor/github.com/google/cel-go/interpreter/activation.go

202 lines
6.1 KiB
Go
Raw Normal View History

// Copyright 2018 Google LLC
//
// 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 interpreter
import (
"errors"
"fmt"
"sync"
"github.com/google/cel-go/common/types/ref"
)
// Activation used to resolve identifiers by name and references by id.
//
// An Activation is the primary mechanism by which a caller supplies input into a CEL program.
type Activation interface {
// ResolveName returns a value from the activation by qualified name, or false if the name
// could not be found.
ResolveName(name string) (any, bool)
// Parent returns the parent of the current activation, may be nil.
// If non-nil, the parent will be searched during resolve calls.
Parent() Activation
}
// EmptyActivation returns a variable-free activation.
func EmptyActivation() Activation {
return emptyActivation{}
}
// emptyActivation is a variable-free activation.
type emptyActivation struct{}
func (emptyActivation) ResolveName(string) (any, bool) { return nil, false }
func (emptyActivation) Parent() Activation { return nil }
// NewActivation returns an activation based on a map-based binding where the map keys are
// expected to be qualified names used with ResolveName calls.
//
// The input `bindings` may either be of type `Activation` or `map[string]any`.
//
// Lazy bindings may be supplied within the map-based input in either of the following forms:
// - func() any
// - func() ref.Val
//
// The output of the lazy binding will overwrite the variable reference in the internal map.
//
// Values which are not represented as ref.Val types on input may be adapted to a ref.Val using
// the types.Adapter configured in the environment.
func NewActivation(bindings any) (Activation, error) {
if bindings == nil {
return nil, errors.New("bindings must be non-nil")
}
a, isActivation := bindings.(Activation)
if isActivation {
return a, nil
}
m, isMap := bindings.(map[string]any)
if !isMap {
return nil, fmt.Errorf(
"activation input must be an activation or map[string]interface: got %T",
bindings)
}
return &mapActivation{bindings: m}, nil
}
// mapActivation which implements Activation and maps of named values.
//
// Named bindings may lazily supply values by providing a function which accepts no arguments and
// produces an interface value.
type mapActivation struct {
bindings map[string]any
}
// Parent implements the Activation interface method.
func (a *mapActivation) Parent() Activation {
return nil
}
// ResolveName implements the Activation interface method.
func (a *mapActivation) ResolveName(name string) (any, bool) {
obj, found := a.bindings[name]
if !found {
return nil, false
}
fn, isLazy := obj.(func() ref.Val)
if isLazy {
obj = fn()
a.bindings[name] = obj
}
fnRaw, isLazy := obj.(func() any)
if isLazy {
obj = fnRaw()
a.bindings[name] = obj
}
return obj, found
}
// hierarchicalActivation which implements Activation and contains a parent and
// child activation.
type hierarchicalActivation struct {
parent Activation
child Activation
}
// Parent implements the Activation interface method.
func (a *hierarchicalActivation) Parent() Activation {
return a.parent
}
// ResolveName implements the Activation interface method.
func (a *hierarchicalActivation) ResolveName(name string) (any, bool) {
if object, found := a.child.ResolveName(name); found {
return object, found
}
return a.parent.ResolveName(name)
}
// NewHierarchicalActivation takes two activations and produces a new one which prioritizes
// resolution in the child first and parent(s) second.
func NewHierarchicalActivation(parent Activation, child Activation) Activation {
return &hierarchicalActivation{parent, child}
}
// NewPartialActivation returns an Activation which contains a list of AttributePattern values
// representing field and index operations that should result in a 'types.Unknown' result.
//
// The `bindings` value may be any value type supported by the interpreter.NewActivation call,
// but is typically either an existing Activation or map[string]any.
func NewPartialActivation(bindings any,
unknowns ...*AttributePattern) (PartialActivation, error) {
a, err := NewActivation(bindings)
if err != nil {
return nil, err
}
return &partActivation{Activation: a, unknowns: unknowns}, nil
}
// PartialActivation extends the Activation interface with a set of UnknownAttributePatterns.
type PartialActivation interface {
Activation
// UnknownAttributePaths returns a set of AttributePattern values which match Attribute
// expressions for data accesses whose values are not yet known.
UnknownAttributePatterns() []*AttributePattern
}
// partActivation is the default implementations of the PartialActivation interface.
type partActivation struct {
Activation
unknowns []*AttributePattern
}
// UnknownAttributePatterns implements the PartialActivation interface method.
func (a *partActivation) UnknownAttributePatterns() []*AttributePattern {
return a.unknowns
}
// varActivation represents a single mutable variable binding.
//
// This activation type should only be used within folds as the fold loop controls the object
// life-cycle.
type varActivation struct {
parent Activation
name string
val ref.Val
}
// Parent implements the Activation interface method.
func (v *varActivation) Parent() Activation {
return v.parent
}
// ResolveName implements the Activation interface method.
func (v *varActivation) ResolveName(name string) (any, bool) {
if name == v.name {
return v.val, true
}
return v.parent.ResolveName(name)
}
var (
// pool of var activations to reduce allocations during folds.
varActivationPool = &sync.Pool{
New: func() any {
return &varActivation{}
},
}
)