// Copyright 2019 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 cel import ( "errors" "sync" "github.com/google/cel-go/checker" chkdecls "github.com/google/cel-go/checker/decls" "github.com/google/cel-go/common" celast "github.com/google/cel-go/common/ast" "github.com/google/cel-go/common/containers" "github.com/google/cel-go/common/decls" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/interpreter" "github.com/google/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) // Source interface representing a user-provided expression. type Source = common.Source // Ast representing the checked or unchecked expression, its source, and related metadata such as // source position information. type Ast struct { expr *exprpb.Expr info *exprpb.SourceInfo source Source refMap map[int64]*celast.ReferenceInfo typeMap map[int64]*types.Type } // Expr returns the proto serializable instance of the parsed/checked expression. func (ast *Ast) Expr() *exprpb.Expr { return ast.expr } // IsChecked returns whether the Ast value has been successfully type-checked. func (ast *Ast) IsChecked() bool { return ast.typeMap != nil && len(ast.typeMap) > 0 } // SourceInfo returns character offset and newline position information about expression elements. func (ast *Ast) SourceInfo() *exprpb.SourceInfo { return ast.info } // ResultType returns the output type of the expression if the Ast has been type-checked, else // returns chkdecls.Dyn as the parse step cannot infer the type. // // Deprecated: use OutputType func (ast *Ast) ResultType() *exprpb.Type { if !ast.IsChecked() { return chkdecls.Dyn } out := ast.OutputType() t, err := TypeToExprType(out) if err != nil { return chkdecls.Dyn } return t } // OutputType returns the output type of the expression if the Ast has been type-checked, else // returns cel.DynType as the parse step cannot infer types. func (ast *Ast) OutputType() *Type { t, found := ast.typeMap[ast.expr.GetId()] if !found { return DynType } return t } // Source returns a view of the input used to create the Ast. This source may be complete or // constructed from the SourceInfo. func (ast *Ast) Source() Source { return ast.source } // FormatType converts a type message into a string representation. // // Deprecated: prefer FormatCELType func FormatType(t *exprpb.Type) string { return checker.FormatCheckedType(t) } // FormatCELType formats a cel.Type value to a string representation. // // The type formatting is identical to FormatType. func FormatCELType(t *Type) string { return checker.FormatCELType(t) } // Env encapsulates the context necessary to perform parsing, type checking, or generation of // evaluable programs for different expressions. type Env struct { Container *containers.Container variables []*decls.VariableDecl functions map[string]*decls.FunctionDecl macros []parser.Macro adapter types.Adapter provider types.Provider features map[int]bool appliedFeatures map[int]bool libraries map[string]bool validators []ASTValidator costOptions []checker.CostOption // Internal parser representation prsr *parser.Parser prsrOpts []parser.Option // Internal checker representation chkMutex sync.Mutex chk *checker.Env chkErr error chkOnce sync.Once chkOpts []checker.Option // Program options tied to the environment progOpts []ProgramOption } // NewEnv creates a program environment configured with the standard library of CEL functions and // macros. The Env value returned can parse and check any CEL program which builds upon the core // features documented in the CEL specification. // // See the EnvOption helper functions for the options that can be used to configure the // environment. func NewEnv(opts ...EnvOption) (*Env, error) { // Extend the statically configured standard environment, disabling eager validation to ensure // the cost of setup for the environment is still just as cheap as it is in v0.11.x and earlier // releases. The user provided options can easily re-enable the eager validation as they are // processed after this default option. stdOpts := append([]EnvOption{EagerlyValidateDeclarations(false)}, opts...) env, err := getStdEnv() if err != nil { return nil, err } return env.Extend(stdOpts...) } // NewCustomEnv creates a custom program environment which is not automatically configured with the // standard library of functions and macros documented in the CEL spec. // // The purpose for using a custom environment might be for subsetting the standard library produced // by the cel.StdLib() function. Subsetting CEL is a core aspect of its design that allows users to // limit the compute and memory impact of a CEL program by controlling the functions and macros // that may appear in a given expression. // // See the EnvOption helper functions for the options that can be used to configure the // environment. func NewCustomEnv(opts ...EnvOption) (*Env, error) { registry, err := types.NewRegistry() if err != nil { return nil, err } return (&Env{ variables: []*decls.VariableDecl{}, functions: map[string]*decls.FunctionDecl{}, macros: []parser.Macro{}, Container: containers.DefaultContainer, adapter: registry, provider: registry, features: map[int]bool{}, appliedFeatures: map[int]bool{}, libraries: map[string]bool{}, validators: []ASTValidator{}, progOpts: []ProgramOption{}, costOptions: []checker.CostOption{}, }).configure(opts) } // Check performs type-checking on the input Ast and yields a checked Ast and/or set of Issues. // If any `ASTValidators` are configured on the environment, they will be applied after a valid // type-check result. If any issues are detected, the validators will provide them on the // output Issues object. // // Either checking or validation has failed if the returned Issues value and its Issues.Err() // value are non-nil. Issues should be inspected if they are non-nil, but may not represent a // fatal error. // // It is possible to have both non-nil Ast and Issues values returned from this call: however, // the mere presence of an Ast does not imply that it is valid for use. func (e *Env) Check(ast *Ast) (*Ast, *Issues) { // Note, errors aren't currently possible on the Ast to ParsedExpr conversion. pe, _ := AstToParsedExpr(ast) // Construct the internal checker env, erroring if there is an issue adding the declarations. chk, err := e.initChecker() if err != nil { errs := common.NewErrors(ast.Source()) errs.ReportError(common.NoLocation, err.Error()) return nil, NewIssuesWithSourceInfo(errs, ast.SourceInfo()) } res, errs := checker.Check(pe, ast.Source(), chk) if len(errs.GetErrors()) > 0 { return nil, NewIssuesWithSourceInfo(errs, ast.SourceInfo()) } // Manually create the Ast to ensure that the Ast source information (which may be more // detailed than the information provided by Check), is returned to the caller. ast = &Ast{ source: ast.Source(), expr: res.Expr, info: res.SourceInfo, refMap: res.ReferenceMap, typeMap: res.TypeMap} // Generate a validator configuration from the set of configured validators. vConfig := newValidatorConfig() for _, v := range e.validators { if cv, ok := v.(ASTValidatorConfigurer); ok { cv.Configure(vConfig) } } // Apply additional validators on the type-checked result. iss := NewIssuesWithSourceInfo(errs, ast.SourceInfo()) for _, v := range e.validators { v.Validate(e, vConfig, res, iss) } if iss.Err() != nil { return nil, iss } return ast, nil } // Compile combines the Parse and Check phases CEL program compilation to produce an Ast and // associated issues. // // If an error is encountered during parsing the Compile step will not continue with the Check // phase. If non-error issues are encountered during Parse, they may be combined with any issues // discovered during Check. // // Note, for parse-only uses of CEL use Parse. func (e *Env) Compile(txt string) (*Ast, *Issues) { return e.CompileSource(common.NewTextSource(txt)) } // CompileSource combines the Parse and Check phases CEL program compilation to produce an Ast and // associated issues. // // If an error is encountered during parsing the CompileSource step will not continue with the // Check phase. If non-error issues are encountered during Parse, they may be combined with any // issues discovered during Check. // // Note, for parse-only uses of CEL use Parse. func (e *Env) CompileSource(src Source) (*Ast, *Issues) { ast, iss := e.ParseSource(src) if iss.Err() != nil { return nil, iss } checked, iss2 := e.Check(ast) if iss2.Err() != nil { return nil, iss2 } return checked, iss2 } // Extend the current environment with additional options to produce a new Env. // // Note, the extended Env value should not share memory with the original. It is possible, however, // that a CustomTypeAdapter or CustomTypeProvider options could provide values which are mutable. // To ensure separation of state between extended environments either make sure the TypeAdapter and // TypeProvider are immutable, or that their underlying implementations are based on the // ref.TypeRegistry which provides a Copy method which will be invoked by this method. func (e *Env) Extend(opts ...EnvOption) (*Env, error) { chk, chkErr := e.getCheckerOrError() if chkErr != nil { return nil, chkErr } prsrOptsCopy := make([]parser.Option, len(e.prsrOpts)) copy(prsrOptsCopy, e.prsrOpts) // The type-checker is configured with Declarations. The declarations may either be provided // as options which have not yet been validated, or may come from a previous checker instance // whose types have already been validated. chkOptsCopy := make([]checker.Option, len(e.chkOpts)) copy(chkOptsCopy, e.chkOpts) // Copy the declarations if needed. varsCopy := []*decls.VariableDecl{} if chk != nil { // If the type-checker has already been instantiated, then the e.declarations have been // validated within the chk instance. chkOptsCopy = append(chkOptsCopy, checker.ValidatedDeclarations(chk)) } else { // If the type-checker has not been instantiated, ensure the unvalidated declarations are // provided to the extended Env instance. varsCopy = make([]*decls.VariableDecl, len(e.variables)) copy(varsCopy, e.variables) } // Copy macros and program options macsCopy := make([]parser.Macro, len(e.macros)) progOptsCopy := make([]ProgramOption, len(e.progOpts)) copy(macsCopy, e.macros) copy(progOptsCopy, e.progOpts) // Copy the adapter / provider if they appear to be mutable. adapter := e.adapter provider := e.provider adapterReg, isAdapterReg := e.adapter.(*types.Registry) providerReg, isProviderReg := e.provider.(*types.Registry) // In most cases the provider and adapter will be a ref.TypeRegistry; // however, in the rare cases where they are not, they are assumed to // be immutable. Since it is possible to set the TypeProvider separately // from the TypeAdapter, the possible configurations which could use a // TypeRegistry as the base implementation are captured below. if isAdapterReg && isProviderReg { reg := providerReg.Copy() provider = reg // If the adapter and provider are the same object, set the adapter // to the same ref.TypeRegistry as the provider. if adapterReg == providerReg { adapter = reg } else { // Otherwise, make a copy of the adapter. adapter = adapterReg.Copy() } } else if isProviderReg { provider = providerReg.Copy() } else if isAdapterReg { adapter = adapterReg.Copy() } featuresCopy := make(map[int]bool, len(e.features)) for k, v := range e.features { featuresCopy[k] = v } appliedFeaturesCopy := make(map[int]bool, len(e.appliedFeatures)) for k, v := range e.appliedFeatures { appliedFeaturesCopy[k] = v } funcsCopy := make(map[string]*decls.FunctionDecl, len(e.functions)) for k, v := range e.functions { funcsCopy[k] = v } libsCopy := make(map[string]bool, len(e.libraries)) for k, v := range e.libraries { libsCopy[k] = v } validatorsCopy := make([]ASTValidator, len(e.validators)) copy(validatorsCopy, e.validators) costOptsCopy := make([]checker.CostOption, len(e.costOptions)) copy(costOptsCopy, e.costOptions) ext := &Env{ Container: e.Container, variables: varsCopy, functions: funcsCopy, macros: macsCopy, progOpts: progOptsCopy, adapter: adapter, features: featuresCopy, appliedFeatures: appliedFeaturesCopy, libraries: libsCopy, validators: validatorsCopy, provider: provider, chkOpts: chkOptsCopy, prsrOpts: prsrOptsCopy, costOptions: costOptsCopy, } return ext.configure(opts) } // HasFeature checks whether the environment enables the given feature // flag, as enumerated in options.go. func (e *Env) HasFeature(flag int) bool { enabled, has := e.features[flag] return has && enabled } // HasLibrary returns whether a specific SingletonLibrary has been configured in the environment. func (e *Env) HasLibrary(libName string) bool { configured, exists := e.libraries[libName] return exists && configured } // Libraries returns a list of SingletonLibrary that have been configured in the environment. func (e *Env) Libraries() []string { libraries := make([]string, 0, len(e.libraries)) for libName := range e.libraries { libraries = append(libraries, libName) } return libraries } // HasValidator returns whether a specific ASTValidator has been configured in the environment. func (e *Env) HasValidator(name string) bool { for _, v := range e.validators { if v.Name() == name { return true } } return false } // Parse parses the input expression value `txt` to a Ast and/or a set of Issues. // // This form of Parse creates a Source value for the input `txt` and forwards to the // ParseSource method. func (e *Env) Parse(txt string) (*Ast, *Issues) { src := common.NewTextSource(txt) return e.ParseSource(src) } // ParseSource parses the input source to an Ast and/or set of Issues. // // Parsing has failed if the returned Issues value and its Issues.Err() value is non-nil. // Issues should be inspected if they are non-nil, but may not represent a fatal error. // // It is possible to have both non-nil Ast and Issues values returned from this call; however, // the mere presence of an Ast does not imply that it is valid for use. func (e *Env) ParseSource(src Source) (*Ast, *Issues) { res, errs := e.prsr.Parse(src) if len(errs.GetErrors()) > 0 { return nil, &Issues{errs: errs} } // Manually create the Ast to ensure that the text source information is propagated on // subsequent calls to Check. return &Ast{ source: src, expr: res.GetExpr(), info: res.GetSourceInfo()}, nil } // Program generates an evaluable instance of the Ast within the environment (Env). func (e *Env) Program(ast *Ast, opts ...ProgramOption) (Program, error) { optSet := e.progOpts if len(opts) != 0 { mergedOpts := []ProgramOption{} mergedOpts = append(mergedOpts, e.progOpts...) mergedOpts = append(mergedOpts, opts...) optSet = mergedOpts } return newProgram(e, ast, optSet) } // CELTypeAdapter returns the `types.Adapter` configured for the environment. func (e *Env) CELTypeAdapter() types.Adapter { return e.adapter } // CELTypeProvider returns the `types.Provider` configured for the environment. func (e *Env) CELTypeProvider() types.Provider { return e.provider } // TypeAdapter returns the `ref.TypeAdapter` configured for the environment. // // Deprecated: use CELTypeAdapter() func (e *Env) TypeAdapter() ref.TypeAdapter { return e.adapter } // TypeProvider returns the `ref.TypeProvider` configured for the environment. // // Deprecated: use CELTypeProvider() func (e *Env) TypeProvider() ref.TypeProvider { if legacyProvider, ok := e.provider.(ref.TypeProvider); ok { return legacyProvider } return &interopLegacyTypeProvider{Provider: e.provider} } // UnknownVars returns an interpreter.PartialActivation which marks all variables declared in the // Env as unknown AttributePattern values. // // Note, the UnknownVars will behave the same as an interpreter.EmptyActivation unless the // PartialAttributes option is provided as a ProgramOption. func (e *Env) UnknownVars() interpreter.PartialActivation { act := interpreter.EmptyActivation() part, _ := PartialVars(act, e.computeUnknownVars(act)...) return part } // PartialVars returns an interpreter.PartialActivation where all variables not in the input variable // set, but which have been configured in the environment, are marked as unknown. // // The `vars` value may either be an interpreter.Activation or any valid input to the // interpreter.NewActivation call. // // Note, this is equivalent to calling cel.PartialVars and manually configuring the set of unknown // variables. For more advanced use cases of partial state where portions of an object graph, rather // than top-level variables, are missing the PartialVars() method may be a more suitable choice. // // Note, the PartialVars will behave the same as an interpreter.EmptyActivation unless the // PartialAttributes option is provided as a ProgramOption. func (e *Env) PartialVars(vars any) (interpreter.PartialActivation, error) { act, err := interpreter.NewActivation(vars) if err != nil { return nil, err } return PartialVars(act, e.computeUnknownVars(act)...) } // ResidualAst takes an Ast and its EvalDetails to produce a new Ast which only contains the // attribute references which are unknown. // // Residual expressions are beneficial in a few scenarios: // // - Optimizing constant expression evaluations away. // - Indexing and pruning expressions based on known input arguments. // - Surfacing additional requirements that are needed in order to complete an evaluation. // - Sharing the evaluation of an expression across multiple machines/nodes. // // For example, if an expression targets a 'resource' and 'request' attribute and the possible // values for the resource are known, a PartialActivation could mark the 'request' as an unknown // interpreter.AttributePattern and the resulting ResidualAst would be reduced to only the parts // of the expression that reference the 'request'. // // Note, the expression ids within the residual AST generated through this method have no // correlation to the expression ids of the original AST. // // See the PartialVars helper for how to construct a PartialActivation. // // TODO: Consider adding an option to generate a Program.Residual to avoid round-tripping to an // Ast format and then Program again. func (e *Env) ResidualAst(a *Ast, details *EvalDetails) (*Ast, error) { pruned := interpreter.PruneAst(a.Expr(), a.SourceInfo().GetMacroCalls(), details.State()) expr, err := AstToString(ParsedExprToAst(pruned)) if err != nil { return nil, err } parsed, iss := e.Parse(expr) if iss != nil && iss.Err() != nil { return nil, iss.Err() } if !a.IsChecked() { return parsed, nil } checked, iss := e.Check(parsed) if iss != nil && iss.Err() != nil { return nil, iss.Err() } return checked, nil } // EstimateCost estimates the cost of a type checked CEL expression using the length estimates of input data and // extension functions provided by estimator. func (e *Env) EstimateCost(ast *Ast, estimator checker.CostEstimator, opts ...checker.CostOption) (checker.CostEstimate, error) { checked := &celast.CheckedAST{ Expr: ast.Expr(), SourceInfo: ast.SourceInfo(), TypeMap: ast.typeMap, ReferenceMap: ast.refMap, } extendedOpts := make([]checker.CostOption, 0, len(e.costOptions)) extendedOpts = append(extendedOpts, opts...) extendedOpts = append(extendedOpts, e.costOptions...) return checker.Cost(checked, estimator, extendedOpts...) } // configure applies a series of EnvOptions to the current environment. func (e *Env) configure(opts []EnvOption) (*Env, error) { // Customized the environment using the provided EnvOption values. If an error is // generated at any step this, will be returned as a nil Env with a non-nil error. var err error for _, opt := range opts { e, err = opt(e) if err != nil { return nil, err } } // If the default UTC timezone fix has been enabled, make sure the library is configured e, err = e.maybeApplyFeature(featureDefaultUTCTimeZone, Lib(timeUTCLibrary{})) if err != nil { return nil, err } // Configure the parser. prsrOpts := []parser.Option{} prsrOpts = append(prsrOpts, e.prsrOpts...) prsrOpts = append(prsrOpts, parser.Macros(e.macros...)) if e.HasFeature(featureEnableMacroCallTracking) { prsrOpts = append(prsrOpts, parser.PopulateMacroCalls(true)) } if e.HasFeature(featureVariadicLogicalASTs) { prsrOpts = append(prsrOpts, parser.EnableVariadicOperatorASTs(true)) } e.prsr, err = parser.NewParser(prsrOpts...) if err != nil { return nil, err } // Ensure that the checker init happens eagerly rather than lazily. if e.HasFeature(featureEagerlyValidateDeclarations) { _, err := e.initChecker() if err != nil { return nil, err } } return e, nil } func (e *Env) initChecker() (*checker.Env, error) { e.chkOnce.Do(func() { chkOpts := []checker.Option{} chkOpts = append(chkOpts, e.chkOpts...) chkOpts = append(chkOpts, checker.CrossTypeNumericComparisons( e.HasFeature(featureCrossTypeNumericComparisons))) ce, err := checker.NewEnv(e.Container, e.provider, chkOpts...) if err != nil { e.setCheckerOrError(nil, err) return } // Add the statically configured declarations. err = ce.AddIdents(e.variables...) if err != nil { e.setCheckerOrError(nil, err) return } // Add the function declarations which are derived from the FunctionDecl instances. for _, fn := range e.functions { if fn.IsDeclarationDisabled() { continue } err = ce.AddFunctions(fn) if err != nil { e.setCheckerOrError(nil, err) return } } // Add function declarations here separately. e.setCheckerOrError(ce, nil) }) return e.getCheckerOrError() } // setCheckerOrError sets the checker.Env or error state in a concurrency-safe manner func (e *Env) setCheckerOrError(chk *checker.Env, chkErr error) { e.chkMutex.Lock() e.chk = chk e.chkErr = chkErr e.chkMutex.Unlock() } // getCheckerOrError gets the checker.Env or error state in a concurrency-safe manner func (e *Env) getCheckerOrError() (*checker.Env, error) { e.chkMutex.Lock() defer e.chkMutex.Unlock() return e.chk, e.chkErr } // maybeApplyFeature determines whether the feature-guarded option is enabled, and if so applies // the feature if it has not already been enabled. func (e *Env) maybeApplyFeature(feature int, option EnvOption) (*Env, error) { if !e.HasFeature(feature) { return e, nil } _, applied := e.appliedFeatures[feature] if applied { return e, nil } e, err := option(e) if err != nil { return nil, err } // record that the feature has been applied since it will generate declarations // and functions which will be propagated on Extend() calls and which should only // be registered once. e.appliedFeatures[feature] = true return e, nil } // computeUnknownVars determines a set of missing variables based on the input activation and the // environment's configured declaration set. func (e *Env) computeUnknownVars(vars interpreter.Activation) []*interpreter.AttributePattern { var unknownPatterns []*interpreter.AttributePattern for _, v := range e.variables { varName := v.Name() if _, found := vars.ResolveName(varName); found { continue } unknownPatterns = append(unknownPatterns, interpreter.NewAttributePattern(varName)) } return unknownPatterns } // Error type which references an expression id, a location within source, and a message. type Error = common.Error // Issues defines methods for inspecting the error details of parse and check calls. // // Note: in the future, non-fatal warnings and notices may be inspectable via the Issues struct. type Issues struct { errs *common.Errors info *exprpb.SourceInfo } // NewIssues returns an Issues struct from a common.Errors object. func NewIssues(errs *common.Errors) *Issues { return NewIssuesWithSourceInfo(errs, nil) } // NewIssuesWithSourceInfo returns an Issues struct from a common.Errors object with SourceInfo metatata // which can be used with the `ReportErrorAtID` method for additional error reports within the context // information that's inferred from an expression id. func NewIssuesWithSourceInfo(errs *common.Errors, info *exprpb.SourceInfo) *Issues { return &Issues{ errs: errs, info: info, } } // Err returns an error value if the issues list contains one or more errors. func (i *Issues) Err() error { if i == nil { return nil } if len(i.Errors()) > 0 { return errors.New(i.String()) } return nil } // Errors returns the collection of errors encountered in more granular detail. func (i *Issues) Errors() []*Error { if i == nil { return []*Error{} } return i.errs.GetErrors() } // Append collects the issues from another Issues struct into a new Issues object. func (i *Issues) Append(other *Issues) *Issues { if i == nil { return other } if other == nil { return i } return NewIssues(i.errs.Append(other.errs.GetErrors())) } // String converts the issues to a suitable display string. func (i *Issues) String() string { if i == nil { return "" } return i.errs.ToDisplayString() } // ReportErrorAtID reports an error message with an optional set of formatting arguments. // // The source metadata for the expression at `id`, if present, is attached to the error report. // To ensure that source metadata is attached to error reports, use NewIssuesWithSourceInfo. func (i *Issues) ReportErrorAtID(id int64, message string, args ...any) { i.errs.ReportErrorAtID(id, locationByID(id, i.info), message, args...) } // locationByID returns a common.Location given an expression id. // // TODO: move this functionality into the native SourceInfo and an overhaul of the common.Source // as this implementation relies on the abstractions present in the protobuf SourceInfo object, // and is replicated in the checker. func locationByID(id int64, sourceInfo *exprpb.SourceInfo) common.Location { positions := sourceInfo.GetPositions() var line = 1 if offset, found := positions[id]; found { col := int(offset) for _, lineOffset := range sourceInfo.GetLineOffsets() { if lineOffset < offset { line++ col = int(offset - lineOffset) } else { break } } return common.NewLocation(line, col) } return common.NoLocation } // getStdEnv lazy initializes the CEL standard environment. func getStdEnv() (*Env, error) { stdEnvInit.Do(func() { stdEnv, stdEnvErr = NewCustomEnv(StdLib(), EagerlyValidateDeclarations(true)) }) return stdEnv, stdEnvErr } // interopCELTypeProvider layers support for the types.Provider interface on top of a ref.TypeProvider. type interopCELTypeProvider struct { ref.TypeProvider } // FindStructType returns a types.Type instance for the given fully-qualified typeName if one exists. // // This method proxies to the underyling ref.TypeProvider's FindType method and converts protobuf type // into a native type representation. If the conversion fails, the type is listed as not found. func (p *interopCELTypeProvider) FindStructType(typeName string) (*types.Type, bool) { if et, found := p.FindType(typeName); found { t, err := types.ExprTypeToType(et) if err != nil { return nil, false } return t, true } return nil, false } // FindStructFieldType returns a types.FieldType instance for the given fully-qualified typeName and field // name, if one exists. // // This method proxies to the underyling ref.TypeProvider's FindFieldType method and converts protobuf type // into a native type representation. If the conversion fails, the type is listed as not found. func (p *interopCELTypeProvider) FindStructFieldType(structType, fieldName string) (*types.FieldType, bool) { if ft, found := p.FindFieldType(structType, fieldName); found { t, err := types.ExprTypeToType(ft.Type) if err != nil { return nil, false } return &types.FieldType{ Type: t, IsSet: ft.IsSet, GetFrom: ft.GetFrom, }, true } return nil, false } // interopLegacyTypeProvider layers support for the ref.TypeProvider interface on top of a types.Provider. type interopLegacyTypeProvider struct { types.Provider } // FindType retruns the protobuf Type representation for the input type name if one exists. // // This method proxies to the underlying types.Provider FindStructType method and converts the types.Type // value to a protobuf Type representation. // // Failure to convert the type will result in the type not being found. func (p *interopLegacyTypeProvider) FindType(typeName string) (*exprpb.Type, bool) { if t, found := p.FindStructType(typeName); found { et, err := types.TypeToExprType(t) if err != nil { return nil, false } return et, true } return nil, false } // FindFieldType returns the protobuf-based FieldType representation for the input type name and field, // if one exists. // // This call proxies to the types.Provider FindStructFieldType method and converts the types.FIeldType // value to a protobuf-based ref.FieldType representation if found. // // Failure to convert the FieldType will result in the field not being found. func (p *interopLegacyTypeProvider) FindFieldType(structType, fieldName string) (*ref.FieldType, bool) { if cft, found := p.FindStructFieldType(structType, fieldName); found { et, err := types.TypeToExprType(cft.Type) if err != nil { return nil, false } return &ref.FieldType{ Type: et, IsSet: cft.IsSet, GetFrom: cft.GetFrom, }, true } return nil, false } var ( stdEnvInit sync.Once stdEnv *Env stdEnvErr error )