cel

package
v1.11.1-0...-ed78b6e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 19, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

Documentation

Overview

Copyright 2026 The KubeVela 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 cel evaluates CEL expressions embedded in properties with $( ).

It knows no roots by name. A host declares the roots its expressions may read and supplies a resolver for each, which answers every read before anything is evaluated, so no expression performs I/O.

A tree is parsed once into a Plan, which serves each stage that needs it: Reads for dependencies and status, Check for admission, and Eval to resolve and evaluate.

Copyright 2026 The KubeVela 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.

Copyright 2026 The KubeVela 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.

Copyright 2026 The KubeVela 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.

Index

Constants

View Source
const QualifiedKey = "$q"

QualifiedKey holds, inside a delivered value, what each qualifier call on it returns, keyed by template.Call. A chained call nests the same way: at("a").in("b") is the "in:b" entry inside the "at:a" entry.

Variables

View Source
var ErrNotReady = errors.New("not ready")

ErrNotReady marks a resolver's error as waiting rather than failing: what a read names does not exist yet. A resolver wraps it; a host checks it with errors.Is and tries again later rather than reporting a fault.

View Source
var Unknown interface{} = unknownValue{}

Unknown is what a resolver answers when its root has no value in this evaluation, as in a dry run. TreeOptions.Unknown renders reads of it.

Functions

func DeclType

func DeclType(v cue.Value, name string) *apiservercel.DeclType

DeclType translates a CUE schema into a CEL declared type, named name.

The name must be unique across everything declared in one environment: the type provider keeps the first type registered under a name, so a second schema sharing it would have its fields reported as undefined.

func ElementsCompatible

func ElementsCompatible(src *cel.Type, dst cue.Value) (bool, string, string)

ElementsCompatible reports whether a collection-valued expression can feed a collection-valued parameter.

A CUE kind says only "list", so a kind check cannot tell list(string) from a `[...int]` parameter. CEL carries the element type, so the elements are compared here.

It judges collections only, and only where both sides are concrete. A struct, an untyped region and a `dyn` all fail open, because that is where a legitimate widening is most likely and a false rejection is unfixable by the expression's author. The outer kind check has already run; this only narrows within a matching kind.

Returns the target and expression types when they cannot agree, for the message.

func KindFits

func KindFits(src, dst cue.Kind) bool

KindFits reports whether a value of kind src fits a target of kind dst. Kinds that share a member fit, so a number fits an int, since it may hold one; an integer fits a number; a float does not fit an int, since it may carry a fraction. Bottom, meaning unknown, fits anything.

func OptionalPath

func OptionalPath(v cue.Value, path []string) (bool, error)

OptionalPath reports whether any segment of a path may be absent at render.

Any segment, not just the last: if `network?: {vpcId: string}` then network.vpcId is absent whenever network is, however required vpcId looks inside it.

A key read out of an open map counts too: `labels: [string]: string` declares the map, never a key, so `labels["team"]` may find nothing.

func Typed

func Typed(v interface{}) interface{}

Typed marks a value whose numbers carry the types a schema declares, such as a fetched value retyped against the schema that describes it. Its widths are lined up for CEL, and integral floats in it are left as floats rather than read as ints.

Types

type CheckError

type CheckError struct {
	Property string
	// Expr is the expression at fault, when the fault is not about one read.
	Expr string
	// Read is the read at fault, when there is one.
	Read Read
	Err  error
}

A CheckError is one fault in a tree, and where it is.

func (CheckError) Error

func (c CheckError) Error() string

func (CheckError) Unwrap

func (c CheckError) Unwrap() error

Unwrap exposes the underlying error.

type CheckErrors

type CheckErrors []CheckError

CheckErrors is every fault found in one pass.

func (CheckErrors) Error

func (c CheckErrors) Error() string

func (CheckErrors) Unwrap

func (c CheckErrors) Unwrap() []error

Unwrap exposes each fault, so errors.Is and errors.As see through the list.

type Checker

type Checker interface {
	Check(reads []Read) []CheckError
}

A Checker validates every read of one root in a plan against what the host knows, such as the resource being admitted and the definitions it names. It judges reads without their values: resolving them is a Resolver's job.

type CheckerFunc

type CheckerFunc func(reads []Read) []CheckError

CheckerFunc adapts a function to a Checker.

func (CheckerFunc) Check

func (f CheckerFunc) Check(reads []Read) []CheckError

Check calls f.

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

An Engine compiles and evaluates expressions over a fixed set of roots.

It is immutable once built and safe for concurrent use; lru.Cache locks internally.

func NewEngine

func NewEngine(roots ...Root) (*Engine, error)

NewEngine builds an engine for the roots given.

A qualifier compiles on any receiver, since a read below a root is dyn, so References refuses one called on anything but a read of a root declaring it. Two roots may declare the same qualifier only with the same result type.

func (*Engine) DynEnv

func (e *Engine) DynEnv() *cel.Env

DynEnv is the environment for evaluation, where every root is an open map.

Static typing is an admission concern: by the time a render happens the values exist, and admission has already refused anything that would not type. CEL selects a map key with `.`, so an expression reads the same here as against a declared object.

func (*Engine) Eval

func (e *Engine) Eval(env *cel.Env, expr string, in map[string]interface{}) (interface{}, error)

Eval compiles and runs an expression against real data.

func (*Engine) EvalProperty

func (e *Engine) EvalProperty(env *cel.Env, raw string, in map[string]interface{}) (interface{}, error)

EvalProperty evaluates a whole property value, interpolation included.

template.Parse splits the value into text and expression fragments:

'https://$(source.registry.host)/health'  ->  "https://" + host + "/health"

A value that is a single expression keeps its type - an int stays an int - and one embedded in text yields a string, because concatenating with text is what that means.

func (*Engine) EvalTree

func (e *Engine) EvalTree(ctx context.Context, tree interface{}, resolvers map[string]Resolver,
	opts TreeOptions) (interface{}, error)

EvalTree is Plan followed by Eval, for a tree evaluated once.

func (*Engine) OutputType

func (e *Engine) OutputType(env *cel.Env, expr string) (*cel.Type, error)

OutputType compiles an expression and reports the type it produces, from the AST alone, before any data exists.

func (*Engine) Plan

func (e *Engine) Plan(tree interface{}) (*Plan, error)

Plan parses every expression in a tree and collects what each reads.

Every expression that does not parse or compile is reported, each with its property, as CheckErrors.

func (*Engine) PlanJSON

func (e *Engine) PlanJSON(doc []byte) (*Plan, error)

PlanJSON plans a JSON document, such as a resource's properties, and keeps the plan for the document's bytes: the same properties reach admission and every render, and a plan is only ever read once built. A document that is not JSON, or holds an expression that will not compile, is not kept.

func (*Engine) PropertyReferences

func (e *Engine) PropertyReferences(expr string) ([]template.Reference, error)

PropertyReferences returns every read an expression makes, against the shared permissive environment.

The environment is the same for every caller resolving or scanning an expression, so building one per call site only invites them to differ.

func (*Engine) References

func (e *Engine) References(env *cel.Env, expr string) ([]template.Reference, error)

References returns every read an expression makes.

Walking the checked AST rather than the source text is what makes this exact: `source.cfg.data["image"]` and `source["cfg"].data.image` are the same read spelled two ways, and the parser has already normalised both into a select chain over an index.

Reads inside a comprehension are included. A source read only in the untaken arm of a ternary is included too, deliberately: it still has to be resolved before the expression can be evaluated, and a value that might be substituted must count as sensitive whether or not this particular render reaches it.

func (*Engine) TypedEnv

func (e *Engine) TypedEnv(key string, decls func() (map[string]*apiservercel.DeclType, error)) (*cel.Env, error)

TypedEnv is an environment where the roots in decls carry those types and the rest stay open maps. It is kept under key, and decls is only called on a miss; an empty key is never kept.

The key must capture everything decls depends on: two callers sharing a key with different schemas would each type-check against the other's.

type Expression

type Expression struct {
	// Property is where it sits, as template.Walk names it.
	Property string
	// Expr is the text inside $( ).
	Expr string
	// Whole reports that it is the property's entire value, so it keeps its type.
	Whole bool
	Reads []Read
}

An Expression is one $( ) expression in a tree.

type MayBeAbsent

type MayBeAbsent struct{ Read Read }

A MayBeAbsent is an unguarded read that may be missing at render, feeding a required property.

func (*MayBeAbsent) Error

func (m *MayBeAbsent) Error() string

type Plan

type Plan struct {
	// contains filtered or unexported fields
}

A Plan is a properties tree parsed once: every expression in it and every read each one makes. Building one calls no resolver and evaluates nothing, so the same plan serves admission, dependency planning and evaluation.

func (*Plan) Check

func (p *Plan) Check(permitted []string, checkers map[string]Checker) []CheckError

Check is admission for the tree: every read's root must be one of permitted, and each read root with a Checker has it called once with all of its reads. Every fault is reported, reads of roots outside permitted first.

func (*Plan) CheckTypes

func (p *Plan) CheckTypes(env *cel.Env, target Target, opts TypeOptions) []CheckError

CheckTypes compiles each property's expressions against env, whose roots carry the types of what they read, and asserts the result fits the target at that property. It reports at most one fault per property, in this order: an expression that does not compile there; a collection embedded in text; a result whose kind, or whose elements, do not fit; an unguarded read that may be missing feeding a required property.

A dyn result, a read below an untyped region or of a value with no schema, has no type to compare and fits anything; a property the target does not declare is not judged. With no target, only what env refuses is reported.

func (*Plan) Eval

func (p *Plan) Eval(ctx context.Context, resolvers map[string]Resolver, opts TreeOptions) (interface{}, error)

Eval resolves and evaluates the tree.

Each read root's resolver is called once with all of its reads, in the order the roots were declared; a root nothing reads is not resolved. The first resolver error ends the evaluation, so a host declares its cheapest roots, and those most likely to wait, first. Only then is every expression evaluated, against the permissive environment. A leaf with no expression is returned byte-identical, and the tree is left alone.

func (*Plan) Expressions

func (p *Plan) Expressions() []Expression

Expressions returns every expression, sorted by property.

func (*Plan) Reads

func (p *Plan) Reads(root string) []Read

Reads returns every read of root, sorted by property.

type Qualifier

type Qualifier struct {
	Name string
	// Result is the call's type; nil is dyn.
	Result *cel.Type
}

A Qualifier is a member call taking one literal string, answered by looking up what the caller delivered under QualifiedKey. Which value it names is settled before evaluation, so the call performs no I/O.

type Read

type Read struct {
	template.Reference
	// Property is where the expression sits in the tree, as template.Walk names it.
	Property string
}

A Read is one reference an expression makes, and the property it feeds.

type Resolver

type Resolver interface {
	Resolve(ctx context.Context, reads []Read) (interface{}, error)
}

A Resolver answers every read of one root in one tree, before any expression is evaluated. Whatever I/O a root needs happens here, never inside an expression.

Its value is JSON-shaped, as a decoded document is: map[string]interface{}, []interface{} and scalars. Numbers are normalised for CEL only within those containers.

func Static

func Static(v interface{}) Resolver

Static answers every read from a value already in hand.

type ResolverFunc

type ResolverFunc func(ctx context.Context, reads []Read) (interface{}, error)

ResolverFunc adapts a function to a Resolver.

func (ResolverFunc) Resolve

func (f ResolverFunc) Resolve(ctx context.Context, reads []Read) (interface{}, error)

Resolve calls f.

type Root

type Root struct {
	Name string
	// Qualifiers are the member calls a read of this root may make to select
	// among values delivered with it, such as peer.db.at("east").
	Qualifiers []Qualifier
	// QualifiedAt is how many path segments a qualifier call follows: 1 puts it
	// straight after an entry's name, as peer.db.at("east"). Further qualifiers
	// may follow it directly.
	QualifiedAt int
}

A Root is a top-level name an expression may read. Everything an expression reads hangs off a declared root, so the sandbox is "these names and nothing else" rather than a denylist.

type Target

type Target interface {
	// At is the schema at property, whether the property is required, and
	// whether the target declares it at all; an open struct may not.
	At(property string) (schema cue.Value, required, declared bool)
}

A Target is the schema a tree's values land in: the parameter block of whatever the properties feed.

type TargetFunc

type TargetFunc func(property string) (cue.Value, bool, bool)

TargetFunc adapts a function to a Target.

func (TargetFunc) At

func (f TargetFunc) At(property string) (cue.Value, bool, bool)

At calls f.

type TreeOptions

type TreeOptions struct {
	// Unknown renders an expression that reads a root answered Unknown. whole
	// reports whether the expression is the property's entire value; otherwise
	// the result is joined into the surrounding text. Without it, such a read is
	// an error.
	Unknown func(expr string, whole bool) (interface{}, error)
	// OnEvalError is given each evaluation error with the roots the expression
	// reads, each once, and returns the error to report; nil keeps the original. A host uses it to treat a value
	// missing below a read it would wait on, such as an element a status fills
	// in later, as a wait.
	OnEvalError func(expr string, roots []string, err error) error
}

TreeOptions adjusts Plan.Eval and EvalTree.

type TypeMismatch

type TypeMismatch struct {
	// Got and Want are kind names, or element types when Elements is set.
	Got, Want string
	// Elements reports that the kinds agree, both lists or both maps, but
	// their element types do not.
	Elements bool
}

A TypeMismatch is an expression whose result does not fit its target.

func (*TypeMismatch) Error

func (m *TypeMismatch) Error() string

type TypeOptions

type TypeOptions struct {
	// Optional reports a read that may be missing at render. Such a read,
	// unguarded, is refused where it feeds a required property.
	Optional func(Read) bool
	// DeclaredKind is the kind the host's schema declares for a read, when it
	// knows one. CEL types CUE's number as double, so an expression that is
	// exactly one read is judged by this kind instead; a computed result keeps
	// CEL's type.
	DeclaredKind func(Read) (cue.Kind, bool)
}

TypeOptions adjusts CheckTypes.

Directories

Path Synopsis
Package template splits a property value into literal text and $( ) expressions, and walks a properties tree to reach every such value.
Package template splits a property value into literal text and $( ) expressions, and walks a properties tree to reach every such value.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL