From 5e219300a1260d9568c4192a3b05271d62594fce Mon Sep 17 00:00:00 2001 From: Roshan Singh <35294369+lopster568@users.noreply.github.com> Date: Mon, 27 Jul 2026 23:07:54 +0530 Subject: [PATCH 01/37] Respect composed adapter for unregistered structs (#1384) nativeTypeProvider.NativeToValue routed every struct-kind value to newNativeObject, regardless of whether the type was registered via ext.NativeTypes. Every other method on the provider checks the nativeTypes registry first and falls back to the composed base adapter when a type is not registered; NativeToValue was the exception, so a custom adapter added with cel.CustomTypeAdapter never saw unregistered structs it wanted to convert itself. Mirror the registry check from NewValue: only wrap registered struct types as native objects, and delegate the rest to the base adapter. Fixes #1343 --- ext/native.go | 11 ++++++++- ext/native_test.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/ext/native.go b/ext/native.go index d9f5fab0d..c03b053f1 100644 --- a/ext/native.go +++ b/ext/native.go @@ -395,7 +395,16 @@ func (tp *nativeTypeProvider) NativeToValue(val any) ref.Val { time.Time: return tp.baseAdapter.NativeToValue(val) default: - return tp.newNativeObject(val, rawVal) + // Only claim struct values whose type was registered via + // ext.NativeTypes. Every other method on nativeTypeProvider checks + // the registry before handling a type; without the same check here + // a composed base adapter never sees unregistered structs it wants + // to convert itself. + typeName := fmt.Sprintf("%s.%s", simplePkgAlias(refVal.Type().PkgPath()), refVal.Type().Name()) + if _, found := tp.nativeTypes[typeName]; found { + return tp.newNativeObject(val, rawVal) + } + return tp.baseAdapter.NativeToValue(val) } default: return tp.baseAdapter.NativeToValue(val) diff --git a/ext/native_test.go b/ext/native_test.go index 75c024d1e..59c0cd26d 100644 --- a/ext/native_test.go +++ b/ext/native_test.go @@ -1277,3 +1277,61 @@ type TestRefValFieldType struct { IntVal types.Int CELTime types.Timestamp `cel:"time"` } + +// registeredNativeStruct is registered with NativeTypes in the delegation test. +type registeredNativeStruct struct { + Name string +} + +// unregisteredNativeStruct is not registered, so NativeToValue should hand it to +// the composed base adapter rather than wrapping it as a native object. +type unregisteredNativeStruct struct { + Name string +} + +// recordingAdapter converts unregisteredNativeStruct into a sentinel string and +// records that it was asked to, so the test can confirm nativeTypeProvider +// delegated the value. Everything else falls through to the base adapter. +type recordingAdapter struct { + base types.Adapter + saw bool +} + +func (a *recordingAdapter) NativeToValue(value any) ref.Val { + if _, ok := value.(unregisteredNativeStruct); ok { + a.saw = true + return types.String("from-base-adapter") + } + return a.base.NativeToValue(value) +} + +func TestNativeToValueDelegatesUnregisteredStructs(t *testing.T) { + custom := &recordingAdapter{base: types.DefaultTypeAdapter} + env, err := cel.NewEnv( + cel.CustomTypeAdapter(custom), + NativeTypes(reflect.TypeOf(registeredNativeStruct{})), + ) + if err != nil { + t.Fatalf("cel.NewEnv() failed: %v", err) + } + adapter := env.CELTypeAdapter() + + // An unregistered struct must reach the composed base adapter. + got := adapter.NativeToValue(unregisteredNativeStruct{Name: "x"}) + if !custom.saw { + t.Error("base adapter was not consulted for an unregistered struct") + } + if got.Equal(types.String("from-base-adapter")) != types.True { + t.Errorf("NativeToValue(unregisteredNativeStruct) = %v, want the base adapter's value", got) + } + + // A registered native type must still be wrapped as a native object. + custom.saw = false + gotReg := adapter.NativeToValue(registeredNativeStruct{Name: "y"}) + if custom.saw { + t.Error("base adapter was consulted for a registered native type") + } + if tn := gotReg.Type().TypeName(); !strings.Contains(tn, "registeredNativeStruct") { + t.Errorf("NativeToValue(registeredNativeStruct).Type() = %q, want a native object type", tn) + } +} From fa407aa28b01a4a1c475e6796fc2190078020e35 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Mon, 27 Jul 2026 11:08:15 -0700 Subject: [PATCH 02/37] Regex program plan size controls (#1383) * Regex program plan size controls This implementation mirrors the logic used in RE2 within cel-java and cel-cpp. Regex implementations across platforms do not guarantee equivalent program plan sizes, but this at least provides a means to control the plan size per-platform. * Minor refactor for maintenance of limit extraction * Minor updates to test setup --- cel/cel_test.go | 90 +++++++++++++++++++++ cel/options.go | 22 +++++ cel/program.go | 3 + cel/validator.go | 139 ++++++++++++++++++++++++-------- cel/validator_test.go | 71 ++++++++++++++++ common/types/BUILD.bazel | 2 + common/types/regex.go | 49 +++++++++++ common/types/regex_test.go | 76 +++++++++++++++++ ext/regex_test.go | 74 +++++++++++++++++ interpreter/decorators.go | 73 +++++++++++++++++ interpreter/interpreter.go | 5 ++ interpreter/interpreter_test.go | 66 +++++++++++++++ 12 files changed, 636 insertions(+), 34 deletions(-) create mode 100644 common/types/regex.go create mode 100644 common/types/regex_test.go diff --git a/cel/cel_test.go b/cel/cel_test.go index 5aa0cbf7b..159e5c592 100644 --- a/cel/cel_test.go +++ b/cel/cel_test.go @@ -2504,6 +2504,96 @@ func TestRegexOptimizer(t *testing.T) { } } +func TestRegexProgramSizeLimit(t *testing.T) { + env, err := NewEnv( + Variable("pattern", StringType), + RegexProgramSizeLimit(5), + ) + if err != nil { + t.Fatalf("NewEnv failed: %v", err) + } + + tests := []struct { + name string + expr string + progOpts []ProgramOption + vars any + want ref.Val + compileErr string + progErr string + evalErr string + }{ + { + name: "constant_regex_exceeds_limit_ast_validation", + expr: `"123 abc 456".matches('(a|b)*[0-9]+')`, + compileErr: "regex program size 8 exceeds limit of 5", + }, + { + name: "dynamic_regex_exceeds_limit_runtime", + expr: `"123 abc 456".matches(pattern)`, + vars: map[string]any{"pattern": "(a|b)*[0-9]+"}, + evalErr: "regex program size 8 exceeds limit of 5", + }, + { + name: "dynamic_regex_within_limit", + expr: `"123 abc 456".matches(pattern)`, + vars: map[string]any{"pattern": "[0-9]+"}, + want: types.True, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(tt *testing.T) { + ast, iss := env.Compile(tc.expr) + if tc.compileErr != "" { + if iss.Err() == nil { + tt.Fatalf("env.Compile(%s) succeeded, wanted error %s", tc.expr, tc.compileErr) + } + if !strings.Contains(iss.Err().Error(), tc.compileErr) { + tt.Errorf("got compile error %v, wanted error containing %s", iss.Err(), tc.compileErr) + } + return + } + if iss.Err() != nil { + tt.Fatalf("env.Compile(%s) failed: %v", tc.expr, iss.Err()) + } + prg, err := env.Program(ast, tc.progOpts...) + if tc.progErr != "" { + if err == nil { + tt.Fatalf("env.Program(%s) succeeded, wanted error %s", tc.expr, tc.progErr) + } + if !strings.Contains(err.Error(), tc.progErr) { + tt.Errorf("got program error %v, wanted error containing %s", err, tc.progErr) + } + return + } + if err != nil { + tt.Fatalf("env.Program(%s) failed: %v", tc.expr, err) + } + vars := tc.vars + if vars == nil { + vars = NoVars() + } + res, _, err := prg.Eval(vars) + if tc.evalErr != "" { + if err == nil { + tt.Fatalf("prg.Eval(%s) succeeded, wanted error %s", tc.expr, tc.evalErr) + } + if !strings.Contains(err.Error(), tc.evalErr) { + tt.Errorf("got eval error %v, wanted error containing %s", err, tc.evalErr) + } + return + } + if err != nil { + tt.Fatalf("prg.Eval(%s) failed: %v", tc.expr, err) + } + if res != tc.want { + tt.Errorf("got %v, wanted %v", res, tc.want) + } + }) + } +} + func TestDefaultUTCTimeZoneDisabled(t *testing.T) { testEnvs := []struct { name string diff --git a/cel/options.go b/cel/options.go index 540ad38ba..710536fb7 100644 --- a/cel/options.go +++ b/cel/options.go @@ -114,6 +114,8 @@ const ( limitMaxASTDepth // The maximum number of expression nodes permitted in parsing (including macro expansion). limitExpressionNodeCount + // The maximum regex program plan size permitted. + limitRegexProgramSize ) // defaultMaxASTDepth mirrors the parser's default maxRecursionDepth (250) and @@ -127,6 +129,7 @@ var limitIDsToNames = map[limitID]string{ limitParseRecursionDepth: "cel.limit.parse_recursion_depth", limitMaxASTDepth: "cel.limit.max_ast_depth", limitExpressionNodeCount: "cel.limit.expression_node_count", + limitRegexProgramSize: "cel.limit.regex_program_size", } func limitNameByID(id limitID) (string, bool) { @@ -1022,6 +1025,25 @@ func ExpressionNestingDepthLimit(limit int) EnvOption { return setLimit(limitMaxASTDepth, limit) } +// RegexProgramSizeLimit caps the maximum regex program plan size permitted for regular expressions. +// A negative or zero value means unbounded. +func RegexProgramSizeLimit(limit int) EnvOption { + return func(e *Env) (*Env, error) { + var err error + e, err = setLimit(limitRegexProgramSize, limit)(e) + if err != nil { + return nil, err + } + if limit > 0 { + e, err = ASTValidators(ValidateRegexProgramSizeLimit(limit))(e) + if err != nil { + return nil, err + } + } + return e, nil + } +} + // EnableHiddenAccumulatorName sets the parser to use the identifier '@result' for accumulators // which is not normally accessible from CEL source. func EnableHiddenAccumulatorName(enabled bool) EnvOption { diff --git a/cel/program.go b/cel/program.go index 3a7589a71..c97d99213 100644 --- a/cel/program.go +++ b/cel/program.go @@ -295,6 +295,9 @@ func newProgram(e *Env, a *ast.AST, opts []ProgramOption) (Program, error) { if len(p.regexOptimizations) > 0 { plannerOptions = append(plannerOptions, interpreter.CompileRegexConstants(p.regexOptimizations...)) } + if limit := p.limits[limitRegexProgramSize]; limit > 0 { + plannerOptions = append(plannerOptions, interpreter.RegexProgramSizeLimit(limit)) + } // Enable exhaustive eval, state tracking and cost tracking last since they require a factory. if p.evalOpts&(OptExhaustiveEval|OptTrackState|OptTrackCost) != 0 { diff --git a/cel/validator.go b/cel/validator.go index cb7f4c29e..229defe79 100644 --- a/cel/validator.go +++ b/cel/validator.go @@ -23,15 +23,17 @@ import ( "github.com/google/cel-go/common/ast" "github.com/google/cel-go/common/env" "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" ) const ( - durationValidatorName = "cel.validator.duration" - regexValidatorName = "cel.validator.matches" - timestampValidatorName = "cel.validator.timestamp" - homogeneousValidatorName = "cel.validator.homogeneous_literals" - nestingLimitValidatorName = "cel.validator.comprehension_nesting_limit" - bindNestingLimitValidatorName = "cel.validator.bind_nesting_limit" + durationValidatorName = "cel.validator.duration" + regexValidatorName = "cel.validator.matches" + timestampValidatorName = "cel.validator.timestamp" + homogeneousValidatorName = "cel.validator.homogeneous_literals" + nestingLimitValidatorName = "cel.validator.comprehension_nesting_limit" + bindNestingLimitValidatorName = "cel.validator.bind_nesting_limit" + regexProgramSizeLimitValidatorName = "cel.validator.regex_program_size_limit" // HomogeneousAggregateLiteralExemptFunctions is the ValidatorConfig key used to configure // the set of function names which are exempt from homogeneous type checks. The expected type @@ -46,38 +48,25 @@ const ( var ( astValidatorFactories = map[string]ASTValidatorFactory{ nestingLimitValidatorName: func(val *env.Validator) (ASTValidator, error) { - if limit, found := val.ConfigValue("limit"); found { - // In case of protos, config value is of type by google.protobuf.Value, which numeric values are always a double. - if val, isDouble := limit.(float64); isDouble { - if val != float64(int64(val)) { - return nil, fmt.Errorf("invalid validator: %s, limit value is not a whole number: %v", nestingLimitValidatorName, limit) - } - return ValidateComprehensionNestingLimit(int(val)), nil - } - - if val, isInt := limit.(int); isInt { - return ValidateComprehensionNestingLimit(val), nil - } - return nil, fmt.Errorf("invalid validator: %s unsupported limit type: %v", nestingLimitValidatorName, limit) + limit, err := validatorIntConfig(val, "limit") + if err != nil { + return nil, err } - return nil, fmt.Errorf("invalid validator: %s missing limit", nestingLimitValidatorName) + return ValidateComprehensionNestingLimit(limit), nil }, bindNestingLimitValidatorName: func(val *env.Validator) (ASTValidator, error) { - if limit, found := val.ConfigValue("limit"); found { - // In case of protos, config value is of type by google.protobuf.Value, which numeric values are always a double. - if val, isDouble := limit.(float64); isDouble { - if val != float64(int64(val)) { - return nil, fmt.Errorf("invalid validator: %s, limit value is not a whole number: %v", bindNestingLimitValidatorName, limit) - } - return ValidateBindNestingLimit(int(val)), nil - } - - if val, isInt := limit.(int); isInt { - return ValidateBindNestingLimit(val), nil - } - return nil, fmt.Errorf("invalid validator: %s unsupported limit type: %v", bindNestingLimitValidatorName, limit) + limit, err := validatorIntConfig(val, "limit") + if err != nil { + return nil, err } - return nil, fmt.Errorf("invalid validator: %s missing limit", bindNestingLimitValidatorName) + return ValidateBindNestingLimit(limit), nil + }, + regexProgramSizeLimitValidatorName: func(val *env.Validator) (ASTValidator, error) { + limit, err := validatorIntConfig(val, "limit") + if err != nil { + return nil, err + } + return ValidateRegexProgramSizeLimit(limit), nil }, durationValidatorName: func(*env.Validator) (ASTValidator, error) { return ValidateDurationLiterals(), nil @@ -266,6 +255,11 @@ func ValidateBindNestingLimit(limit int) ASTValidator { return bindNestingLimitValidator{limit: limit} } +// ValidateRegexProgramSizeLimit ensures that regex pattern literals do not exceed the specified regex program size limit. +func ValidateRegexProgramSizeLimit(limit int) ASTValidator { + return regexProgramSizeLimitValidator{limit: limit} +} + type argChecker func(env *Env, call, arg ast.Expr) error func newFormatValidator(funcName string, argNum int, check argChecker) formatValidator { @@ -495,6 +489,20 @@ func (v bindNestingLimitValidator) ToConfig() *env.Validator { return env.NewValidator(v.Name()).SetConfig(map[string]any{"limit": v.limit}) } +type regexProgramSizeLimitValidator struct { + limit int +} + +// Name returns the name of the regex program size limit validator. +func (v regexProgramSizeLimitValidator) Name() string { + return regexProgramSizeLimitValidatorName +} + +// ToConfig converts the ASTValidator to an env.Validator specifying the validator name and the limit. +func (v regexProgramSizeLimitValidator) ToConfig() *env.Validator { + return env.NewValidator(v.Name()).SetConfig(map[string]any{"limit": v.limit}) +} + // Validate implements the ASTValidator interface method. func (v bindNestingLimitValidator) Validate(e *Env, _ ValidatorConfig, a *ast.AST, iss *Issues) { root := ast.NavigateAST(a) @@ -525,6 +533,47 @@ func (v bindNestingLimitValidator) Validate(e *Env, _ ValidatorConfig, a *ast.AS } } +func (v regexProgramSizeLimitValidator) Validate(e *Env, _ ValidatorConfig, a *ast.AST, iss *Issues) { + if v.limit <= 0 { + return + } + root := ast.NavigateAST(a) + callExprs := ast.MatchDescendants(root, ast.KindMatcher(ast.CallKind)) + for _, call := range callExprs { + c := call.AsCall() + fn := c.FunctionName() + if !isRegexFunctionName(fn) { + continue + } + args := c.Args() + var regexArgIndex int + if (fn == overloads.Matches || fn == "matches") && c.Target() != nil { + regexArgIndex = 0 + } else { + regexArgIndex = 1 + } + if len(args) <= regexArgIndex { + continue + } + arg := args[regexArgIndex] + if arg.Kind() != ast.LiteralKind { + continue + } + pattern, ok := arg.AsLiteral().Value().(string) + if !ok { + continue + } + sz, err := types.RegexProgramSize(pattern) + if err != nil { + // Invalid regex literals are handled in a different validator. + continue + } + if sz > v.limit { + iss.ReportErrorAtID(arg.ID(), "regex program size %d exceeds limit of %d", sz, v.limit) + } + } +} + func isEmptyRangeComprehension(e ast.NavigableExpr) bool { if e.Kind() != ast.ComprehensionKind { return false @@ -544,3 +593,25 @@ func isCelBind(e ast.NavigableExpr) bool { loopCond.Kind() == ast.LiteralKind && loopCond.AsLiteral().Value() == false && loopStep.Kind() == ast.IdentKind && loopStep.AsIdent() == compre.AccuVar() } + +func isRegexFunctionName(fn string) bool { + return fn == overloads.Matches || fn == "matches" || fn == "regex.extract" || fn == "regex.extractAll" || fn == "regex.replace" +} + +func validatorIntConfig(val *env.Validator, configKey string) (int, error) { + if limit, found := val.ConfigValue(configKey); found { + // In case of protos, config value is of type google.protobuf.Value, which numeric values are always a double. + if v, isDouble := limit.(float64); isDouble { + if v != float64(int64(v)) { + return 0, fmt.Errorf("invalid validator: %s, %s value is not a whole number: %v", val.Name, configKey, limit) + } + return int(v), nil + } + + if v, isInt := limit.(int); isInt { + return v, nil + } + return 0, fmt.Errorf("invalid validator: %s unsupported %s type: %v", val.Name, configKey, limit) + } + return 0, fmt.Errorf("invalid validator: %s missing %s", val.Name, configKey) +} diff --git a/cel/validator_test.go b/cel/validator_test.go index 606a5b6d9..99795da0d 100644 --- a/cel/validator_test.go +++ b/cel/validator_test.go @@ -204,6 +204,77 @@ func TestValidateRegexLiterals(t *testing.T) { } } +func TestValidateRegexProgramSizeLimit(t *testing.T) { + opts := []EnvOption{ + Variable("x", types.StringType), + ASTValidators(ValidateRegexProgramSizeLimit(5)), + } + + tests := []struct { + expr string + iss string + }{ + { + expr: `'hello'.matches('el*')`, + }, + { + expr: `'hello'.matches('(a|b)*[0-9]+')`, + iss: ` + ERROR: :1:17: regex program size 8 exceeds limit of 5 + | 'hello'.matches('(a|b)*[0-9]+') + | ................^`, + }, + { + expr: `'hello'.matches(x)`, + }, + } + for _, tst := range tests { + tc := tst + t.Run(tc.expr, func(t *testing.T) { + _, err := Compile(tc.expr, opts...) + if tc.iss != "" { + if err == nil { + t.Fatalf("Compile(%v) returned ast, expected error: %v", tc.expr, tc.iss) + } + if !test.Compare(err.Error(), tc.iss) { + t.Fatalf("Compile(%v) returned %v, expected error: %v", tc.expr, err, tc.iss) + } + return + } + if err != nil { + t.Fatalf("Compile(%v) failed: %v", tc.expr, err) + } + }) + } +} + +func TestValidateRegexProgramSizeLimitToConfig(t *testing.T) { + val := ValidateRegexProgramSizeLimit(5) + cfg := val.(ConfigurableASTValidator).ToConfig() + if cfg.Name != regexProgramSizeLimitValidatorName { + t.Errorf("ToConfig().Name = %s, wanted %s", cfg.Name, regexProgramSizeLimitValidatorName) + } + if limit, ok := cfg.ConfigValue("limit"); !ok || limit != 5 { + t.Errorf("ToConfig().ConfigValue('limit') = %v, wanted 5", limit) + } +} + +func TestValidateRegexProgramSizeLimitFactory(t *testing.T) { + val := ValidateRegexProgramSizeLimit(5) + cfg := val.(ConfigurableASTValidator).ToConfig() + fac, ok := astValidatorFactories[regexProgramSizeLimitValidatorName] + if !ok { + t.Fatalf("missing factory for %s", regexProgramSizeLimitValidatorName) + } + vFromCfg, err := fac(cfg) + if err != nil { + t.Fatalf("fac(cfg) failed: %v", err) + } + if vFromCfg.Name() != regexProgramSizeLimitValidatorName { + t.Errorf("vFromCfg.Name() = %s, wanted %s", vFromCfg.Name(), regexProgramSizeLimitValidatorName) + } +} + func TestValidateHomogeneousAggregateLiterals(t *testing.T) { env, err := NewCustomEnv( Variable("name", StringType), diff --git a/common/types/BUILD.bazel b/common/types/BUILD.bazel index 37d4df495..7dda3ede9 100644 --- a/common/types/BUILD.bazel +++ b/common/types/BUILD.bazel @@ -26,6 +26,7 @@ go_library( "optional.go", "overflow.go", "provider.go", + "regex.go", "string.go", "timestamp.go", "types.go", @@ -71,6 +72,7 @@ go_test( "object_test.go", "optional_test.go", "provider_test.go", + "regex_test.go", "string_test.go", "timestamp_test.go", "types_test.go", diff --git a/common/types/regex.go b/common/types/regex.go new file mode 100644 index 000000000..14173eb52 --- /dev/null +++ b/common/types/regex.go @@ -0,0 +1,49 @@ +// Copyright 2026 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 types + +import ( + "fmt" + "regexp" + "regexp/syntax" +) + +// RegexProgramSize calculates the instruction count (program plan size) of a regex pattern. +func RegexProgramSize(pattern string) (int, error) { + re, err := syntax.Parse(pattern, syntax.Perl) + if err != nil { + return 0, err + } + prog, err := syntax.Compile(re) + if err != nil { + return 0, err + } + return len(prog.Inst), nil +} + +// CompileRegexWithLimit compiles a regex pattern and verifies that its program plan size does not exceed limit. +// A limit <= 0 means unbounded. +func CompileRegexWithLimit(pattern string, limit int) (*regexp.Regexp, error) { + if limit > 0 { + sz, err := RegexProgramSize(pattern) + if err != nil { + return nil, err + } + if sz > limit { + return nil, fmt.Errorf("regex program size %d exceeds limit of %d", sz, limit) + } + } + return regexp.Compile(pattern) +} diff --git a/common/types/regex_test.go b/common/types/regex_test.go new file mode 100644 index 000000000..033a43979 --- /dev/null +++ b/common/types/regex_test.go @@ -0,0 +1,76 @@ +// Copyright 2026 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 types + +import ( + "testing" +) + +func TestRegexProgramSize(t *testing.T) { + tests := []struct { + pattern string + minSize int + hasError bool + }{ + {pattern: "a", minSize: 1}, + {pattern: "el*", minSize: 3}, + {pattern: "(a|b)*[0-9]+", minSize: 5}, + {pattern: "(", hasError: true}, + } + + for _, tc := range tests { + sz, err := RegexProgramSize(tc.pattern) + if tc.hasError { + if err == nil { + t.Errorf("RegexProgramSize(%q) expected error, got nil", tc.pattern) + } + continue + } + if err != nil { + t.Errorf("RegexProgramSize(%q) unexpected error: %v", tc.pattern, err) + continue + } + if sz < tc.minSize { + t.Errorf("RegexProgramSize(%q) = %d, expected >= %d", tc.pattern, sz, tc.minSize) + } + } +} + +func TestCompileRegexWithLimit(t *testing.T) { + tests := []struct { + pattern string + limit int + hasError bool + }{ + {pattern: "el*", limit: 10}, + {pattern: "el*", limit: 0}, + {pattern: "el*", limit: -1}, + {pattern: "(a|b)*[0-9]+", limit: 5, hasError: true}, + {pattern: "(", limit: 10, hasError: true}, + } + + for _, tc := range tests { + _, err := CompileRegexWithLimit(tc.pattern, tc.limit) + if tc.hasError { + if err == nil { + t.Errorf("CompileRegexWithLimit(%q, %d) expected error, got nil", tc.pattern, tc.limit) + } + } else { + if err != nil { + t.Errorf("CompileRegexWithLimit(%q, %d) unexpected error: %v", tc.pattern, tc.limit, err) + } + } + } +} diff --git a/ext/regex_test.go b/ext/regex_test.go index 24c5582ea..9a61b7946 100644 --- a/ext/regex_test.go +++ b/ext/regex_test.go @@ -415,3 +415,77 @@ func TestRegexCosts(t *testing.T) { }) } } + +func TestRegexProgramSizeLimit(t *testing.T) { + overloads := []struct { + name string + expr string + }{ + { + name: "matches", + expr: `'a1'.matches(pat)`, + }, + { + name: "regex.extract", + expr: `regex.extract('a1', pat)`, + }, + { + name: "regex.extractAll", + expr: `regex.extractAll('a1', pat)`, + }, + { + name: "regex.replace 3-arg", + expr: `regex.replace('a1', pat, 'x')`, + }, + { + name: "regex.replace 4-arg", + expr: `regex.replace('a1', pat, 'x', 1)`, + }, + } + + t.Run("ExceedsLimit", func(t *testing.T) { + for _, tc := range overloads { + t.Run(tc.name, func(t *testing.T) { + prg, err := cel.Compile(tc.expr, + cel.OptionalTypes(), + Regex(), + cel.RegexProgramSizeLimit(5), + cel.Variable("pat", cel.StringType), + ) + if err != nil { + t.Fatalf("cel.Compile(%s) failed: %v", tc.expr, err) + } + _, _, err = prg.Eval(map[string]any{"pat": "(a|b)*[0-9]+"}) + if err == nil { + t.Fatalf("expected runtime error for regex program size exceeding limit") + } + if !strings.Contains(err.Error(), "regex program size 8 exceeds limit of 5") { + t.Fatalf("got error %v, expected error containing 'regex program size 8 exceeds limit of 5'", err) + } + }) + } + }) + + t.Run("WithinLimit", func(t *testing.T) { + for _, tc := range overloads { + t.Run(tc.name, func(t *testing.T) { + prg, err := cel.Compile(tc.expr, + cel.OptionalTypes(), + Regex(), + cel.RegexProgramSizeLimit(10), + cel.Variable("pat", cel.StringType), + ) + if err != nil { + t.Fatalf("cel.Compile(%s) failed: %v", tc.expr, err) + } + val, _, err := prg.Eval(map[string]any{"pat": "(a|b)*[0-9]+"}) + if err != nil { + t.Fatalf("prg.Eval(%s) unexpected error: %v", tc.expr, err) + } + if val == nil { + t.Fatalf("prg.Eval(%s) returned nil result", tc.expr) + } + }) + } + }) +} diff --git a/interpreter/decorators.go b/interpreter/decorators.go index 9c973664a..6c48e5c1b 100644 --- a/interpreter/decorators.go +++ b/interpreter/decorators.go @@ -15,6 +15,8 @@ package interpreter import ( + "fmt" + "github.com/google/cel-go/common/overloads" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" @@ -169,6 +171,77 @@ func decRegexOptimizer(regexOptimizations ...*RegexOptimization) InterpretableDe } } +func decRegexProgramSizeLimit(limit int) InterpretableDecoratorV2 { + return func(i InterpretableV2) (InterpretableV2, error) { + if limit <= 0 { + return i, nil + } + call, ok := i.(InterpretableCall) + if !ok { + return i, nil + } + if !isRegexFunction(call.Function(), call.OverloadID()) || len(call.Args()) < 2 { + return i, nil + } + regexArg := call.Args()[1] + if constVal, isConst := regexArg.(InterpretableConst); isConst { + if pattern, ok := constVal.Value().(types.String); ok { + sz, err := types.RegexProgramSize(string(pattern)) + if err != nil { + return i, nil + } + if sz > limit { + return nil, fmt.Errorf("regex program size %d exceeds limit of %d", sz, limit) + } + } + return i, nil + } + return ®exLimitCall{InterpretableCall: call, limit: limit}, nil + } +} + +func isRegexFunction(fn, overload string) bool { + switch fn { + case overloads.Matches, "regex.extract", "regex.extractAll", "regex.replace": + return true + } + switch overload { + case overloads.Matches, overloads.MatchesString, + "regex_extract_string_string", "regex_extractAll_string_string", + "regex_replace_string_string_string", "regex_replace_string_string_string_int": + return true + } + return false +} + +type regexLimitCall struct { + InterpretableCall + limit int +} + +func (r *regexLimitCall) Exec(frame *ExecutionFrame) ref.Val { + args := r.Args() + if len(args) >= 2 { + patternVal := args[1].Exec(frame) + if types.IsError(patternVal) { + return patternVal + } + if types.IsUnknown(patternVal) { + return patternVal + } + if pat, ok := patternVal.(types.String); ok { + sz, err := types.RegexProgramSize(string(pat)) + if err != nil { + return types.WrapErr(err) + } + if sz > r.limit { + return types.WrapErr(fmt.Errorf("regex program size %d exceeds limit of %d", sz, r.limit)) + } + } + } + return r.InterpretableCall.Exec(frame) +} + func maybeOptimizeConstUnary(i InterpretableV2, call InterpretableCall) (InterpretableV2, error) { args := call.Args() if len(args) != 1 { diff --git a/interpreter/interpreter.go b/interpreter/interpreter.go index ef13ab922..29df9d41e 100644 --- a/interpreter/interpreter.go +++ b/interpreter/interpreter.go @@ -227,6 +227,11 @@ func CompileRegexConstants(regexOptimizations ...*RegexOptimization) PlannerOpti return CustomDecoratorV2(decRegexOptimizer(regexOptimizations...)) } +// RegexProgramSizeLimit caps the maximum regex program plan size permitted during evaluation. +func RegexProgramSizeLimit(limit int) PlannerOption { + return CustomDecoratorV2(decRegexProgramSizeLimit(limit)) +} + type exprInterpreter struct { dispatcher Dispatcher container *containers.Container diff --git a/interpreter/interpreter_test.go b/interpreter/interpreter_test.go index bcb0f5758..b4b090ed8 100644 --- a/interpreter/interpreter_test.go +++ b/interpreter/interpreter_test.go @@ -2113,6 +2113,72 @@ func TestInterpreter_InterruptableEval(t *testing.T) { } } +func TestInterpreter_RegexProgramSizeLimit(t *testing.T) { + tcConst := testCase{ + expr: `'hello'.matches('(a|b)*[0-9]+')`, + } + _, _, err := program(t, &tcConst, RegexProgramSizeLimit(5)) + if err == nil { + t.Fatalf("expected program creation error for constant regex exceeding limit") + } + if !strings.Contains(err.Error(), "regex program size 8 exceeds limit of 5") { + t.Errorf("got error %v, wanted error containing 'regex program size 8 exceeds limit of 5'", err) + } + + tcDyn := testCase{ + expr: `'hello'.matches(pattern)`, + vars: []*decls.VariableDecl{ + decls.NewVariable("pattern", types.StringType), + }, + in: map[string]any{ + "pattern": "(a|b)*[0-9]+", + }, + } + prg, frame, err := program(t, &tcDyn, RegexProgramSizeLimit(5)) + if err != nil { + t.Fatalf("program() failed: %v", err) + } + out := prg.Exec(frame) + frame.Close() + if !types.IsError(out) || !strings.Contains(out.(*types.Err).String(), "regex program size 8 exceeds limit of 5") { + t.Errorf("got %v, wanted regex program size limit error", out) + } + + tcValid := testCase{ + expr: `'hello'.matches(pattern)`, + vars: []*decls.VariableDecl{ + decls.NewVariable("pattern", types.StringType), + }, + in: map[string]any{ + "pattern": "el*", + }, + out: true, + } + prgValid, frameValid, err := program(t, &tcValid, RegexProgramSizeLimit(5)) + if err != nil { + t.Fatalf("program() failed: %v", err) + } + outValid := prgValid.Exec(frameValid) + frameValid.Close() + if outValid != types.True { + t.Errorf("got %v, wanted true", outValid) + } + + // Non-regex function should not be modified by RegexProgramSizeLimit decorator + tcOther := testCase{ + expr: `'hello'.contains('e')`, + } + prgOther, frameOther, err := program(t, &tcOther, RegexProgramSizeLimit(5)) + if err != nil { + t.Fatalf("program() failed: %v", err) + } + outOther := prgOther.Exec(frameOther) + frameOther.Close() + if outOther != types.True { + t.Errorf("got %v, wanted true", outOther) + } +} + func TestInterpreter_ExhaustiveLogicalOrEquals(t *testing.T) { // a || b == "b" // Operator "==" is at Expr 4, should be evaluated though "a" is true From 32243258a4b24d1ba45ee7a0c9d91a865b61f898 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Mon, 27 Jul 2026 12:38:15 -0700 Subject: [PATCH 03/37] Improve the efficiency of optMap / optFlatMap (#1387) --- cel/cel_test.go | 12 ++++++++++++ cel/library.go | 49 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/cel/cel_test.go b/cel/cel_test.go index 159e5c592..459f80cdc 100644 --- a/cel/cel_test.go +++ b/cel/cel_test.go @@ -3196,6 +3196,18 @@ func TestOptionalValuesEval(t *testing.T) { }, out: types.OptionalOf(types.Int(43)), }, + { + expr: `{0: 10}[?0].optMap(v, v + 1)`, + out: types.OptionalOf(types.Int(11)), + }, + { + expr: `{0: 10}[?0].optMap(a, a + 1).optMap(b, b * 2)`, + out: types.OptionalOf(types.Int(22)), + }, + { + expr: `{0: 10}[?1].optMap(a, a + 1).optMap(b, b * 2)`, + out: types.OptionalNone, + }, { expr: `optional.ofNonZeroValue(z).or(optional.of(10)).value() == 42`, in: map[string]any{ diff --git a/cel/library.go b/cel/library.go index 332eb3f17..a14025222 100644 --- a/cel/library.go +++ b/cel/library.go @@ -43,6 +43,7 @@ const ( optionalUnwrapFunc = "optional.unwrap" valueFunc = "value" unusedIterVar = "#unused" + targetVar = "@target" ) // Library provides a collection of EnvOption and ProgramOption values used to configure a CEL @@ -609,22 +610,38 @@ func optMap(meh MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, * return nil, meh.NewError(varIdent.ID(), "optMap() variable name must be a simple identifier") } mapExpr := args[1] - return meh.NewCall( + targetIdent := target + if target.Kind() != ast.IdentKind { + targetIdent = meh.NewIdent(targetVar) + } + res := meh.NewCall( operators.Conditional, - meh.NewMemberCall(hasValueFunc, target), + meh.NewMemberCall(hasValueFunc, targetIdent), meh.NewCall(optionalOfFunc, meh.NewComprehension( meh.NewList(), unusedIterVar, varName, - meh.NewMemberCall(valueFunc, meh.Copy(target)), + meh.NewMemberCall(valueFunc, meh.Copy(targetIdent)), meh.NewLiteral(types.False), meh.NewIdent(varName), mapExpr, ), ), meh.NewCall(optionalNoneFunc), - ), nil + ) + if target.Kind() != ast.IdentKind { + return meh.NewComprehension( + meh.NewList(), + unusedIterVar, + targetVar, + target, + meh.NewLiteral(types.False), + meh.NewIdent(targetVar), + res, + ), nil + } + return res, nil } func optFlatMap(meh MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *Error) { @@ -637,20 +654,36 @@ func optFlatMap(meh MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Exp return nil, meh.NewError(varIdent.ID(), "optFlatMap() variable name must be a simple identifier") } mapExpr := args[1] - return meh.NewCall( + targetIdent := target + if target.Kind() != ast.IdentKind { + targetIdent = meh.NewIdent(targetVar) + } + res := meh.NewCall( operators.Conditional, - meh.NewMemberCall(hasValueFunc, target), + meh.NewMemberCall(hasValueFunc, targetIdent), meh.NewComprehension( meh.NewList(), unusedIterVar, varName, - meh.NewMemberCall(valueFunc, meh.Copy(target)), + meh.NewMemberCall(valueFunc, meh.Copy(targetIdent)), meh.NewLiteral(types.False), meh.NewIdent(varName), mapExpr, ), meh.NewCall(optionalNoneFunc), - ), nil + ) + if target.Kind() != ast.IdentKind { + return meh.NewComprehension( + meh.NewList(), + unusedIterVar, + targetVar, + target, + meh.NewLiteral(types.False), + meh.NewIdent(targetVar), + res, + ), nil + } + return res, nil } func optUnwrap(value ref.Val) ref.Val { From 8bbb639931c782be1de2c55c009f0dc0e079ff6c Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Tue, 28 Jul 2026 13:44:27 -0700 Subject: [PATCH 04/37] Fix the expression limit node test (#1390) --- cel/cel_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cel/cel_test.go b/cel/cel_test.go index 459f80cdc..6e8813934 100644 --- a/cel/cel_test.go +++ b/cel/cel_test.go @@ -3826,9 +3826,9 @@ func TestExpressionNodeLimit(t *testing.T) { { name: "chained optMap of various complexity exceeding default limit", expr: "x.optMap(a, [a, a]).optMap(b, {b: b}).optMap(c, c + 1).optMap(d, d + 2).optMap(e, e + 3).optMap(f, f + 4).optMap(g, g + 5).optMap(h, h + 6).optMap(i, i + 7).optMap(j, j + 8).optMap(k, k + 9).optMap(l, l + 10).optMap(m, m + 11).optMap(n, n + 12)", - limit: 0, // default limit 100,000 + limit: 200, // default limit 100,000 expectErr: true, - errSubstring: "expression count exceeds limit of 100000 while expanding macro 'optMap'", + errSubstring: "expression count exceeds limit of 200 while expanding macro 'optMap'", }, { name: "chained optMap with unbounded limit (-1)", From 8c1485e1cbf94f8973b4b0616f466a681012d5db Mon Sep 17 00:00:00 2001 From: Jonathan Tatum Date: Wed, 29 Jul 2026 12:12:15 -0700 Subject: [PATCH 05/37] Correct documented default max value for range (#1392) --- ext/lists.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/lists.go b/ext/lists.go index 3d0e67642..2196d218c 100644 --- a/ext/lists.go +++ b/ext/lists.go @@ -192,7 +192,7 @@ func ListsVersion(version uint32) ListsOption { } // ListsMaxRangeSize sets the maximum number of elements lists.range() will -// allocate. If not set, the default is 10,000,000. Setting this to zero +// allocate. If not set, the default is 1,000,000. Setting this to zero // disables the limit (not recommended). func ListsMaxRangeSize(size int64) ListsOption { return func(lib *listsLib) *listsLib { From c15365a7610acb37a6c8e26b6af6ee0eaf7c10ce Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Wed, 29 Jul 2026 21:25:16 -0700 Subject: [PATCH 06/37] Simplify support for native object types (#1393) * Simplify support for native object types Shift most of the logic from the NativeTypeProvider over to the NativeObject wrappers. This change is a stepping stone to consolidating type providers into the common/types package and making it less painful to manage type adaptation. * Fix for embedded type field traversal * Robustness checks for field retrieval with nil embedded pointers to structs Since promoted fields were previously not discovered, field setting also needed to be updated to ensure nested pointers to structs with promoted fields were initialized properly. * Note for future work --- ext/native.go | 288 ++++++++++++++++++++++++--------------------- ext/native_test.go | 271 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 414 insertions(+), 145 deletions(-) diff --git a/ext/native.go b/ext/native.go index c03b053f1..5ac2f9328 100644 --- a/ext/native.go +++ b/ext/native.go @@ -181,14 +181,10 @@ type nativeTypeOptions struct { // ParseStructTags configures if native types field names should be overridable by CEL struct tags. // This is equivalent to ParseStructTag("cel") func ParseStructTags(enabled bool) NativeTypesOption { - return func(ntp *nativeTypeOptions) error { - if enabled { - ntp.fieldNameHandler = fieldNameByTag("cel") - } else { - ntp.fieldNameHandler = nil - } - return nil + if enabled { + return ParseStructTag("cel") } + return ParseStructField(nil) } // ParseStructTag configures the struct tag to parse. The 0th item in the tag is used as the name of the CEL field. @@ -196,10 +192,7 @@ func ParseStructTags(enabled bool) NativeTypesOption { // If the tag to parse is "cel" and the struct field has tag cel:"foo", the CEL struct field will be "foo". // If the tag to parse is "json" and the struct field has tag json:"foo,omitempty", the CEL struct field will be "foo". func ParseStructTag(tag string) NativeTypesOption { - return func(ntp *nativeTypeOptions) error { - ntp.fieldNameHandler = fieldNameByTag(tag) - return nil - } + return ParseStructField(fieldNameByTag(tag)) } // ParseStructField configures how to parse Go struct fields. It can be used to customize struct field parsing. @@ -270,38 +263,15 @@ func (tp *nativeTypeProvider) FindStructType(typeName string) (*types.Type, bool if _, found := tp.nativeTypes[typeName]; found { return types.NewTypeTypeWithParam(types.NewObjectType(typeName)), true } - if celType, found := tp.baseProvider.FindStructType(typeName); found { - return celType, true - } return tp.baseProvider.FindStructType(typeName) } -func toFieldName(fieldNameHandler NativeTypesFieldNameHandler, f reflect.StructField) string { - if fieldNameHandler == nil { - return f.Name - } - - return fieldNameHandler(f) -} - // FindStructFieldNames looks up the type definition first from the native types, then from // the backing provider type set. If found, a set of field names corresponding to the type // will be returned. func (tp *nativeTypeProvider) FindStructFieldNames(typeName string) ([]string, bool) { if t, found := tp.nativeTypes[typeName]; found { - fieldCount := t.refType.NumField() - fields := make([]string, 0, fieldCount) - for i := 0; i < fieldCount; i++ { - fieldName := toFieldName(tp.options.fieldNameHandler, t.refType.Field(i)) - if isSkippedFieldName(fieldName) { - continue - } - fields = append(fields, fieldName) - } - return fields, true - } - if celTypeFields, found := tp.baseProvider.FindStructFieldNames(typeName); found { - return celTypeFields, true + return t.FieldNames(), true } return tp.baseProvider.FindStructFieldNames(typeName) } @@ -309,55 +279,18 @@ func (tp *nativeTypeProvider) FindStructFieldNames(typeName string) ([]string, b // FindStructFieldType looks up a native type's field definition, and if the type name is not a native // type then proxies to the composed types.Provider func (tp *nativeTypeProvider) FindStructFieldType(typeName, fieldName string) (*types.FieldType, bool) { - t, found := tp.nativeTypes[typeName] - if !found { - return tp.baseProvider.FindStructFieldType(typeName, fieldName) - } - refField, isDefined := t.hasField(fieldName) - if !found || !isDefined { - return nil, false - } - celType, ok := convertToCelType(refField.Type) - if !ok { - return nil, false + if t, found := tp.nativeTypes[typeName]; found { + return t.FindFieldType(fieldName) } - return &types.FieldType{ - Type: celType, - IsSet: func(obj any) bool { - refVal := reflect.Indirect(reflect.ValueOf(obj)) - refField := refVal.FieldByName(refField.Name) - return !refField.IsZero() - }, - GetFrom: func(obj any) (any, error) { - refVal := reflect.Indirect(reflect.ValueOf(obj)) - refField := refVal.FieldByName(refField.Name) - return getFieldValue(refField), nil - }, - }, true + return tp.baseProvider.FindStructFieldType(typeName, fieldName) } // NewValue implements the ref.TypeProvider interface method. func (tp *nativeTypeProvider) NewValue(typeName string, fields map[string]ref.Val) ref.Val { - t, found := tp.nativeTypes[typeName] - if !found { - return tp.baseProvider.NewValue(typeName, fields) - } - refPtr := reflect.New(t.refType) - refVal := refPtr.Elem() - for fieldName, val := range fields { - refFieldDef, isDefined := t.hasField(fieldName) - if !isDefined { - return types.NewErr("no such field: %s", fieldName) - } - fieldVal, err := val.ConvertToNative(refFieldDef.Type) - if err != nil { - return types.NewErrFromString(err.Error()) - } - refField := refVal.FieldByIndex(refFieldDef.Index) - refFieldVal := reflect.ValueOf(fieldVal) - refField.Set(refFieldVal) + if t, found := tp.nativeTypes[typeName]; found { + return t.NewValue(tp, fields) } - return tp.NativeToValue(refPtr.Interface()) + return tp.baseProvider.NewValue(typeName, fields) } // NewValue adapts native values to CEL values and will proxy to the composed type adapter @@ -382,7 +315,7 @@ func (tp *nativeTypeProvider) NativeToValue(val any) ref.Val { case []byte: return tp.baseAdapter.NativeToValue(val) default: - if refVal.Type().Elem() == reflect.TypeOf(byte(0)) { + if refVal.Type().Elem() == reflect.TypeFor[byte]() { return tp.baseAdapter.NativeToValue(val) } return types.NewDynamicList(tp, val) @@ -401,8 +334,8 @@ func (tp *nativeTypeProvider) NativeToValue(val any) ref.Val { // a composed base adapter never sees unregistered structs it wants // to convert itself. typeName := fmt.Sprintf("%s.%s", simplePkgAlias(refVal.Type().PkgPath()), refVal.Type().Name()) - if _, found := tp.nativeTypes[typeName]; found { - return tp.newNativeObject(val, rawVal) + if ntype, found := tp.nativeTypes[typeName]; found { + return tp.newNativeObject(val, ntype, rawVal) } return tp.baseAdapter.NativeToValue(val) } @@ -472,11 +405,7 @@ func convertToCelType(refType reflect.Type) (*cel.Type, bool) { return nil, false } -func (tp *nativeTypeProvider) newNativeObject(val any, refValue reflect.Value) ref.Val { - valType, err := newNativeType(tp.options.fieldNameHandler, refValue.Type()) - if err != nil { - return types.NewErrFromString(err.Error()) - } +func (tp *nativeTypeProvider) newNativeObject(val any, valType *nativeType, refValue reflect.Value) ref.Val { return &nativeObj{ Adapter: tp, val: val, @@ -517,18 +446,12 @@ func (o *nativeObj) ConvertToNative(typeDesc reflect.Type) (any, error) { return structpb.NewStructValue(jsonStruct.(*structpb.Struct)), nil case jsonStructType: refVal := reflect.Indirect(o.refValue) - refType := refVal.Type() fields := make(map[string]*structpb.Value, refVal.NumField()) - for i := 0; i < refVal.NumField(); i++ { - fieldType := refType.Field(i) - fieldValue := refVal.Field(i) + for fieldName, fieldType := range o.valType.fieldsByName { + fieldValue := refVal.FieldByIndex(fieldType.Index) if !fieldValue.IsValid() || fieldValue.IsZero() { continue } - fieldName := toFieldName(o.valType.fieldNameHandler, fieldType) - if isSkippedFieldName(fieldName) { - continue - } fieldCELVal := o.NativeToValue(fieldValue.Interface()) fieldJSONVal, err := fieldCELVal.ConvertToNative(jsonValueType) if err != nil { @@ -613,7 +536,7 @@ func (o *nativeObj) getReflectedField(field ref.Val) (reflect.Value, ref.Val) { return reflect.Value{}, types.NewErr("no such field: %s", fieldName) } refVal := reflect.Indirect(o.refValue) - return refVal.FieldByIndex(refField.Index), nil + return safeGetFieldByIndex(refVal, refField.Index), nil } // Type implements the ref.Val interface method. @@ -658,8 +581,11 @@ func newNativeTypes(fieldNameHandler NativeTypesFieldNameHandler, rawType reflec } result = append(result, nt) - for idx := 0; idx < t.NumField(); idx++ { - iterateStructMembers(t.Field(idx).Type) + for _, field := range reflect.VisibleFields(t) { + if !field.IsExported() || !isSupportedType(field.Type) { + continue + } + iterateStructMembers(field.Type) } } iterateStructMembers(rawType) @@ -671,6 +597,13 @@ var ( errDuplicatedFieldName = errors.New("field name already exists in struct") ) +func toFieldName(fieldNameHandler NativeTypesFieldNameHandler, f reflect.StructField) string { + if fieldNameHandler == nil { + return f.Name + } + return fieldNameHandler(f) +} + func newNativeType(fieldNameHandler NativeTypesFieldNameHandler, rawType reflect.Type) (*nativeType, error) { refType := rawType if refType.Kind() == reflect.Pointer { @@ -680,35 +613,34 @@ func newNativeType(fieldNameHandler NativeTypesFieldNameHandler, rawType reflect return nil, fmt.Errorf("unsupported reflect.Type %v, must be reflect.Struct", rawType) } - // Since naming collisions can only happen with struct tag parsing, we only check for them if it is enabled. - if fieldNameHandler != nil { - fieldNames := make(map[string]struct{}) - - for idx := 0; idx < refType.NumField(); idx++ { - field := refType.Field(idx) - fieldName := toFieldName(fieldNameHandler, field) - if isSkippedFieldName(fieldName) { - continue - } - if _, found := fieldNames[fieldName]; found { - return nil, fmt.Errorf("invalid field name `%s` in struct `%s`: %w", fieldName, refType.Name(), errDuplicatedFieldName) - } else { - fieldNames[fieldName] = struct{}{} - } + // Collect the set of visible / exported fields, ensuring that unsupported types and sentinel + // 'skip' tags such as `-` are filtered out. + fieldsByName := make(map[string]reflect.StructField) + for _, field := range reflect.VisibleFields(refType) { + if !field.IsExported() || !isSupportedType(field.Type) { + continue + } + fieldName := toFieldName(fieldNameHandler, field) + if isSkippedFieldName(fieldName) { + continue } + if _, found := fieldsByName[fieldName]; found { + return nil, fmt.Errorf("invalid field name `%s` in struct `%s`: %w", fieldName, refType.Name(), errDuplicatedFieldName) + } + fieldsByName[fieldName] = field } return &nativeType{ - typeName: fmt.Sprintf("%s.%s", simplePkgAlias(refType.PkgPath()), refType.Name()), - refType: refType, - fieldNameHandler: fieldNameHandler, + typeName: fmt.Sprintf("%s.%s", simplePkgAlias(refType.PkgPath()), refType.Name()), + refType: refType, + fieldsByName: fieldsByName, }, nil } type nativeType struct { - typeName string - refType reflect.Type - fieldNameHandler NativeTypesFieldNameHandler + typeName string + refType reflect.Type + fieldsByName map[string]reflect.StructField } // ConvertToNative implements ref.Val.ConvertToNative. @@ -756,41 +688,123 @@ func (t *nativeType) Value() any { return t.typeName } -// fieldByName returns the corresponding reflect.StructField for the give name either by matching -// field tag or field name. -func (t *nativeType) fieldByName(fieldName string) (reflect.StructField, bool) { - if isSkippedFieldName(fieldName) { +// hasField returns whether a field name has a corresponding Golang reflect.StructField +func (t *nativeType) hasField(fieldName string) (reflect.StructField, bool) { + f, found := t.fieldsByName[fieldName] + if !found { return reflect.StructField{}, false } + return f, true +} - if t.fieldNameHandler == nil { - return t.refType.FieldByName(fieldName) +// FieldNames provides the list of field names for this type. +func (t *nativeType) FieldNames() []string { + fields := make([]string, 0, len(t.fieldsByName)) + for fieldName := range t.fieldsByName { + fields = append(fields, fieldName) } + return fields +} - for i := 0; i < t.refType.NumField(); i++ { - f := t.refType.Field(i) - if toFieldName(t.fieldNameHandler, f) == fieldName { - return f, true - } +// FindFieldType looks up a field by name and provides the type and accessor functions +// required for type identification at check-time and accessors for use at runtime. +func (t *nativeType) FindFieldType(fieldName string) (*types.FieldType, bool) { + refField, found := t.hasField(fieldName) + if !found { + return nil, false } - - return reflect.StructField{}, false + celType, ok := convertToCelType(refField.Type) + if !ok { + return nil, false + } + return &types.FieldType{ + Type: celType, + IsSet: func(obj any) bool { + // TODO: determine what to do if refVal is Invalid() + refVal := reflect.Indirect(reflect.ValueOf(obj)) + // Check if field path exists and is set + refFieldVal := safeGetFieldByIndex(refVal, refField.Index) + return refFieldVal.IsValid() && !refFieldVal.IsZero() + }, + GetFrom: func(obj any) (any, error) { + // TODO: determine what to do if refVal is Invalid() + refVal := reflect.Indirect(reflect.ValueOf(obj)) + // Check if field path exists and is set + refFieldVal := safeGetFieldByIndex(refVal, refField.Index) + return getFieldValue(refFieldVal), nil + }, + }, true } -// hasField returns whether a field name has a corresponding Golang reflect.StructField -func (t *nativeType) hasField(fieldName string) (reflect.StructField, bool) { - f, found := t.fieldByName(fieldName) - if !found || !f.IsExported() || !isSupportedType(f.Type) { - return reflect.StructField{}, false +// NewValue constructs a new native Go struct instance populated with the given field values. +func (t *nativeType) NewValue(adapter types.Adapter, fields map[string]ref.Val) ref.Val { + refPtr := reflect.New(t.refType) + refVal := refPtr.Elem() + for fieldName, val := range fields { + refFieldDef, isDefined := t.hasField(fieldName) + if !isDefined { + return types.NewErr("no such field: %s", fieldName) + } + fieldVal, err := val.ConvertToNative(refFieldDef.Type) + if err != nil { + return types.NewErrFromString(err.Error()) + } + refField := safeSetFieldByIndex(refVal, refFieldDef.Index) + if !refField.IsValid() { + return types.NewErr("cannot set field: %s", fieldName) + } + refField.Set(reflect.ValueOf(fieldVal)) } - return f, true + return adapter.NativeToValue(refPtr.Interface()) } func adaptFieldValue(adapter types.Adapter, refField reflect.Value) ref.Val { return adapter.NativeToValue(getFieldValue(refField)) } +// safeSetFieldByIndex traverses refField.Index to set a field value. +// If an intermediate pointer along the path is nil, it allocates a new +// instance of the struct that the pointer references. +func safeSetFieldByIndex(v reflect.Value, index []int) reflect.Value { + for _, i := range index { + if v.Kind() == reflect.Pointer { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + if v.Kind() != reflect.Struct || i >= v.NumField() { + return reflect.Value{} + } + v = v.Field(i) + } + return v +} + +// safeGetFieldByIndex traverses refField.Index. If an intermediate pointer along +// the path is nil, it substitutes a pointer to an empty struct instance of that type. +func safeGetFieldByIndex(v reflect.Value, index []int) reflect.Value { + for _, i := range index { + if v.Kind() == reflect.Pointer { + if v.IsNil() { + // Intermediate pointer to struct is nil: instantiate an empty struct + v = reflect.New(v.Type().Elem()).Elem() + } else { + v = v.Elem() + } + } + if v.Kind() != reflect.Struct || i >= v.NumField() { + return reflect.Value{} + } + v = v.Field(i) + } + return v +} + func getFieldValue(refField reflect.Value) any { + if !refField.IsValid() { + return nil + } if refField.IsZero() { switch refField.Kind() { case reflect.Struct: diff --git a/ext/native_test.go b/ext/native_test.go index 59c0cd26d..a0bef47f9 100644 --- a/ext/native_test.go +++ b/ext/native_test.go @@ -874,7 +874,7 @@ func TestNativeTypeConvertToType(t *testing.T) { tc := tst t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { handler := fieldNameByTag(tc.tag) - nt, err := newNativeType(handler, reflect.TypeOf(&TestAllTypes{})) + nt, err := newNativeType(handler, reflect.TypeFor[*TestAllTypes]()) if err != nil { t.Fatalf("newNativeType() failed: %v", err) } @@ -889,7 +889,7 @@ func TestNativeTypeConvertToType(t *testing.T) { } func TestNativeTypeConvertToNative(t *testing.T) { - nt, err := newNativeType(fieldNameByTag("cel"), reflect.TypeOf(&TestAllTypes{})) + nt, err := newNativeType(fieldNameByTag("cel"), reflect.TypeFor[*TestAllTypes]()) if err != nil { t.Fatalf("newNativeType() failed: %v", err) } @@ -900,7 +900,7 @@ func TestNativeTypeConvertToNative(t *testing.T) { } func TestNativeTypeHasTrait(t *testing.T) { - nt, err := newNativeType(fieldNameByTag("cel"), reflect.TypeOf(&TestAllTypes{})) + nt, err := newNativeType(fieldNameByTag("cel"), reflect.TypeFor[*TestAllTypes]()) if err != nil { t.Fatalf("newNativeType() failed: %v", err) } @@ -910,7 +910,7 @@ func TestNativeTypeHasTrait(t *testing.T) { } func TestNativeTypeValue(t *testing.T) { - nt, err := newNativeType(fieldNameByTag("cel"), reflect.TypeOf(&TestAllTypes{})) + nt, err := newNativeType(fieldNameByTag("cel"), reflect.TypeFor[*TestAllTypes]()) if err != nil { t.Fatalf("newNativeType() failed: %v", err) } @@ -920,7 +920,7 @@ func TestNativeTypeValue(t *testing.T) { } func TestNativeStructWithMultipleSameFieldNames(t *testing.T) { - _, err := newNativeType(fieldNameByTag("cel"), reflect.TypeOf(TestStructWithMultipleSameNames{})) + _, err := newNativeType(fieldNameByTag("cel"), reflect.TypeFor[TestStructWithMultipleSameNames]()) if err == nil { t.Fatal("newNativeType() did not fail as expected") } @@ -965,6 +965,15 @@ func TestNativeStructEmbedded(t *testing.T) { }, out: true, }, + { + expr: `test.Name == "name"`, + in: map[string]any{ + "test": &TestEmbeddedTypes{ + Custom: Custom{Name: "name"}, + }, + }, + out: true, + }, } envOpts := []cel.EnvOption{ @@ -1015,6 +1024,79 @@ func TestNativeStructEmbedded(t *testing.T) { } } +func TestNativeStructEmbeddedPointer(t *testing.T) { + nativeTests := []struct { + expr string + in map[string]any + out any + }{ + { + expr: `!has(test.custom_name) && test.custom_name == ""`, + in: map[string]any{ + "test": &TestEmbeddedPointerTypes{ + TestNestedType: nil, + }, + }, + out: true, + }, + { + expr: `has(test.custom_name) && test.custom_name == "name"`, + in: map[string]any{ + "test": &TestEmbeddedPointerTypes{ + TestNestedType: &TestNestedType{NestedCustomName: "name"}, + }, + }, + out: true, + }, + { + expr: `ext.TestEmbeddedPointerTypes{custom_name: "name"}.custom_name == "name"`, + in: nil, + out: true, + }, + } + + envOpts := []cel.EnvOption{ + NativeTypes( + reflect.TypeFor[*TestEmbeddedPointerTypes](), + reflect.TypeFor[*TestNestedType](), + ParseStructTag("json"), + ), + cel.Variable("test", cel.ObjectType("ext.TestEmbeddedPointerTypes")), + } + + env, err := cel.NewEnv(envOpts...) + if err != nil { + t.Fatalf("cel.NewEnv(NativeTypes()) failed: %v", err) + } + + for i, tst := range nativeTests { + tc := tst + t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { + pAst, iss := env.Parse(tc.expr) + if iss.Err() != nil { + t.Fatalf("env.Parse(%v) failed: %v", tc.expr, iss.Err()) + } + cAst, iss := env.Check(pAst) + if iss.Err() != nil { + t.Fatalf("env.Check(%v) failed: %v", tc.expr, iss.Err()) + } + for _, ast := range []*cel.Ast{pAst, cAst} { + prg, err := env.Program(ast) + if err != nil { + t.Fatal(err) + } + out, _, err := prg.Eval(tc.in) + if err != nil { + t.Fatalf("prg.Eval() failed: %v", err) + } + if !reflect.DeepEqual(out.Value(), tc.out) { + t.Errorf("got %v, wanted %v for expr: %s", out.Value(), tc.out, tc.expr) + } + } + }) + } +} + func TestNativeStructHiddenField(t *testing.T) { envOpts := []cel.EnvOption{ NativeTypes( @@ -1168,7 +1250,7 @@ func TestTypeResolutionRace(t *testing.T) { } // testEnv initializes the test environment common to all tests. -func testNativeEnv(t *testing.T, opts ...any) *cel.Env { +func testNativeEnv(t testing.TB, opts ...any) *cel.Env { t.Helper() envOpts := []cel.EnvOption{ cel.Container("ext"), @@ -1217,8 +1299,8 @@ type Custom struct { } type TestStructWithMultipleSameNames struct { - Name string - custom_name string `cel:"Name"` + Name string + CustomName string `cel:"Name"` } type TestNestedType struct { @@ -1268,10 +1350,15 @@ type TestMapVal struct { } type TestEmbeddedTypes struct { + Custom TestNestedType `json:"embedded,omitempty"` Skipped string `json:"-"` } +type TestEmbeddedPointerTypes struct { + *TestNestedType `json:"embedded,omitempty"` +} + type TestRefValFieldType struct { OptionalName *types.Optional `cel:"optional_name"` IntVal types.Int @@ -1335,3 +1422,171 @@ func TestNativeToValueDelegatesUnregisteredStructs(t *testing.T) { t.Errorf("NativeToValue(registeredNativeStruct).Type() = %q, want a native object type", tn) } } + +func BenchmarkNativeTypesEval(b *testing.B) { + benchmarks := []struct { + name string + expr string + in any + envOpts []any + }{ + { + name: "FieldAccess", + expr: "t.Int32Val + t.Int64Val", + in: map[string]any{ + "t": &TestAllTypes{Int32Val: 10, Int64Val: 20}, + }, + }, + { + name: "NestedFieldAccess", + expr: "t.NestedVal.NestedCustomName == 'name'", + in: map[string]any{ + "t": &TestAllTypes{ + NestedVal: &TestNestedType{NestedCustomName: "name"}, + }, + }, + }, + { + name: "StructCreation", + expr: `ext.TestAllTypes{ + BoolVal: true, + Int32Val: 10, + Int64Val: 20, + StringVal: 'hello world', + }`, + }, + { + name: "FieldPresence", + expr: "has(t.BoolVal) && has(t.NestedVal)", + in: map[string]any{ + "t": &TestAllTypes{ + BoolVal: true, + NestedVal: &TestNestedType{}, + }, + }, + }, + { + name: "StructTagFieldAccess", + expr: "t.custom_name == 'name'", + envOpts: []any{ParseStructTags(true)}, + in: map[string]any{ + "t": &TestAllTypes{CustomName: "name"}, + }, + }, + { + name: "ListExists", + expr: "tests.exists(t, t.Int32Val > 15)", + in: map[string]any{ + "tests": []*TestAllTypes{ + {Int32Val: 10}, + {Int32Val: 20}, + }, + }, + }, + } + + for _, bm := range benchmarks { + b.Run(bm.name, func(b *testing.B) { + envOpts := append([]any{ + cel.Variable("t", cel.ObjectType("ext.TestAllTypes")), + }, bm.envOpts...) + env := testNativeEnv(b, envOpts...) + ast, iss := env.Compile(bm.expr) + if iss.Err() != nil { + b.Fatalf("env.Compile(%q) failed: %v", bm.expr, iss.Err()) + } + prg, err := env.Program(ast, cel.EvalOptions(cel.OptOptimize)) + if err != nil { + b.Fatalf("env.Program() failed: %v", err) + } + input := bm.in + if input == nil { + input = cel.NoVars() + } + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + prg.Eval(input) + } + }) + } +} + +func BenchmarkNativeToValue(b *testing.B) { + env := testNativeEnv(b) + adapter := env.CELTypeAdapter() + + nested := &TestNestedType{ + NestedListVal: []string{"a", "b", "c"}, + NestedMapVal: map[int64]bool{1: true}, + NestedCustomName: "test", + } + allTypes := &TestAllTypes{ + BoolVal: true, + Int32Val: 10, + Int64Val: 20, + StringVal: "hello world", + NestedVal: nested, + ListVal: []*TestNestedType{nested}, + } + allTypesSlice := []*TestAllTypes{allTypes, allTypes} + + benchmarks := []struct { + name string + val any + }{ + {name: "TestNestedType", val: nested}, + {name: "TestAllTypes", val: allTypes}, + {name: "SliceTestAllTypes", val: allTypesSlice}, + } + + for _, bm := range benchmarks { + b.Run(bm.name, func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + adapter.NativeToValue(bm.val) + } + }) + } +} + +func BenchmarkConvertToNative(b *testing.B) { + env := testNativeEnv(b) + adapter := env.CELTypeAdapter() + + allTypes := &TestAllTypes{ + BoolVal: true, + Int32Val: 10, + Int64Val: 20, + StringVal: "hello world", + } + celVal := adapter.NativeToValue(allTypes) + targetType := reflect.TypeOf(&TestAllTypes{}) + + allTypesSlice := []*TestAllTypes{allTypes, allTypes} + celSliceVal := adapter.NativeToValue(allTypesSlice) + sliceTargetType := reflect.TypeOf([]*TestAllTypes{}) + + b.Run("TestAllTypes", func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, err := celVal.ConvertToNative(targetType) + if err != nil { + b.Fatalf("ConvertToNative failed: %v", err) + } + } + }) + + b.Run("SliceTestAllTypes", func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, err := celSliceVal.ConvertToNative(sliceTargetType) + if err != nil { + b.Fatalf("ConvertToNative failed: %v", err) + } + } + }) +} From 7c1df6b3f0dc75c64bdf199227f4a5335c181df3 Mon Sep 17 00:00:00 2001 From: pranit more Date: Sat, 1 Aug 2026 00:52:35 +0530 Subject: [PATCH 07/37] reject out-of-range hours in timezone offset parsing (#1391) --- cel/cel_test.go | 21 +++++++++++++++++++++ common/stdlib/standard.go | 9 ++++++++- common/types/timestamp.go | 5 ++++- common/types/timestamp_test.go | 7 +++++++ 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/cel/cel_test.go b/cel/cel_test.go index 6e8813934..4f40783c6 100644 --- a/cel/cel_test.go +++ b/cel/cel_test.go @@ -2790,6 +2790,27 @@ func TestDefaultUTCTimeZoneError(t *testing.T) { } } +func TestTimeZoneOffsetOutOfRange(t *testing.T) { + env := testEnv(t, Variable("x", TimestampType)) + vars := map[string]any{"x": time.Unix(7506, 0).UTC()} + // Offsets whose hour or minute component falls outside a signed HH:MM field + // shift the resolved instant, so they must be rejected at evaluation time. + for _, tz := range []string{"+24:00", "-24:00", "+99:00", "-50:30", "+00:99", "+05:-30"} { + out, err := interpret(t, env, `x.getHours('`+tz+`') >= 0`, vars) + if err == nil { + t.Errorf("getHours(%q) got %v, wanted error", tz, out) + } + } + // A boundary offset within the field ranges keeps resolving. + out, err := interpret(t, env, `x.getHours('23:15')`, vars) + if err != nil { + t.Fatalf("getHours('23:15') failed: %v", err) + } + if out.Equal(types.Int(1)) != types.True { + t.Errorf("getHours('23:15') got %v, wanted 1", out) + } +} + func TestParserRecursionLimit(t *testing.T) { testCases := []struct { expr string diff --git a/common/stdlib/standard.go b/common/stdlib/standard.go index d2313bef1..3a151462e 100644 --- a/common/stdlib/standard.go +++ b/common/stdlib/standard.go @@ -16,6 +16,7 @@ package stdlib import ( + "fmt" "math" "strconv" "strings" @@ -1053,7 +1054,7 @@ func inTimeZone(ts, tz ref.Val) (time.Time, error) { } // If the input is not the name of a timezone (for example, 'US/Central'), it should be a numerical offset from UTC - // in the format ^(+|-)(0[0-9]|1[0-4]):[0-5][0-9]$. The numerical input is parsed in terms of hours and minutes. + // in the format ^(+|-)([01]\d|2[0-3]):[0-5][0-9]$. The numerical input is parsed in terms of hours and minutes. hr, err := strconv.Atoi(string(val[0:ind])) if err != nil { return time.Time{}, err @@ -1062,6 +1063,12 @@ func inTimeZone(ts, tz ref.Val) (time.Time, error) { if err != nil { return time.Time{}, err } + if hr < -23 || hr > 23 { + return time.Time{}, fmt.Errorf("timezone offset hours out of range [-23, 23]: %s", val) + } + if min < 0 || min > 59 { + return time.Time{}, fmt.Errorf("timezone offset minutes out of range [0, 59]: %s", val) + } var offset int if string(val[0]) == "-" { offset = hr*60 - min diff --git a/common/types/timestamp.go b/common/types/timestamp.go index 62a020d97..aee47f053 100644 --- a/common/types/timestamp.go +++ b/common/types/timestamp.go @@ -368,7 +368,7 @@ func timeZone(tz ref.Val, visitor timestampVisitor) timestampVisitor { } // If the input is not the name of a timezone (for example, 'US/Central'), it should be a numerical offset from UTC - // in the format ^(+|-)(0[0-9]|1[0-4]):[0-5][0-9]$. The numerical input is parsed in terms of hours and minutes. + // in the format ^(+|-)([01]\d|2[0-3]):[0-5][0-9]$. The numerical input is parsed in terms of hours and minutes. hr, err := strconv.Atoi(string(val[0:ind])) if err != nil { return WrapErr(err) @@ -377,6 +377,9 @@ func timeZone(tz ref.Val, visitor timestampVisitor) timestampVisitor { if err != nil { return WrapErr(err) } + if hr < -23 || hr > 23 { + return WrapErr(fmt.Errorf("timezone offset hours out of range [-23, 23]: %s", val)) + } if min < 0 || min > 59 { return WrapErr(fmt.Errorf("timezone offset minutes out of range [0, 59]: %s", val)) } diff --git a/common/types/timestamp_test.go b/common/types/timestamp_test.go index 7661c878a..c13b21fef 100644 --- a/common/types/timestamp_test.go +++ b/common/types/timestamp_test.go @@ -439,6 +439,13 @@ func TestTimestampGetHours(t *testing.T) { if !hrTz.Equal(Int(19)).(Bool) { t.Errorf("ts.getHours('America/Phoenix') got %v, wanted 19 hours", hrTz) } + // Out-of-range hour offsets are rejected rather than silently shifting the instant. + for _, tz := range []string{"+24:00", "-24:00", "+99:00", "-50:30"} { + if got := ts.Receive(overloads.TimeGetHours, overloads.TimestampToHoursWithTz, + []ref.Val{String(tz)}); !IsError(got) { + t.Errorf("ts.getHours(%q) got %v, wanted error", tz, got) + } + } } func TestTimestampGetMinutes(t *testing.T) { From ba6c27ebc0d232ae79907d778f3378a3ec741cf3 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Fri, 31 Jul 2026 13:59:35 -0700 Subject: [PATCH 08/37] Support self-describing, self-adapting struct types (#1395) * Support self-describing, self-adapting struct types * Safer duplicate type detection --- cel/cel_test.go | 4 +- cel/env.go | 2 +- cel/env_test.go | 4 +- cel/options.go | 6 +- checker/checker_test.go | 16 +- checker/cost_test.go | 4 +- common/ast/navigable_test.go | 8 +- common/env/env_test.go | 12 +- common/types/BUILD.bazel | 1 + common/types/provider.go | 375 ++++-- common/types/provider_test.go | 2193 ++++++++++++++++++++++++++----- common/types/struct.go | 39 + interpreter/interpreter_test.go | 8 +- 13 files changed, 2195 insertions(+), 477 deletions(-) create mode 100644 common/types/struct.go diff --git a/cel/cel_test.go b/cel/cel_test.go index 4f40783c6..af6843ca9 100644 --- a/cel/cel_test.go +++ b/cel/cel_test.go @@ -4153,9 +4153,9 @@ func TestJSONFieldNamesInvalidProvider(t *testing.T) { type wrapperRegistry struct { *types.Registry } - reg, err := types.NewProtoRegistry(types.JSONFieldNames(true)) + reg, err := types.NewRegistry(types.JSONFieldNames(true)) if err != nil { - t.Fatalf("types.NewProtoRegistry() failed: %v", err) + t.Fatalf("types.NewRegistry() failed: %v", err) } wrapped := wrapperRegistry{Registry: reg} _, err = NewEnv(CustomTypeProvider(wrapped), CustomTypeAdapter(reg), JSONFieldNames(true)) diff --git a/cel/env.go b/cel/env.go index 784790ba2..6631417c0 100644 --- a/cel/env.go +++ b/cel/env.go @@ -365,7 +365,7 @@ func NewEnv(opts ...EnvOption) (*Env, error) { // 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.NewProtoRegistry() + registry, err := types.NewRegistry() if err != nil { return nil, err } diff --git a/cel/env_test.go b/cel/env_test.go index eb4c20850..4957011ee 100644 --- a/cel/env_test.go +++ b/cel/env_test.go @@ -198,9 +198,9 @@ func TestEnvPartialVarsError(t *testing.T) { } func TestTypeProviderInterop(t *testing.T) { - reg, err := types.NewProtoRegistry(types.ProtoTypeDefs(&proto3pb.TestAllTypes{})) + reg, err := types.NewRegistry(&proto3pb.TestAllTypes{}) if err != nil { - t.Fatalf("types.NewProtoRegistry() failed: %v", err) + t.Fatalf("types.NewRegistry() failed: %v", err) } tests := []struct { name string diff --git a/cel/options.go b/cel/options.go index 710536fb7..957b1f699 100644 --- a/cel/options.go +++ b/cel/options.go @@ -920,7 +920,11 @@ func ContextProtoVars(ctx proto.Message, opts ...types.RegistryOption) (Activati } regOpts := []types.RegistryOption{types.ProtoTypeDefs(ctx)} regOpts = append(regOpts, opts...) - reg, err := types.NewProtoRegistry(regOpts...) + var ro []any + for _, opt := range regOpts { + ro = append(ro, opt) + } + reg, err := types.NewRegistry(ro...) if err != nil { return nil, err } diff --git a/checker/checker_test.go b/checker/checker_test.go index 3e86ec577..be84032f2 100644 --- a/checker/checker_test.go +++ b/checker/checker_test.go @@ -2542,12 +2542,12 @@ func TestCheck(t *testing.T) { t.Fatalf("Unexpected parse errors: %v", errors.ToDisplayString()) } - reg, err := types.NewProtoRegistry( + reg, err := types.NewRegistry( types.JSONFieldNames(tc.env.jsonFieldNames), types.ProtoTypeDefs(&proto2pb.TestAllTypes{}, &proto3pb.TestAllTypes{}), ) if err != nil { - t.Fatalf("types.NewProtoRegistry() failed: %v", err) + t.Fatalf("types.NewRegistry() failed: %v", err) } if tc.env.optionalSyntax { if err := reg.RegisterType(types.OptionalType); err != nil { @@ -2654,9 +2654,9 @@ func BenchmarkCheck(b *testing.B) { if len(errors.GetErrors()) > 0 { b.Fatalf("Unexpected parse errors: %v", errors.ToDisplayString()) } - reg, err := types.NewProtoRegistry(types.ProtoTypeDefs(&proto2pb.TestAllTypes{}, &proto3pb.TestAllTypes{})) + reg, err := types.NewRegistry(types.ProtoTypeDefs(&proto2pb.TestAllTypes{}, &proto3pb.TestAllTypes{})) if err != nil { - b.Fatalf("types.NewProtoRegistry() failed: %v", err) + b.Fatalf("types.NewRegistry() failed: %v", err) } if tc.env.optionalSyntax { if err := reg.RegisterType(types.OptionalType); err != nil { @@ -2723,9 +2723,9 @@ func BenchmarkCheck(b *testing.B) { } func TestAddDuplicateDeclarations(t *testing.T) { - reg, err := types.NewProtoRegistry(types.ProtoTypeDefs(&proto2pb.TestAllTypes{}, &proto3pb.TestAllTypes{})) + reg, err := types.NewRegistry(types.ProtoTypeDefs(&proto2pb.TestAllTypes{}, &proto3pb.TestAllTypes{})) if err != nil { - t.Fatalf("types.NewProtoRegistry() failed: %v", err) + t.Fatalf("types.NewRegistry() failed: %v", err) } env, err := NewEnv(containers.DefaultContainer, reg, CrossTypeNumericComparisons(true)) if err != nil { @@ -2742,9 +2742,9 @@ func TestAddDuplicateDeclarations(t *testing.T) { } func TestAddEquivalentDeclarations(t *testing.T) { - reg, err := types.NewProtoRegistry(types.ProtoTypeDefs(&proto2pb.TestAllTypes{}, &proto3pb.TestAllTypes{})) + reg, err := types.NewRegistry(types.ProtoTypeDefs(&proto2pb.TestAllTypes{}, &proto3pb.TestAllTypes{})) if err != nil { - t.Fatalf("types.NewProtoRegistry() failed: %v", err) + t.Fatalf("types.NewRegistry() failed: %v", err) } env, err := NewEnv(containers.DefaultContainer, reg, CrossTypeNumericComparisons(true)) if err != nil { diff --git a/checker/cost_test.go b/checker/cost_test.go index 5ee5a266f..ab83559a1 100644 --- a/checker/cost_test.go +++ b/checker/cost_test.go @@ -766,9 +766,9 @@ func TestCost(t *testing.T) { if len(errs.GetErrors()) != 0 { t.Fatalf("parser.Parse(%v) failed: %v", tc.expr, errs.ToDisplayString()) } - reg, err := types.NewProtoRegistry(types.ProtoTypeDefs(&proto3pb.TestAllTypes{})) + reg, err := types.NewRegistry(types.ProtoTypeDefs(&proto3pb.TestAllTypes{})) if err != nil { - t.Fatalf("types.NewProtoRegistry(...) failed: %v", err) + t.Fatalf("types.NewRegistry(...) failed: %v", err) } e, err := NewEnv(containers.DefaultContainer, reg) diff --git a/common/ast/navigable_test.go b/common/ast/navigable_test.go index 4a378546f..a9e55eaea 100644 --- a/common/ast/navigable_test.go +++ b/common/ast/navigable_test.go @@ -669,9 +669,13 @@ func mustTypeCheck(t testing.TB, expr string, opts ...any) *ast.AST { func newTestRegistry(t testing.TB, opts ...types.RegistryOption) *types.Registry { t.Helper() - reg, err := types.NewProtoRegistry(opts...) + var o []any + for _, opt := range opts { + o = append(o, opt) + } + reg, err := types.NewRegistry(o...) if err != nil { - t.Fatalf("types.NewProtoRegistry() failed: %v", err) + t.Fatalf("types.NewRegistry() failed: %v", err) } return reg } diff --git a/common/env/env_test.go b/common/env/env_test.go index 9ee4ce6f6..866807ef1 100644 --- a/common/env/env_test.go +++ b/common/env/env_test.go @@ -748,9 +748,9 @@ func TestVariableAsCELVariable(t *testing.T) { }, } - tp, err := types.NewProtoRegistry() + tp, err := types.NewRegistry() if err != nil { - t.Fatalf("types.NewProtoRegistry() failed: %v", err) + t.Fatalf("types.NewRegistry() failed: %v", err) } tp.RegisterType(types.NewOpaqueType("set", types.NewTypeParamType("T"))) for _, tst := range tests { @@ -936,9 +936,9 @@ func TestFunctionAsCELFunction(t *testing.T) { types.NewTypeParamType("T"))), }, } - tp, err := types.NewProtoRegistry() + tp, err := types.NewRegistry() if err != nil { - t.Fatalf("types.NewProtoRegistry() failed: %v", err) + t.Fatalf("types.NewRegistry() failed: %v", err) } tp.RegisterType(types.NewOpaqueType("set", types.NewTypeParamType("T"))) for _, tst := range tests { @@ -1047,9 +1047,9 @@ func TestTypeDescAsCELTypeErrors(t *testing.T) { want: errors.New("undefined type"), }, } - tp, err := types.NewProtoRegistry() + tp, err := types.NewRegistry() if err != nil { - t.Fatalf("types.NewProtoRegistry() failed: %v", err) + t.Fatalf("types.NewRegistry() failed: %v", err) } tp.RegisterType(types.NewOpaqueType("set", types.NewTypeParamType("T"))) for _, tst := range tests { diff --git a/common/types/BUILD.bazel b/common/types/BUILD.bazel index 7dda3ede9..dae581820 100644 --- a/common/types/BUILD.bazel +++ b/common/types/BUILD.bazel @@ -28,6 +28,7 @@ go_library( "provider.go", "regex.go", "string.go", + "struct.go", "timestamp.go", "types.go", "uint.go", diff --git a/common/types/provider.go b/common/types/provider.go index 1bb2c11ed..54111b2be 100644 --- a/common/types/provider.go +++ b/common/types/provider.go @@ -16,6 +16,7 @@ package types import ( "fmt" + "maps" "reflect" "time" @@ -54,11 +55,11 @@ type Provider interface { // Returns false if not found. FindStructType(structType string) (*Type, bool) - // FindStructFieldNames returns thet field names associated with the type, if the type + // FindStructFieldNames returns the field names associated with the type, if the type // is found. FindStructFieldNames(structType string) ([]string, bool) - // FieldStructFieldType returns the field type for a checked type value. Returns + // FindStructFieldType returns the field type for a checked type value. Returns // false if the field could not be found. FindStructFieldType(structType, fieldName string) (*FieldType, bool) @@ -88,15 +89,27 @@ type FieldType struct { // Registry provides type information for a set of registered types. type Registry struct { - revTypeMap map[string]*Type - pbdb *pb.Db + revTypeMap map[string]*Type + structTypes map[string]StructTypeDescriptor + reflectTypes map[reflect.Type]StructTypeDescriptor + pbdb *pb.Db + provider Provider + adapter Adapter } -// NewRegistry accepts a list of proto message instances and returns a type -// provider which can create new instances of the provided message or any -// message that proto depends upon in its FileDescriptor. -func NewRegistry(types ...proto.Message) (*Registry, error) { - return NewProtoRegistry(ProtoTypeDefs(types...)) +// NewRegistry accepts a list of proto message instances, ref.Type instances, or RegistryOption +// functions and returns a type provider. +func NewRegistry(types ...any) (*Registry, error) { + r, err := NewProtoRegistry() + if err != nil { + return nil, err + } + for _, t := range types { + if err := registerTypeItem(r, t); err != nil { + return nil, err + } + } + return r, nil } // RegistryOption configures the behavior of the registry. @@ -123,12 +136,20 @@ func ProtoTypeDefs(types ...proto.Message) RegistryOption { } } +// Types creates a RegistryOption which registers individual custom type references or descriptors with the registry. +func Types(types ...ref.Type) RegistryOption { + return func(r *Registry) (*Registry, error) { + err := r.RegisterType(types...) + if err != nil { + return nil, err + } + return r, nil + } +} + // NewProtoRegistry creates a proto-based registry with a set of configurable options. func NewProtoRegistry(opts ...RegistryOption) (*Registry, error) { - r := &Registry{ - revTypeMap: make(map[string]*Type), - pbdb: pb.NewDb(), - } + r := NewEmptyRegistry() err := r.RegisterType( BoolType, BytesType, @@ -164,20 +185,49 @@ func NewProtoRegistry(opts ...RegistryOption) (*Registry, error) { // NewEmptyRegistry returns a registry which is completely unconfigured. func NewEmptyRegistry() *Registry { return &Registry{ - revTypeMap: make(map[string]*Type), - pbdb: pb.NewDb(), + revTypeMap: make(map[string]*Type), + structTypes: make(map[string]StructTypeDescriptor), + reflectTypes: make(map[reflect.Type]StructTypeDescriptor), + pbdb: pb.NewDb(), } } -// Copy copies the current state of the registry into its own memory space. -func (p *Registry) Copy() *Registry { - copy := &Registry{ - revTypeMap: make(map[string]*Type), - pbdb: p.pbdb.Copy(), +// ComposeTypes accepts a provider, adapter, and a list of types (ref.Type, proto.Message, protoreflect.FileDescriptor, or RegistryOption) +// and either: +// - Determines the provider and adapter are the same instance and a *Registry and registers the listed types via RegisterType or +// one of the other registration methods as appropriate. +// - Determines the provider and adapter are not the same, or not a *Registry and creates a new composed *Registry which references +// the new type information first and then proxies to the underlying provider and adapter methods as appropriate. +func ComposeTypes(provider Provider, adapter Adapter, types ...any) (Provider, Adapter, error) { + reg, isReg := provider.(*Registry) + aReg, isAdapterReg := adapter.(*Registry) + if isReg && isAdapterReg && reg == aReg { + for _, t := range types { + if err := registerTypeItem(reg, t); err != nil { + return nil, nil, err + } + } + return reg, reg, nil } - for k, v := range p.revTypeMap { - copy.revTypeMap[k] = v + + composedReg, err := NewRegistry(types...) + if err != nil { + return nil, nil, err } + composedReg.provider = provider + composedReg.adapter = adapter + return composedReg, composedReg, nil +} + +// Copy copies the current state of the registry into its own memory space. +func (p *Registry) Copy() *Registry { + copy := NewEmptyRegistry() + copy.pbdb = p.pbdb.Copy() + copy.provider = p.provider + copy.adapter = p.adapter + maps.Copy(copy.revTypeMap, p.revTypeMap) + maps.Copy(copy.structTypes, p.structTypes) + maps.Copy(copy.reflectTypes, p.reflectTypes) return copy } @@ -207,6 +257,9 @@ func (p *Registry) WithJSONFieldNames(enabled bool) error { func (p *Registry) EnumValue(enumName string) ref.Val { enumVal, found := p.pbdb.DescribeEnum(enumName) if !found { + if p.provider != nil { + return p.provider.EnumValue(enumName) + } return NewErr("unknown enum name '%s'", enumName) } return Int(enumVal.Value()) @@ -217,70 +270,98 @@ func (p *Registry) EnumValue(enumName string) ref.Val { // // Deprecated: use FindStructFieldType func (p *Registry) FindFieldType(structType, fieldName string) (*ref.FieldType, bool) { - msgType, found := p.pbdb.DescribeType(structType) - if !found { - return nil, false + structType = sanitizeStructTypeName(structType) + if st, found := p.structTypes[structType]; found { + if ft, found := st.FindFieldType(fieldName); found { + exprType, err := TypeToExprType(ft.Type) + if err != nil { + return nil, false + } + return makeRefFieldType(exprType, ft.IsSet, ft.GetFrom, ft.IsJSONField), true + } } - field, found := msgType.FieldByName(fieldName) - if !found { - return nil, false + if msgType, found := p.pbdb.DescribeType(structType); found { + if field, found := msgType.FieldByName(fieldName); found { + return makeRefFieldType(field.CheckedType(), field.IsSet, field.GetFrom, p.pbdb.JSONFieldNames() && fieldName == field.JSONName()), true + } } - return &ref.FieldType{ - Type: field.CheckedType(), - IsSet: field.IsSet, - GetFrom: field.GetFrom, - IsJSONField: p.pbdb.JSONFieldNames() && fieldName == field.JSONName(), - }, true + if p.provider != nil { + if ft, ok := p.provider.FindStructFieldType(structType, fieldName); ok && ft != nil { + exprType, err := TypeToExprType(ft.Type) + if err != nil { + return nil, false + } + return makeRefFieldType(exprType, ft.IsSet, ft.GetFrom, ft.IsJSONField), true + } + } + return nil, false } // FindStructFieldNames returns the set of field names for the given struct type, // if the type exists in the registry. func (p *Registry) FindStructFieldNames(structType string) ([]string, bool) { - msgType, found := p.pbdb.DescribeType(structType) - if !found { - return []string{}, false + structType = sanitizeStructTypeName(structType) + if st, found := p.structTypes[structType]; found { + return st.FieldNames(), true } - fieldMap := msgType.FieldMap() - fields := make([]string, len(fieldMap)) - idx := 0 - for f := range fieldMap { - fields[idx] = f - idx++ + if msgType, found := p.pbdb.DescribeType(structType); found { + fieldMap := msgType.FieldMap() + fields := make([]string, len(fieldMap)) + idx := 0 + for f := range fieldMap { + fields[idx] = f + idx++ + } + return fields, true + } + if p.provider != nil { + return p.provider.FindStructFieldNames(structType) } - return fields, true + return []string{}, false } // FindStructFieldType returns the field type for a checked type value. Returns // false if the field could not be found. func (p *Registry) FindStructFieldType(structType, fieldName string) (*FieldType, bool) { - msgType, found := p.pbdb.DescribeType(structType) - if !found { - return nil, false + structType = sanitizeStructTypeName(structType) + if st, found := p.structTypes[structType]; found { + if ft, found := st.FindFieldType(fieldName); found { + return ft, true + } } - field, found := msgType.FieldByName(fieldName) - if !found { - return nil, false + if msgType, found := p.pbdb.DescribeType(structType); found { + if field, found := msgType.FieldByName(fieldName); found { + return &FieldType{ + Type: fieldDescToCELType(field), + IsSet: field.IsSet, + GetFrom: field.GetFrom, + IsJSONField: p.pbdb.JSONFieldNames() && fieldName == field.JSONName(), + }, true + } + } + if p.provider != nil { + return p.provider.FindStructFieldType(structType, fieldName) } - return &FieldType{ - Type: fieldDescToCELType(field), - IsSet: field.IsSet, - GetFrom: field.GetFrom, - IsJSONField: p.pbdb.JSONFieldNames() && fieldName == field.JSONName(), - }, true + return nil, false } // FindStructFieldDescription returns documentation for a field if available. // Returns false if the field could not be found. func (p *Registry) FindStructFieldDescription(structType, fieldName string) (string, bool) { - msgType, found := p.pbdb.DescribeType(structType) - if !found { - return "", false + structType = sanitizeStructTypeName(structType) + if msgType, found := p.pbdb.DescribeType(structType); found { + if field, found := msgType.FieldByName(fieldName); found { + return field.Documentation(), true + } } - field, found := msgType.FieldByName(fieldName) - if !found { - return "", false + if p.provider != nil { + if pd, ok := p.provider.(interface { + FindStructFieldDescription(string, string) (string, bool) + }); ok { + return pd.FindStructFieldDescription(structType, fieldName) + } } - return field.Documentation(), true + return "", false } // FindIdent takes a qualified identifier name and returns a ref.Val if one exists. @@ -291,6 +372,9 @@ func (p *Registry) FindIdent(identName string) (ref.Val, bool) { if enumVal, found := p.pbdb.DescribeEnum(identName); found { return Int(enumVal.Value()), true } + if p.provider != nil { + return p.provider.FindIdent(identName) + } return nil, false } @@ -298,17 +382,19 @@ func (p *Registry) FindIdent(identName string) (ref.Val, bool) { // // Deprecated: use FindStructType func (p *Registry) FindType(structType string) (*exprpb.Type, bool) { - if _, found := p.pbdb.DescribeType(structType); !found { - return nil, false + structType = sanitizeStructTypeName(structType) + if p.hasStructType(structType) { + return makeExprMessageType(structType), true } - if structType != "" && structType[0] == '.' { - structType = structType[1:] + if p.provider != nil { + if tp, ok := p.provider.(ref.TypeProvider); ok { + return tp.FindType(structType) + } + if _, ok := p.provider.FindStructType(structType); ok { + return makeExprMessageType(structType), true + } } - return &exprpb.Type{ - TypeKind: &exprpb.Type_Type{ - Type: &exprpb.Type{ - TypeKind: &exprpb.Type_MessageType{ - MessageType: structType}}}}, true + return nil, false } // FindStructType returns the Type give a qualified type name. @@ -319,13 +405,14 @@ func (p *Registry) FindType(structType string) (*exprpb.Type, bool) { // // Returns false if not found. func (p *Registry) FindStructType(structType string) (*Type, bool) { - if _, found := p.pbdb.DescribeType(structType); !found { - return nil, false + structType = sanitizeStructTypeName(structType) + if p.hasStructType(structType) { + return NewTypeTypeWithParam(NewObjectType(structType)), true } - if structType != "" && structType[0] == '.' { - structType = structType[1:] + if p.provider != nil { + return p.provider.FindStructType(structType) } - return NewTypeTypeWithParam(NewObjectType(structType)), true + return nil, false } // NewValue creates a new type value from a qualified name and map of field @@ -335,8 +422,15 @@ func (p *Registry) FindStructType(structType string) (*Type, bool) { // to convert the Val to the field's native type. If an error occurs during // conversion, the NewValue will be a types.Err. func (p *Registry) NewValue(structType string, fields map[string]ref.Val) ref.Val { + structType = sanitizeStructTypeName(structType) + if st, found := p.structTypes[structType]; found { + return st.NewValue(p, fields) + } td, found := p.pbdb.DescribeType(structType) if !found { + if p.provider != nil { + return p.provider.NewValue(structType, fields) + } return NewErr("unknown type '%s'", structType) } msg := td.New() @@ -382,24 +476,57 @@ func (p *Registry) RegisterMessage(message proto.Message) error { // to CEL, even when they're not based on protobuf types. func (p *Registry) RegisterType(types ...ref.Type) error { for _, t := range types { - celType := maybeForeignType(t) existing, found := p.revTypeMap[t.TypeName()] - if !found { - p.revTypeMap[t.TypeName()] = celType + celType := maybeForeignType(t) + if found { + if !existing.IsEquivalentType(celType) { + return fmt.Errorf("type registration conflict. found: %v, input: %v", existing, celType) + } + if existing.traitMask != celType.traitMask { + return fmt.Errorf( + "type registered with conflicting traits: %v with traits %v, input: %v", + existing.TypeName(), existing.traitMask, celType.traitMask) + } continue } - if !existing.IsEquivalentType(celType) { - return fmt.Errorf("type registration conflict. found: %v, input: %v", existing, celType) - } - if existing.traitMask != celType.traitMask { - return fmt.Errorf( - "type registered with conflicting traits: %v with traits %v, input: %v", - existing.TypeName(), existing.traitMask, celType.traitMask) + + typeName := t.TypeName() + p.revTypeMap[typeName] = celType + if st, ok := t.(StructTypeDescriptor); ok { + // Conflicts are gated above so if we see a struct here, it's safe to register. + p.structTypes[typeName] = st + if rt := st.ReflectType(); rt != nil { + p.reflectTypes[rt] = st + if rt.Kind() == reflect.Ptr { + p.reflectTypes[rt.Elem()] = st + } else { + p.reflectTypes[reflect.PointerTo(rt)] = st + } + } } } return nil } +func (p *Registry) findStructDescriptorByReflectType(rt reflect.Type) (StructTypeDescriptor, bool) { + if rt == nil { + return nil, false + } + if st, found := p.reflectTypes[rt]; found { + return st, true + } + if rt.Kind() == reflect.Ptr { + if st, found := p.reflectTypes[rt.Elem()]; found { + return st, true + } + } else { + if st, found := p.reflectTypes[reflect.PointerTo(rt)]; found { + return st, true + } + } + return nil, false +} + // NativeToValue converts various "native" types to ref.Val with this specific implementation // providing support for custom proto-based types. // @@ -413,6 +540,9 @@ func (p *Registry) NativeToValue(value any) ref.Val { typeName := string(v.ProtoReflect().Descriptor().FullName()) td, found := p.pbdb.DescribeType(typeName) if !found { + if p.adapter != nil { + return p.adapter.NativeToValue(value) + } return NewErr("unknown type: '%s'", typeName) } unwrapped, isUnwrapped, err := td.MaybeUnwrap(v) @@ -435,6 +565,19 @@ func (p *Registry) NativeToValue(value any) ref.Val { return p.NativeToValue(v.Interface()) case protoreflect.Value: return p.NativeToValue(v.Interface()) + default: + if len(p.reflectTypes) > 0 && value != nil { + if st, found := p.findStructDescriptorByReflectType(reflect.TypeOf(value)); found { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr && val.IsNil() { + return NullValue + } + return st.Adapt(p, value) + } + } + } + if p.adapter != nil { + return p.adapter.NativeToValue(value) } return UnsupportedRefValConversionErr(value) } @@ -454,6 +597,58 @@ func (p *Registry) registerAllTypes(fd *pb.FileDescription) error { return nil } +func (p *Registry) hasStructType(structType string) bool { + if _, found := p.structTypes[structType]; found { + return true + } + _, found := p.pbdb.DescribeType(structType) + return found +} + +func sanitizeStructTypeName(structType string) string { + if len(structType) > 0 && structType[0] == '.' { + return structType[1:] + } + return structType +} + +func registerTypeItem(r *Registry, t any) error { + switch v := t.(type) { + case proto.Message: + return r.RegisterMessage(v) + case protoreflect.FileDescriptor: + return r.RegisterDescriptor(v) + case ref.Type: + return r.RegisterType(v) + case RegistryOption: + _, err := v(r) + return err + default: + return fmt.Errorf("unsupported type: %T", t) + } +} + +func makeExprMessageType(structType string) *exprpb.Type { + return &exprpb.Type{ + TypeKind: &exprpb.Type_Type{ + Type: &exprpb.Type{ + TypeKind: &exprpb.Type_MessageType{ + MessageType: structType, + }, + }, + }, + } +} + +func makeRefFieldType(t *exprpb.Type, isSet ref.FieldTester, getFrom ref.FieldGetter, isJSONField bool) *ref.FieldType { + return &ref.FieldType{ + Type: t, + IsSet: isSet, + GetFrom: getFrom, + IsJSONField: isJSONField, + } +} + func fieldDescToCELType(field *pb.FieldDescription) *Type { if field.IsMap() { return NewMapType( @@ -522,6 +717,8 @@ func nativeToValue(a Adapter, value any) (ref.Val, bool) { if v != nil { return *v, true } + case ref.Val: + return v, true case bool: return Bool(v), true case int: @@ -620,8 +817,6 @@ func nativeToValue(a Adapter, value any) (ref.Val, bool) { return NewJSONList(a, v), true case *structpb.Struct: return NewJSONStruct(a, v), true - case ref.Val: - return v, true case protoreflect.EnumNumber: return Int(v), true case proto.Message: @@ -649,7 +844,7 @@ func nativeToValue(a Adapter, value any) (ref.Val, bool) { refValue := reflect.ValueOf(v) if refValue.Kind() == reflect.Ptr { if refValue.IsNil() { - return UnsupportedRefValConversionErr(v), true + return nil, false } refValue = refValue.Elem() } diff --git a/common/types/provider_test.go b/common/types/provider_test.go index 552ac1836..60b61c46d 100644 --- a/common/types/provider_test.go +++ b/common/types/provider_test.go @@ -37,48 +37,72 @@ import ( ) func TestRegistryCopy(t *testing.T) { - reg := NewEmptyRegistry() - reg2 := reg.Copy() - if !reflect.DeepEqual(reg, reg2) { - t.Fatal("type registry copy did not produce equivalent values.") - } - reg = newTestRegistry(t) - reg2 = reg.Copy() - if !reflect.DeepEqual(reg, reg2) { - t.Fatal("type registry copy did not produce equivalent values.") + tests := []struct { + name string + reg *Registry + }{ + { + name: "empty", + reg: NewEmptyRegistry(), + }, + { + name: "populated", + reg: newTestRegistry(t), + }, } -} -func TestRegistryRegisterType(t *testing.T) { - reg := newTestRegistry(t) - err := reg.RegisterType( - NewTypeValue("http.Request", traits.ReceiverType), - NewObjectType("http.Request", traits.ReceiverType), - ) - if err == nil { - t.Error("RegisterType() for differing type definitions with the same name did not fail") + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + reg2 := tc.reg.Copy() + if !reflect.DeepEqual(tc.reg, reg2) { + t.Fatal("type registry copy did not produce equivalent values.") + } + }) } } -func TestRegistryRegisterTypeNoConflict(t *testing.T) { - reg := newTestRegistry(t) - err := reg.RegisterType( - NewOpaqueType("http.Request", NewTypeParamType("T")), - NewOpaqueType("http.Request", NewTypeParamType("V")), - ) - if err != nil { - t.Errorf("RegisterType() failed for equivalent types: %v", err) +func TestRegistryRegisterType(t *testing.T) { + tests := []struct { + name string + types []ref.Type + wantErr bool + }{ + { + name: "differing type definitions same name", + types: []ref.Type{ + NewTypeValue("http.Request", traits.ReceiverType), + NewObjectType("http.Request", traits.ReceiverType), + }, + wantErr: true, + }, + { + name: "equivalent opaque types no conflict", + types: []ref.Type{ + NewOpaqueType("http.Request", NewTypeParamType("T")), + NewOpaqueType("http.Request", NewTypeParamType("V")), + }, + wantErr: false, + }, + { + name: "differing opaque types conflict", + types: []ref.Type{ + NewOpaqueType("http.Request", NewTypeParamType("T"), NewTypeParamType("V")), + NewOpaqueType("http.Request", NewTypeParamType("V")), + }, + wantErr: true, + }, } -} -func TestRegistryRegisterTypeConflict(t *testing.T) { - reg := newTestRegistry(t) - err := reg.RegisterType( - NewOpaqueType("http.Request", NewTypeParamType("T"), NewTypeParamType("V")), - NewOpaqueType("http.Request", NewTypeParamType("V")), - ) - if err == nil { - t.Error("RegisterType() for differing type definitions with the same name did not fail") + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + reg := newTestRegistry(t) + err := reg.RegisterType(tc.types...) + if (err != nil) != tc.wantErr { + t.Errorf("RegisterType() error = %v, wantErr %v", err, tc.wantErr) + } + }) } } @@ -88,17 +112,24 @@ func TestRegistryEnumValue(t *testing.T) { if err != nil { t.Fatalf("RegisterDescriptor() failed: %v", err) } - enumVal := reg.EnumValue("google.expr.proto3.test.GlobalEnum.GOO") - if Int(proto3pb.GlobalEnum_GOO.Number()) != enumVal.(Int) { - t.Errorf("enum values were not equal between registry and proto: %v", enumVal) - } - enumVal2, found := reg.FindIdent("google.expr.proto3.test.GlobalEnum.GOO") - if !found { - t.Fatal("Ident not found google.expr.proto3.test.GlobalEnum.GOO") - } - if enumVal.(Int) != enumVal2.(Int) { - t.Errorf("got enum value %v, wanted %v", enumVal2, enumVal) - } + + t.Run("EnumValue", func(t *testing.T) { + enumVal := reg.EnumValue("google.expr.proto3.test.GlobalEnum.GOO") + if IsError(enumVal) || Int(proto3pb.GlobalEnum_GOO.Number()) != enumVal.(Int) { + t.Errorf("enum values were not equal between registry and proto: %v", enumVal) + } + }) + + t.Run("FindIdent", func(t *testing.T) { + enumVal := reg.EnumValue("google.expr.proto3.test.GlobalEnum.GOO") + enumVal2, found := reg.FindIdent("google.expr.proto3.test.GlobalEnum.GOO") + if !found { + t.Fatal("Ident not found google.expr.proto3.test.GlobalEnum.GOO") + } + if enumVal.(Int) != enumVal2.(Int) { + t.Errorf("got enum value %v, wanted %v", enumVal2, enumVal) + } + }) } func TestRegistryFindStructType(t *testing.T) { @@ -107,60 +138,80 @@ func TestRegistryFindStructType(t *testing.T) { if err != nil { t.Fatalf("RegisterDescriptor() failed: %v", err) } - msgTypeName := ".google.expr.proto3.test.TestAllTypes" - exprType, found := reg.FindType(msgTypeName) - if !found { - t.Fatalf("FindType() did not find: %q", msgTypeName) - } - celType, found := reg.FindStructType(msgTypeName) - if !found { - t.Fatalf("FindStructType() did not find %q", msgTypeName) - } - exprConvType, err := ExprTypeToType(exprType) - if err != nil { - t.Fatalf("ExprTypeToType(%v) failed: %v", exprType, err) - } - if !exprConvType.IsExactType(celType) { - t.Errorf("Got %v type, wanted %v", exprConvType, celType) - } - _, found = reg.FindType(msgTypeName + "Undefined") - if found { - t.Fatalf("FindType() found: %q", msgTypeName+"Undefined") + + tests := []struct { + typeName string + wantFound bool + }{ + { + typeName: ".google.expr.proto3.test.TestAllTypes", + wantFound: true, + }, + { + typeName: ".google.expr.proto3.test.TestAllTypesUndefined", + wantFound: false, + }, } - _, found = reg.FindStructType(msgTypeName + "Undefined") - if found { - t.Fatalf("FindStructType() found: %q", msgTypeName+"Undefined") + + for _, tt := range tests { + tc := tt + t.Run(tc.typeName, func(t *testing.T) { + exprType, foundType := reg.FindType(tc.typeName) + celType, foundStruct := reg.FindStructType(tc.typeName) + + if foundType != tc.wantFound { + t.Errorf("FindType(%q) found = %v, want %v", tc.typeName, foundType, tc.wantFound) + } + if foundStruct != tc.wantFound { + t.Errorf("FindStructType(%q) found = %v, want %v", tc.typeName, foundStruct, tc.wantFound) + } + + if tc.wantFound { + exprConvType, err := ExprTypeToType(exprType) + if err != nil { + t.Fatalf("ExprTypeToType(%v) failed: %v", exprType, err) + } + if !exprConvType.IsExactType(celType) { + t.Errorf("Got %v type, wanted %v", exprConvType, celType) + } + } + }) } } func TestRegistryFindStructFieldNames(t *testing.T) { tests := []struct { + name string typeName string fields []string jsonFieldNames bool }{ { + name: "Reference", typeName: "google.api.expr.v1alpha1.Reference", fields: []string{"name", "overload_id", "value"}, }, { + name: "Decl", typeName: "google.api.expr.v1alpha1.Decl", fields: []string{"name", "ident", "function"}, }, { + name: "invalid type", typeName: "invalid.TypeName", fields: []string{}, }, { + name: "Reference JSON field names", typeName: "google.api.expr.v1alpha1.Reference", fields: []string{"name", "overloadId", "value"}, jsonFieldNames: true, }, } - for _, tst := range tests { - tc := tst - t.Run(fmt.Sprintf("%s", tc.typeName), func(t *testing.T) { + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { reg := newTestRegistry(t, ProtoTypeDefs(&exprpb.Decl{}, &exprpb.Reference{}), JSONFieldNames(tc.jsonFieldNames)) @@ -192,11 +243,6 @@ func TestRegistryFindStructFieldType(t *testing.T) { field: "single_nested_message", found: true, }, - { - typeName: msgTypeName, - field: "single_nested_message", - found: true, - }, { typeName: msgTypeName, field: "standalone_enum", @@ -505,116 +551,145 @@ func TestRegistryNewValueErrors(t *testing.T) { func TestRegistryGetters(t *testing.T) { reg := newTestRegistry(t, ProtoTypeDefs(&exprpb.ParsedExpr{})) - if sourceInfo := reg.NewValue( + sourceInfo := reg.NewValue( "google.api.expr.v1alpha1.SourceInfo", map[string]ref.Val{ "location": String("TestTypeRegistryGetFieldValue"), "line_offsets": NewDynamicList(reg, []int64{0, 2}), "positions": NewDynamicMap(reg, map[int64]int64{1: 2, 2: 4}), - }); IsError(sourceInfo) { - t.Error(sourceInfo) - } else { - si := sourceInfo.(traits.Indexer) - if loc := si.Get(String("location")); IsError(loc) { - t.Error(loc) - } else if loc.(String) != "TestTypeRegistryGetFieldValue" { + }) + if IsError(sourceInfo) { + t.Fatalf("NewValue(SourceInfo) failed: %v", sourceInfo) + } + + si := sourceInfo.(traits.Indexer) + + t.Run("location", func(t *testing.T) { + loc := si.Get(String("location")) + if IsError(loc) { + t.Fatal(loc) + } + if loc.(String) != "TestTypeRegistryGetFieldValue" { t.Errorf("Expected %s, got %s", "TestTypeRegistryGetFieldValue", loc) } - if pos := si.Get(String("positions")); IsError(pos) { - t.Error(pos) - } else if pos.Equal(NewDynamicMap(reg, map[int64]int32{1: 2, 2: 4})) != True { + }) + + t.Run("positions", func(t *testing.T) { + pos := si.Get(String("positions")) + if IsError(pos) { + t.Fatal(pos) + } + if pos.Equal(NewDynamicMap(reg, map[int64]int32{1: 2, 2: 4})) != True { t.Errorf("Expected map[int64]int32, got %v", pos) - } else if posKeyVal := pos.(traits.Indexer).Get(Int(1)); IsError(posKeyVal) { - t.Error(posKeyVal) - } else if posKeyVal.(Int) != 2 { + } + posKeyVal := pos.(traits.Indexer).Get(Int(1)) + if IsError(posKeyVal) { + t.Fatal(posKeyVal) + } + if posKeyVal.(Int) != 2 { t.Error("Expected value to be int64, not int32") } - if offsets := si.Get(String("line_offsets")); IsError(offsets) { - t.Error(offsets) - } else if offset1 := offsets.(traits.Lister).Get(Int(1)); IsError(offset1) { - t.Error(offset1) - } else if offset1.(Int) != 2 { + }) + + t.Run("line_offsets", func(t *testing.T) { + offsets := si.Get(String("line_offsets")) + if IsError(offsets) { + t.Fatal(offsets) + } + offset1 := offsets.(traits.Lister).Get(Int(1)) + if IsError(offset1) { + t.Fatal(offset1) + } + if offset1.(Int) != 2 { t.Errorf("Expected index 1 to be value 2, was %v", offset1) } - } + }) } func TestConvertToNative(t *testing.T) { reg := newTestRegistry(t, ProtoTypeDefs(&exprpb.ParsedExpr{})) - - // Core type conversion tests. - expectValueToNative(t, True, true) - expectValueToNative(t, True, True) - expectValueToNative(t, NewDynamicList(reg, []Bool{True, False}), []any{true, false}) - expectValueToNative(t, NewDynamicList(reg, []Bool{True, False}), []ref.Val{True, False}) - expectValueToNative(t, Int(-1), int32(-1)) - expectValueToNative(t, Int(2), int64(2)) - expectValueToNative(t, Int(-1), Int(-1)) - expectValueToNative(t, NewDynamicList(reg, []Int{4}), []any{int64(4)}) - expectValueToNative(t, NewDynamicList(reg, []Int{5}), []ref.Val{Int(5)}) - expectValueToNative(t, Uint(3), uint32(3)) - expectValueToNative(t, Uint(4), uint64(4)) - expectValueToNative(t, Uint(5), Uint(5)) - expectValueToNative(t, NewDynamicList(reg, []Uint{4}), []any{uint64(4)}) - expectValueToNative(t, NewDynamicList(reg, []Uint{5}), []ref.Val{Uint(5)}) - expectValueToNative(t, Double(5.5), float32(5.5)) - expectValueToNative(t, Double(-5.5), float64(-5.5)) - expectValueToNative(t, NewDynamicList(reg, []Double{-5.5}), []any{-5.5}) - expectValueToNative(t, NewDynamicList(reg, []Double{-5.5}), []ref.Val{Double(-5.5)}) - expectValueToNative(t, Double(-5.5), Double(-5.5)) - expectValueToNative(t, String("hello"), "hello") - expectValueToNative(t, String("hello"), String("hello")) - expectValueToNative(t, NullValue, structpb.NullValue_NULL_VALUE) - expectValueToNative(t, NullValue, NullValue) - expectValueToNative(t, NewDynamicList(reg, []Null{NullValue}), []any{structpb.NullValue_NULL_VALUE}) - expectValueToNative(t, NewDynamicList(reg, []Null{NullValue}), []ref.Val{NullValue}) - expectValueToNative(t, Bytes("world"), []byte("world")) - expectValueToNative(t, Bytes("world"), Bytes("world")) - expectValueToNative(t, NewDynamicList(reg, []Bytes{Bytes("hello")}), []any{[]byte("hello")}) - expectValueToNative(t, NewDynamicList(reg, []Bytes{Bytes("hello")}), []ref.Val{Bytes("hello")}) - expectValueToNative(t, NewDynamicList(reg, []int64{1, 2, 3}), []int32{1, 2, 3}) - expectValueToNative(t, Duration{Duration: time.Duration(500)}, time.Duration(500)) - expectValueToNative(t, Duration{Duration: time.Duration(500)}, Duration{Duration: time.Duration(500)}) - expectValueToNative(t, Timestamp{Time: time.Unix(12345, 0)}, time.Unix(12345, 0)) - expectValueToNative(t, Timestamp{Time: time.Unix(12345, 0)}, Timestamp{Time: time.Unix(12345, 0)}) - expectValueToNative(t, NewDynamicMap(reg, - map[int64]int64{1: 1, 2: 1, 3: 1}), - map[int32]int32{1: 1, 2: 1, 3: 1}) - - // Null conversion tests. - expectValueToNative(t, Null(structpb.NullValue_NULL_VALUE), structpb.NullValue_NULL_VALUE) - - // Proto conversion tests. parsedExpr := &exprpb.ParsedExpr{} - expectValueToNative(t, reg.NativeToValue(parsedExpr), parsedExpr) - - // Custom scalars - expectValueToNative(t, Int(1), testInt(1)) - expectValueToNative(t, Int(1), testInt8(1)) - expectValueToNative(t, Int(1), testInt16(1)) - expectValueToNative(t, Int(1), testInt32(1)) - expectValueToNative(t, Int(1), testInt64(1)) - expectValueToNative(t, Uint(1), testUint(1)) - expectValueToNative(t, Uint(1), testUint8(1)) - expectValueToNative(t, Uint(1), testUint16(1)) - expectValueToNative(t, Uint(1), testUint32(1)) - expectValueToNative(t, Uint(1), testUint64(1)) - expectValueToNative(t, Double(4.5), testFloat32(4.5)) - expectValueToNative(t, Double(-5.1), testFloat64(-5.1)) - expectValueToNative(t, String("foo"), testString("foo")) + + tests := []struct { + name string + in ref.Val + want any + }{ + // Core type conversion tests. + {name: "bool to bool", in: True, want: true}, + {name: "bool to ref.Val Bool", in: True, want: True}, + {name: "bool list to []any", in: NewDynamicList(reg, []Bool{True, False}), want: []any{true, false}}, + {name: "bool list to []ref.Val", in: NewDynamicList(reg, []Bool{True, False}), want: []ref.Val{True, False}}, + {name: "int to int32", in: Int(-1), want: int32(-1)}, + {name: "int to int64", in: Int(2), want: int64(2)}, + {name: "int to ref.Val Int", in: Int(-1), want: Int(-1)}, + {name: "int list to []any", in: NewDynamicList(reg, []Int{4}), want: []any{int64(4)}}, + {name: "int list to []ref.Val", in: NewDynamicList(reg, []Int{5}), want: []ref.Val{Int(5)}}, + {name: "uint to uint32", in: Uint(3), want: uint32(3)}, + {name: "uint to uint64", in: Uint(4), want: uint64(4)}, + {name: "uint to ref.Val Uint", in: Uint(5), want: Uint(5)}, + {name: "uint list to []any", in: NewDynamicList(reg, []Uint{4}), want: []any{uint64(4)}}, + {name: "uint list to []ref.Val", in: NewDynamicList(reg, []Uint{5}), want: []ref.Val{Uint(5)}}, + {name: "double to float32", in: Double(5.5), want: float32(5.5)}, + {name: "double to float64", in: Double(-5.5), want: float64(-5.5)}, + {name: "double list to []any", in: NewDynamicList(reg, []Double{-5.5}), want: []any{-5.5}}, + {name: "double list to []ref.Val", in: NewDynamicList(reg, []Double{-5.5}), want: []ref.Val{Double(-5.5)}}, + {name: "double to ref.Val Double", in: Double(-5.5), want: Double(-5.5)}, + {name: "string to string", in: String("hello"), want: "hello"}, + {name: "string to ref.Val String", in: String("hello"), want: String("hello")}, + {name: "null to structpb.NullValue", in: NullValue, want: structpb.NullValue_NULL_VALUE}, + {name: "null to ref.Val NullValue", in: NullValue, want: NullValue}, + {name: "null list to []any", in: NewDynamicList(reg, []Null{NullValue}), want: []any{structpb.NullValue_NULL_VALUE}}, + {name: "null list to []ref.Val", in: NewDynamicList(reg, []Null{NullValue}), want: []ref.Val{NullValue}}, + {name: "bytes to []byte", in: Bytes("world"), want: []byte("world")}, + {name: "bytes to ref.Val Bytes", in: Bytes("world"), want: Bytes("world")}, + {name: "bytes list to []any", in: NewDynamicList(reg, []Bytes{Bytes("hello")}), want: []any{[]byte("hello")}}, + {name: "bytes list to []ref.Val", in: NewDynamicList(reg, []Bytes{Bytes("hello")}), want: []ref.Val{Bytes("hello")}}, + {name: "int64 list to []int32", in: NewDynamicList(reg, []int64{1, 2, 3}), want: []int32{1, 2, 3}}, + {name: "duration to time.Duration", in: Duration{Duration: time.Duration(500)}, want: time.Duration(500)}, + {name: "duration to ref.Val Duration", in: Duration{Duration: time.Duration(500)}, want: Duration{Duration: time.Duration(500)}}, + {name: "timestamp to time.Time", in: Timestamp{Time: time.Unix(12345, 0)}, want: time.Unix(12345, 0)}, + {name: "timestamp to ref.Val Timestamp", in: Timestamp{Time: time.Unix(12345, 0)}, want: Timestamp{Time: time.Unix(12345, 0)}}, + {name: "map[int64]int64 to map[int32]int32", in: NewDynamicMap(reg, map[int64]int64{1: 1, 2: 1, 3: 1}), want: map[int32]int32{1: 1, 2: 1, 3: 1}}, + + // Null conversion tests. + {name: "Null(NULL_VALUE) to structpb.NullValue", in: Null(structpb.NullValue_NULL_VALUE), want: structpb.NullValue_NULL_VALUE}, + + // Proto conversion tests. + {name: "parsedExpr proto to proto message", in: reg.NativeToValue(parsedExpr), want: parsedExpr}, + + // Custom scalars + {name: "int to testInt", in: Int(1), want: testInt(1)}, + {name: "int to testInt8", in: Int(1), want: testInt8(1)}, + {name: "int to testInt16", in: Int(1), want: testInt16(1)}, + {name: "int to testInt32", in: Int(1), want: testInt32(1)}, + {name: "int to testInt64", in: Int(1), want: testInt64(1)}, + {name: "uint to testUint", in: Uint(1), want: testUint(1)}, + {name: "uint to testUint8", in: Uint(1), want: testUint8(1)}, + {name: "uint to testUint16", in: Uint(1), want: testUint16(1)}, + {name: "uint to testUint32", in: Uint(1), want: testUint32(1)}, + {name: "uint to testUint64", in: Uint(1), want: testUint64(1)}, + {name: "double to testFloat32", in: Double(4.5), want: testFloat32(4.5)}, + {name: "double to testFloat64", in: Double(-5.1), want: testFloat64(-5.1)}, + {name: "string to testString", in: String("foo"), want: testString("foo")}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + expectValueToNative(t, tc.in, tc.want) + }) + } } func TestNativeToValue_Any(t *testing.T) { reg := newTestRegistry(t, ProtoTypeDefs(&exprpb.ParsedExpr{})) - // NullValue - anyValue, err := NullValue.ConvertToNative(anyValueType) + + nullAny, err := NullValue.ConvertToNative(anyValueType) if err != nil { - t.Error(err) + t.Fatalf("NullValue.ConvertToNative() failed: %v", err) } - expectNativeToValue(t, anyValue, NullValue) - // Json Struct - anyValue, err = anypb.New( + jsonStructAny, err := anypb.New( structpb.NewStructValue( &structpb.Struct{ Fields: map[string]*structpb.Value{ @@ -625,18 +700,10 @@ func TestNativeToValue_Any(t *testing.T) { ), ) if err != nil { - t.Error(err) + t.Fatalf("anypb.New(NewStructValue) failed: %v", err) } - expected := NewJSONStruct(reg, &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "a": structpb.NewStringValue("world"), - "b": structpb.NewStringValue("five!"), - }, - }) - expectNativeToValue(t, anyValue, expected) - //Json List - anyValue, err = anypb.New(structpb.NewListValue( + jsonListAny, err := anypb.New(structpb.NewListValue( &structpb.ListValue{ Values: []*structpb.Value{ structpb.NewStringValue("world"), @@ -645,183 +712,266 @@ func TestNativeToValue_Any(t *testing.T) { }, )) if err != nil { - t.Error(err) + t.Fatalf("anypb.New(NewListValue) failed: %v", err) } - expectedList := NewJSONList(reg, &structpb.ListValue{ - Values: []*structpb.Value{ - structpb.NewStringValue("world"), - structpb.NewStringValue("five!"), - }}) - expectNativeToValue(t, anyValue, expectedList) - // Object pbMessage := exprpb.ParsedExpr{ SourceInfo: &exprpb.SourceInfo{ - LineOffsets: []int32{1, 2, 3}}} - anyValue, err = anypb.New(&pbMessage) + LineOffsets: []int32{1, 2, 3}, + }, + } + pbMessageAny, err := anypb.New(&pbMessage) if err != nil { - t.Error(err) + t.Fatalf("anypb.New(ParsedExpr) failed: %v", err) + } + + tests := []struct { + name string + in any + want ref.Val + }{ + { + name: "NullValue", + in: nullAny, + want: NullValue, + }, + { + name: "JSON Struct", + in: jsonStructAny, + want: NewJSONStruct(reg, &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "a": structpb.NewStringValue("world"), + "b": structpb.NewStringValue("five!"), + }, + }), + }, + { + name: "JSON List", + in: jsonListAny, + want: NewJSONList(reg, &structpb.ListValue{ + Values: []*structpb.Value{ + structpb.NewStringValue("world"), + structpb.NewStringValue("five!"), + }, + }), + }, + { + name: "Proto Message", + in: pbMessageAny, + want: reg.NativeToValue(&pbMessage), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + expectNativeToValue(t, tc.in, tc.want) + }) } - expectNativeToValue(t, anyValue, reg.NativeToValue(&pbMessage)) } func TestNativeToValue_Json(t *testing.T) { reg := newTestRegistry(t, ProtoTypeDefs(&exprpb.ParsedExpr{})) - // Json primitive conversion test. - expectNativeToValue(t, structpb.NewBoolValue(false), False) - expectNativeToValue(t, structpb.NewNumberValue(1.1), Double(1.1)) - expectNativeToValue(t, structpb.NewNullValue(), Null(structpb.NullValue_NULL_VALUE)) - expectNativeToValue(t, structpb.NewStringValue("hello"), String("hello")) - - // Json list conversion. - expectNativeToValue(t, - structpb.NewListValue( - &structpb.ListValue{ + parsedExpr := &exprpb.ParsedExpr{} + + tests := []struct { + name string + in any + want ref.Val + }{ + // Json primitive conversion test. + {name: "bool value", in: structpb.NewBoolValue(false), want: False}, + {name: "number value", in: structpb.NewNumberValue(1.1), want: Double(1.1)}, + {name: "null value", in: structpb.NewNullValue(), want: Null(structpb.NullValue_NULL_VALUE)}, + {name: "string value", in: structpb.NewStringValue("hello"), want: String("hello")}, + + // Json list conversion. + { + name: "list value", + in: structpb.NewListValue( + &structpb.ListValue{ + Values: []*structpb.Value{ + structpb.NewStringValue("world"), + structpb.NewStringValue("five!"), + }, + }, + ), + want: NewJSONList(reg, &structpb.ListValue{ Values: []*structpb.Value{ structpb.NewStringValue("world"), structpb.NewStringValue("five!"), }, - }, - ), - NewJSONList(reg, &structpb.ListValue{ - Values: []*structpb.Value{ - structpb.NewStringValue("world"), - structpb.NewStringValue("five!"), - }, - })) + }), + }, - // Json struct conversion. - expectNativeToValue(t, - structpb.NewStructValue( - &structpb.Struct{ + // Json struct conversion. + { + name: "struct value", + in: structpb.NewStructValue( + &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "a": structpb.NewStringValue("world"), + "b": structpb.NewStringValue("five!"), + }, + }, + ), + want: NewJSONStruct(reg, &structpb.Struct{ Fields: map[string]*structpb.Value{ "a": structpb.NewStringValue("world"), "b": structpb.NewStringValue("five!"), }, - }, - ), - NewJSONStruct(reg, &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "a": structpb.NewStringValue("world"), - "b": structpb.NewStringValue("five!"), - }, - })) + }), + }, - // Proto conversion test. - parsedExpr := &exprpb.ParsedExpr{} - expectNativeToValue(t, parsedExpr, reg.NativeToValue(parsedExpr)) + // Proto conversion test. + { + name: "proto message", + in: parsedExpr, + want: reg.NativeToValue(parsedExpr), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + expectNativeToValue(t, tc.in, tc.want) + }) + } } func TestNativeToValue_Wrappers(t *testing.T) { - // Wrapper conversion test. - expectNativeToValue(t, wrapperspb.Bool(true), True) - expectNativeToValue(t, &wrapperspb.BoolValue{}, False) - expectNativeToValue(t, (*wrapperspb.BoolValue)(nil), NullValue) - expectNativeToValue(t, &wrapperspb.BytesValue{}, Bytes{}) - expectNativeToValue(t, wrapperspb.Bytes([]byte("hi")), Bytes("hi")) - expectNativeToValue(t, (*wrapperspb.BytesValue)(nil), NullValue) - expectNativeToValue(t, &wrapperspb.DoubleValue{}, Double(0.0)) - expectNativeToValue(t, wrapperspb.Double(6.4), Double(6.4)) - expectNativeToValue(t, (*wrapperspb.DoubleValue)(nil), NullValue) - expectNativeToValue(t, &wrapperspb.FloatValue{}, Double(0.0)) - expectNativeToValue(t, wrapperspb.Float(3.0), Double(3.0)) - expectNativeToValue(t, (*wrapperspb.FloatValue)(nil), NullValue) - expectNativeToValue(t, &wrapperspb.Int32Value{}, IntZero) - expectNativeToValue(t, wrapperspb.Int32(-32), Int(-32)) - expectNativeToValue(t, (*wrapperspb.Int32Value)(nil), NullValue) - expectNativeToValue(t, &wrapperspb.Int64Value{}, IntZero) - expectNativeToValue(t, wrapperspb.Int64(-64), Int(-64)) - expectNativeToValue(t, (*wrapperspb.Int64Value)(nil), NullValue) - expectNativeToValue(t, &wrapperspb.StringValue{}, String("")) - expectNativeToValue(t, wrapperspb.String("hello"), String("hello")) - expectNativeToValue(t, (*wrapperspb.StringValue)(nil), NullValue) - expectNativeToValue(t, &wrapperspb.UInt32Value{}, Uint(0)) - expectNativeToValue(t, wrapperspb.UInt32(32), Uint(32)) - expectNativeToValue(t, (*wrapperspb.UInt32Value)(nil), NullValue) - expectNativeToValue(t, &wrapperspb.UInt64Value{}, Uint(0)) - expectNativeToValue(t, wrapperspb.UInt64(64), Uint(64)) - expectNativeToValue(t, (*wrapperspb.UInt64Value)(nil), NullValue) + tests := []struct { + name string + in any + want ref.Val + }{ + {name: "bool wrapper true", in: wrapperspb.Bool(true), want: True}, + {name: "bool wrapper zero value", in: &wrapperspb.BoolValue{}, want: False}, + {name: "bool wrapper nil", in: (*wrapperspb.BoolValue)(nil), want: NullValue}, + {name: "bytes wrapper zero value", in: &wrapperspb.BytesValue{}, want: Bytes{}}, + {name: "bytes wrapper value", in: wrapperspb.Bytes([]byte("hi")), want: Bytes("hi")}, + {name: "bytes wrapper nil", in: (*wrapperspb.BytesValue)(nil), want: NullValue}, + {name: "double wrapper zero value", in: &wrapperspb.DoubleValue{}, want: Double(0.0)}, + {name: "double wrapper value", in: wrapperspb.Double(6.4), want: Double(6.4)}, + {name: "double wrapper nil", in: (*wrapperspb.DoubleValue)(nil), want: NullValue}, + {name: "float wrapper zero value", in: &wrapperspb.FloatValue{}, want: Double(0.0)}, + {name: "float wrapper value", in: wrapperspb.Float(3.0), want: Double(3.0)}, + {name: "float wrapper nil", in: (*wrapperspb.FloatValue)(nil), want: NullValue}, + {name: "int32 wrapper zero value", in: &wrapperspb.Int32Value{}, want: IntZero}, + {name: "int32 wrapper value", in: wrapperspb.Int32(-32), want: Int(-32)}, + {name: "int32 wrapper nil", in: (*wrapperspb.Int32Value)(nil), want: NullValue}, + {name: "int64 wrapper zero value", in: &wrapperspb.Int64Value{}, want: IntZero}, + {name: "int64 wrapper value", in: wrapperspb.Int64(-64), want: Int(-64)}, + {name: "int64 wrapper nil", in: (*wrapperspb.Int64Value)(nil), want: NullValue}, + {name: "string wrapper zero value", in: &wrapperspb.StringValue{}, want: String("")}, + {name: "string wrapper value", in: wrapperspb.String("hello"), want: String("hello")}, + {name: "string wrapper nil", in: (*wrapperspb.StringValue)(nil), want: NullValue}, + {name: "uint32 wrapper zero value", in: &wrapperspb.UInt32Value{}, want: Uint(0)}, + {name: "uint32 wrapper value", in: wrapperspb.UInt32(32), want: Uint(32)}, + {name: "uint32 wrapper nil", in: (*wrapperspb.UInt32Value)(nil), want: NullValue}, + {name: "uint64 wrapper zero value", in: &wrapperspb.UInt64Value{}, want: Uint(0)}, + {name: "uint64 wrapper value", in: wrapperspb.UInt64(64), want: Uint(64)}, + {name: "uint64 wrapper nil", in: (*wrapperspb.UInt64Value)(nil), want: NullValue}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + expectNativeToValue(t, tc.in, tc.want) + }) + } } func TestNativeToValue_Primitive(t *testing.T) { reg := newTestRegistry(t) - // Core type conversions. - expectNativeToValue(t, true, True) - expectNativeToValue(t, int(-10), Int(-10)) - expectNativeToValue(t, int32(-1), Int(-1)) - expectNativeToValue(t, int64(2), Int(2)) - expectNativeToValue(t, uint(6), Uint(6)) - expectNativeToValue(t, uint32(3), Uint(3)) - expectNativeToValue(t, uint64(4), Uint(4)) - expectNativeToValue(t, float32(5.5), Double(5.5)) - expectNativeToValue(t, float64(-5.5), Double(-5.5)) - expectNativeToValue(t, "hello", String("hello")) - expectNativeToValue(t, []byte("world"), Bytes("world")) - expectNativeToValue(t, [4]byte{1, 2, 3, 4}, Bytes([]byte{1, 2, 3, 4})) - expectNativeToValue(t, &[4]byte{1, 2, 3, 4}, Bytes([]byte{1, 2, 3, 4})) - expectNativeToValue(t, time.Duration(500), Duration{Duration: time.Duration(500)}) - expectNativeToValue(t, time.Unix(12345, 0), Timestamp{Time: time.Unix(12345, 0)}) - expectNativeToValue(t, dpb.New(time.Duration(500)), Duration{Duration: time.Duration(500)}) - expectNativeToValue(t, tpb.New(time.Unix(12345, 0)), Timestamp{Time: time.Unix(12345, 0)}) - expectNativeToValue(t, []int32{1, 2, 3}, NewDynamicList(reg, []int32{1, 2, 3})) - expectNativeToValue(t, map[int32]int32{1: 1, 2: 1, 3: 1}, - NewDynamicMap(reg, map[int32]int32{1: 1, 2: 1, 3: 1})) - - // Pointers to core types. pBool := true - expectNativeToValue(t, &pBool, True) pDub32 := float32(2.5) pDub64 := float64(-1000.2) - expectNativeToValue(t, &pDub32, Double(2.5)) - expectNativeToValue(t, &pDub64, Double(-1000.2)) pInt := int(1) pInt32 := int32(2) pInt64 := int64(-1000) - expectNativeToValue(t, &pInt, Int(1)) - expectNativeToValue(t, &pInt32, Int(2)) - expectNativeToValue(t, &pInt64, Int(-1000)) pStr := "hello" - expectNativeToValue(t, &pStr, String("hello")) pUint := uint(1) pUint32 := uint32(2) pUint64 := uint64(1000) - expectNativeToValue(t, &pUint, Uint(1)) - expectNativeToValue(t, &pUint32, Uint(2)) - expectNativeToValue(t, &pUint64, Uint(1000)) - // Pointers to ref.Val extensions of core types. rBool := True - expectNativeToValue(t, &rBool, True) rDub := Double(32.1) - expectNativeToValue(t, &rDub, rDub) rInt := Int(-12) - expectNativeToValue(t, &rInt, rInt) rStr := String("hello") - expectNativeToValue(t, &rStr, rStr) rUint := Uint(12405) - expectNativeToValue(t, &rUint, rUint) rBytes := Bytes([]byte("hello")) - expectNativeToValue(t, &rBytes, rBytes) - - // Extensions to core types. - expectNativeToValue(t, testInt(1), Int(1)) - expectNativeToValue(t, testInt8(1), Int(1)) - expectNativeToValue(t, testInt16(1), Int(1)) - expectNativeToValue(t, testInt32(1), Int(1)) - expectNativeToValue(t, testInt64(-100), Int(-100)) - expectNativeToValue(t, testUint(1), Uint(1)) - expectNativeToValue(t, testUint8(1), Uint(1)) - expectNativeToValue(t, testUint16(1), Uint(1)) - expectNativeToValue(t, testUint32(2), Uint(2)) - expectNativeToValue(t, testUint64(3), Uint(3)) - expectNativeToValue(t, testFloat32(4.5), Double(4.5)) - expectNativeToValue(t, testFloat64(-5.1), Double(-5.1)) - expectNativeToValue(t, testString("foo"), String("foo")) - - // Null conversion test. - expectNativeToValue(t, nil, NullValue) - expectNativeToValue(t, structpb.NullValue_NULL_VALUE, Null(structpb.NullValue_NULL_VALUE)) + + tests := []struct { + name string + in any + want ref.Val + }{ + // Core type conversions. + {name: "bool", in: true, want: True}, + {name: "int", in: int(-10), want: Int(-10)}, + {name: "int32", in: int32(-1), want: Int(-1)}, + {name: "int64", in: int64(2), want: Int(2)}, + {name: "uint", in: uint(6), want: Uint(6)}, + {name: "uint32", in: uint32(3), want: Uint(3)}, + {name: "uint64", in: uint64(4), want: Uint(4)}, + {name: "float32", in: float32(5.5), want: Double(5.5)}, + {name: "float64", in: float64(-5.5), want: Double(-5.5)}, + {name: "string", in: "hello", want: String("hello")}, + {name: "bytes slice", in: []byte("world"), want: Bytes("world")}, + {name: "bytes array", in: [4]byte{1, 2, 3, 4}, want: Bytes([]byte{1, 2, 3, 4})}, + {name: "bytes array pointer", in: &[4]byte{1, 2, 3, 4}, want: Bytes([]byte{1, 2, 3, 4})}, + {name: "time duration", in: time.Duration(500), want: Duration{Duration: time.Duration(500)}}, + {name: "time timestamp", in: time.Unix(12345, 0), want: Timestamp{Time: time.Unix(12345, 0)}}, + {name: "proto duration", in: dpb.New(time.Duration(500)), want: Duration{Duration: time.Duration(500)}}, + {name: "proto timestamp", in: tpb.New(time.Unix(12345, 0)), want: Timestamp{Time: time.Unix(12345, 0)}}, + {name: "slice of int32", in: []int32{1, 2, 3}, want: NewDynamicList(reg, []int32{1, 2, 3})}, + {name: "map of int32", in: map[int32]int32{1: 1, 2: 1, 3: 1}, want: NewDynamicMap(reg, map[int32]int32{1: 1, 2: 1, 3: 1})}, + + // Pointers to core types. + {name: "pointer to bool", in: &pBool, want: True}, + {name: "pointer to float32", in: &pDub32, want: Double(2.5)}, + {name: "pointer to float64", in: &pDub64, want: Double(-1000.2)}, + {name: "pointer to int", in: &pInt, want: Int(1)}, + {name: "pointer to int32", in: &pInt32, want: Int(2)}, + {name: "pointer to int64", in: &pInt64, want: Int(-1000)}, + {name: "pointer to string", in: &pStr, want: String("hello")}, + {name: "pointer to uint", in: &pUint, want: Uint(1)}, + {name: "pointer to uint32", in: &pUint32, want: Uint(2)}, + {name: "pointer to uint64", in: &pUint64, want: Uint(1000)}, + + // Pointers to ref.Val extensions of core types. + {name: "pointer to ref.Val bool", in: &rBool, want: True}, + {name: "pointer to ref.Val double", in: &rDub, want: rDub}, + {name: "pointer to ref.Val int", in: &rInt, want: rInt}, + {name: "pointer to ref.Val string", in: &rStr, want: rStr}, + {name: "pointer to ref.Val uint", in: &rUint, want: rUint}, + {name: "pointer to ref.Val bytes", in: &rBytes, want: rBytes}, + + // Extensions to core types. + {name: "custom testInt", in: testInt(1), want: Int(1)}, + {name: "custom testInt8", in: testInt8(1), want: Int(1)}, + {name: "custom testInt16", in: testInt16(1), want: Int(1)}, + {name: "custom testInt32", in: testInt32(1), want: Int(1)}, + {name: "custom testInt64", in: testInt64(-100), want: Int(-100)}, + {name: "custom testUint", in: testUint(1), want: Uint(1)}, + {name: "custom testUint8", in: testUint8(1), want: Uint(1)}, + {name: "custom testUint16", in: testUint16(1), want: Uint(1)}, + {name: "custom testUint32", in: testUint32(2), want: Uint(2)}, + {name: "custom testUint64", in: testUint64(3), want: Uint(3)}, + {name: "custom testFloat32", in: testFloat32(4.5), want: Double(4.5)}, + {name: "custom testFloat64", in: testFloat64(-5.1), want: Double(-5.1)}, + {name: "custom testString", in: testString("foo"), want: String("foo")}, + + // Null conversion test. + {name: "nil", in: nil, want: NullValue}, + {name: "proto null value", in: structpb.NullValue_NULL_VALUE, want: Null(structpb.NullValue_NULL_VALUE)}, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + expectNativeToValue(t, tc.in, tc.want) + }) + } } func TestUnsupportedConversion(t *testing.T) { @@ -868,25 +1018,45 @@ func expectNativeToValue(t *testing.T, in any, out ref.Val) { } func BenchmarkNativeToValue(b *testing.B) { - reg, err := NewRegistry() + reg, err := NewRegistry(ProtoTypeDefs(&proto3pb.TestAllTypes{})) if err != nil { b.Fatalf("NewRegistry() failed: %v", err) } - inputs := []any{ - true, - false, - float32(-1.2), - float64(-2.4), - 1, - int32(2), - int64(3), - "", - "hello", - String("hello world"), - } - for _, in := range inputs { - input := in - b.Run(fmt.Sprintf("%T/%v", in, in), func(b *testing.B) { + + dummyDesc := &testDummyStructDescriptor{ + Type: NewObjectType("dummy.Struct"), + reflectType: reflect.TypeOf(dummyNativeStruct{}), + fieldType: &FieldType{Type: StringType}, + } + if err := reg.RegisterType(dummyDesc); err != nil { + b.Fatalf("RegisterType() failed: %v", err) + } + + protoMsg := &proto3pb.TestAllTypes{SingleInt32: 42} + nativeStructVal := dummyNativeStruct{} + nativeStructPtr := &dummyNativeStruct{} + + inputs := []struct { + name string + val any + }{ + {name: "bool/true", val: true}, + {name: "int/1", val: 1}, + {name: "int64/3", val: int64(3)}, + {name: "string/hello", val: "hello"}, + {name: "ref.Val/String", val: String("hello world")}, + {name: "ref.Val/Int", val: Int(42)}, + {name: "ref.Val/Bool", val: Bool(true)}, + {name: "proto/TestAllTypes", val: protoMsg}, + {name: "nativeStruct/value", val: nativeStructVal}, + {name: "nativeStruct/pointer", val: nativeStructPtr}, + } + + for _, tc := range inputs { + input := tc.val + b.Run(tc.name, func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() for i := 0; i < b.N; i++ { reg.NativeToValue(input) } @@ -894,56 +1064,1357 @@ func BenchmarkNativeToValue(b *testing.B) { } } -func BenchmarkTypeProviderNewValue(b *testing.B) { - reg, err := NewRegistry(&exprpb.ParsedExpr{}) - if err != nil { - b.Fatalf("NewRegistry() failed: %v", err) +func TestRegistryStructTypeDescriptor_FindStructType(t *testing.T) { + reg := newTestStructTypeRegistry(t) + tests := []struct { + name string + wantType string + }{ + {name: "custom.MyStruct", wantType: "type"}, + {name: ".custom.MyStruct", wantType: "type"}, } - for i := 0; i < b.N; i++ { - reg.NewValue( - "google.api.expr.v1.SourceInfo", - map[string]ref.Val{ - "Location": String("BenchmarkTypeProvider_NewValue"), - "LineOffsets": NewDynamicList(reg, []int64{0, 2}), - "Positions": NewDynamicMap(reg, map[int64]int64{1: 2, 2: 4}), - }) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + st, found := reg.FindStructType(tc.name) + if !found || st == nil { + t.Fatalf("FindStructType(%q) not found", tc.name) + } + if st.TypeName() != tc.wantType { + t.Errorf("FindStructType(%q).TypeName() = %s, want %s", tc.name, st.TypeName(), tc.wantType) + } + if st.Parameters()[0].TypeName() != "custom.MyStruct" { + t.Errorf("FindStructType(%q) TypeName() = %s, want 'custom.MyStruct'", tc.name, st.Parameters()[0].TypeName()) + } + }) } } -func BenchmarkTypeProviderCopy(b *testing.B) { - reg, err := NewRegistry() - if err != nil { - b.Fatalf("NewRegistry() failed: %v", err) +func TestRegistryStructTypeDescriptor_FindStructFieldNames(t *testing.T) { + reg := newTestStructTypeRegistry(t) + names, found := reg.FindStructFieldNames("custom.MyStruct") + if !found { + t.Fatalf("FindStructFieldNames('custom.MyStruct') not found") } - for i := 0; i < b.N; i++ { - reg.Copy() + want := []string{"Bar", "Foo"} + if !reflect.DeepEqual(names, want) { + t.Errorf("FindStructFieldNames() = %v, want %v", names, want) } } -// Helper types useful for testing extensions of primitive types. -type nonConvertible struct { - Field string +func TestRegistryStructTypeDescriptor_FindStructFieldType(t *testing.T) { + reg := newTestStructTypeRegistry(t) + tests := []struct { + fieldName string + wantType *Type + }{ + {fieldName: "Foo", wantType: StringType}, + {fieldName: "Bar", wantType: IntType}, + } + for _, tc := range tests { + t.Run(tc.fieldName, func(t *testing.T) { + ft, found := reg.FindStructFieldType("custom.MyStruct", tc.fieldName) + if !found || ft == nil { + t.Fatalf("FindStructFieldType(%q) not found", tc.fieldName) + } + if ft.Type != tc.wantType { + t.Errorf("FindStructFieldType(%q).Type = %v, want %v", tc.fieldName, ft.Type, tc.wantType) + } + }) + } } -type testBool bool -type testInt int -type testInt8 int8 -type testInt16 int16 -type testInt32 int32 -type testInt64 int64 -type testUint uint -type testUint8 uint8 -type testUint16 uint16 -type testUint32 uint32 -type testUint64 uint64 -type testFloat32 float32 -type testFloat64 float64 -type testString string -func newTestRegistry(t *testing.T, opts ...RegistryOption) *Registry { - t.Helper() - reg, err := NewProtoRegistry(opts...) - if err != nil { - t.Fatalf("NewProtoRegistry() failed: %v", err) +func TestRegistryStructTypeDescriptor_FindIdent(t *testing.T) { + reg := newTestStructTypeRegistry(t) + ident, found := reg.FindIdent("custom.MyStruct") + if !found || ident == nil { + t.Fatalf("FindIdent('custom.MyStruct') not found") + } +} + +func TestRegistryStructTypeDescriptor_NewValue(t *testing.T) { + reg := newTestStructTypeRegistry(t) + val := reg.NewValue("custom.MyStruct", map[string]ref.Val{"Foo": String("hello"), "Bar": Int(42)}) + if IsError(val) { + t.Fatalf("NewValue() failed: %v", val) + } + + t.Run("Foo", func(t *testing.T) { + fooVal := val.(traits.Indexer).Get(String("Foo")) + if fooVal.Equal(String("hello")) != True { + t.Errorf("Get('Foo') = %v, want 'hello'", fooVal) + } + }) + + t.Run("Bar", func(t *testing.T) { + barVal := val.(traits.Indexer).Get(String("Bar")) + if barVal.Equal(Int(42)) != True { + t.Errorf("Get('Bar') = %v, want 42", barVal) + } + }) +} + +func TestRegistryStructTypeDescriptor_NativeToValue(t *testing.T) { + reg := newTestStructTypeRegistry(t) + tests := []struct { + name string + in any + check func(t *testing.T, val ref.Val) + }{ + { + name: "struct instance", + in: dummyNativeStruct{Foo: "hello", Bar: 42}, + check: func(t *testing.T, val ref.Val) { + fooVal := val.(traits.Indexer).Get(String("Foo")) + if fooVal.Equal(String("hello")) != True { + t.Errorf("Get('Foo') = %v, want 'hello'", fooVal) + } + }, + }, + { + name: "pointer to struct instance", + in: &dummyNativeStruct{Foo: "world", Bar: 99}, + check: func(t *testing.T, val ref.Val) { + barVal := val.(traits.Indexer).Get(String("Bar")) + if barVal.Equal(Int(99)) != True { + t.Errorf("Get('Bar') = %v, want 99", barVal) + } + }, + }, + { + name: "slice of struct instances", + in: []dummyNativeStruct{{Foo: "e1"}, {Foo: "e2"}}, + check: func(t *testing.T, val ref.Val) { + lister := val.(traits.Lister) + if lister.Size().Equal(Int(2)) != True { + t.Errorf("Size() = %v, want 2", lister.Size()) + } + e1 := lister.Get(Int(0)).(traits.Indexer).Get(String("Foo")) + if e1.Equal(String("e1")) != True { + t.Errorf("element 0 Foo = %v, want 'e1'", e1) + } + }, + }, + { + name: "map of struct instances", + in: map[string]dummyNativeStruct{"k1": {Foo: "v1"}}, + check: func(t *testing.T, val ref.Val) { + mapper := val.(traits.Mapper) + k1Val := mapper.Get(String("k1")).(traits.Indexer).Get(String("Foo")) + if k1Val.Equal(String("v1")) != True { + t.Errorf("map k1 Foo = %v, want 'v1'", k1Val) + } + }, + }, + { + name: "typed nil pointer to struct instance", + in: (*dummyNativeStruct)(nil), + check: func(t *testing.T, val ref.Val) { + if val != NullValue { + t.Errorf("NativeToValue((*dummyNativeStruct)(nil)) = %v, want NullValue", val) + } + }, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + val := reg.NativeToValue(tc.in) + if IsError(val) { + t.Fatalf("NativeToValue(%s) error: %v", tc.name, val) + } + tc.check(t, val) + }) + } +} + +func BenchmarkTypeProviderNewValue(b *testing.B) { + reg, err := NewRegistry(&exprpb.ParsedExpr{}) + if err != nil { + b.Fatalf("NewRegistry() failed: %v", err) + } + for i := 0; i < b.N; i++ { + reg.NewValue( + "google.api.expr.v1.SourceInfo", + map[string]ref.Val{ + "Location": String("BenchmarkTypeProvider_NewValue"), + "LineOffsets": NewDynamicList(reg, []int64{0, 2}), + "Positions": NewDynamicMap(reg, map[int64]int64{1: 2, 2: 4}), + }) + } +} + +func BenchmarkTypeProviderCopy(b *testing.B) { + reg, err := NewRegistry() + if err != nil { + b.Fatalf("NewRegistry() failed: %v", err) + } + for i := 0; i < b.N; i++ { + reg.Copy() + } +} + +// Helper types useful for testing extensions of primitive types. +type nonConvertible struct { + Field string +} +type testBool bool +type testInt int +type testInt8 int8 +type testInt16 int16 +type testInt32 int32 +type testInt64 int64 +type testUint uint +type testUint8 uint8 +type testUint16 uint16 +type testUint32 uint32 +type testUint64 uint64 +type testFloat32 float32 +type testFloat64 float64 +type testString string + +func newTestRegistry(t *testing.T, opts ...RegistryOption) *Registry { + t.Helper() + var o []any + for _, opt := range opts { + o = append(o, opt) + } + reg, err := NewRegistry(o...) + if err != nil { + t.Fatalf("NewRegistry() failed: %v", err) + } + return reg +} + +type dummyNativeStruct struct { + Foo string + Bar int64 +} + +type testStructType struct { + typeName string + reflectType reflect.Type + fields map[string]*FieldType + celType *Type +} + +func (d *testStructType) HasTrait(trait int) bool { + return d.objectType().HasTrait(trait) +} + +func (d *testStructType) TypeName() string { + return d.typeName +} + +func (d *testStructType) objectType() *Type { + if d.celType == nil { + d.celType = NewObjectType(d.typeName) + } + return d.celType +} + +func (d *testStructType) ReflectType() reflect.Type { + return d.reflectType +} + +func (d *testStructType) FieldNames() []string { + names := make([]string, 0, len(d.fields)) + for name := range d.fields { + names = append(names, name) + } + sort.Strings(names) + return names +} + +func (d *testStructType) FindFieldType(fieldName string) (*FieldType, bool) { + ft, found := d.fields[fieldName] + return ft, found +} + +func (d *testStructType) NewValue(adapter Adapter, fields map[string]ref.Val) ref.Val { + if d.reflectType == nil { + return &testStructVal{ + adapter: adapter, + st: d, + value: fields, + } + } + refPtr := reflect.New(d.reflectType) + refVal := refPtr.Elem() + for fieldName, val := range fields { + refField := refVal.FieldByName(fieldName) + if !refField.IsValid() || !refField.CanSet() { + return NewErr("no such field: %s", fieldName) + } + nativeVal, err := val.ConvertToNative(refField.Type()) + if err != nil { + return NewErrFromString(err.Error()) + } + refField.Set(reflect.ValueOf(nativeVal)) + } + var inst any + if d.reflectType.Kind() == reflect.Pointer { + inst = refPtr.Interface() + } else { + inst = refVal.Interface() + } + return d.Adapt(adapter, inst) +} + +func (d *testStructType) Adapt(adapter Adapter, value any) ref.Val { + return &testStructVal{ + adapter: adapter, + st: d, + value: value, + } +} + +type testStructVal struct { + adapter Adapter + st *testStructType + value any +} + +func (o *testStructVal) ConvertToNative(typeDesc reflect.Type) (any, error) { + if reflect.TypeOf(o.value).AssignableTo(typeDesc) { + return o.value, nil + } + if reflect.TypeOf(o).AssignableTo(typeDesc) { + return o, nil + } + return nil, fmt.Errorf("type conversion error for type to '%v'", typeDesc) +} + +func (o *testStructVal) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case TypeType: + return NewTypeTypeWithParam(o.Type().(*Type)) + default: + if o.Type().TypeName() == typeVal.TypeName() { + return o + } + } + return NewErr("type conversion error from '%s' to '%s'", o.Type(), typeVal) +} + +func (o *testStructVal) Equal(other ref.Val) ref.Val { + return Bool(reflect.DeepEqual(o.value, other.Value())) +} + +func (o *testStructVal) HasTrait(trait int) bool { + return (traits.FieldTesterType|traits.IndexerType)&trait == trait +} + +func (o *testStructVal) Get(index ref.Val) ref.Val { + fieldName, ok := index.(String) + if !ok { + return MaybeNoSuchOverloadErr(index) + } + ft, found := o.st.FindFieldType(string(fieldName)) + if !found { + return NewErr("no such field: %s", index) + } + if ft.GetFrom == nil { + return NewErr("field '%s' is not readable", index) + } + fv, err := ft.GetFrom(o.value) + if err != nil { + return NewErrFromString(err.Error()) + } + return o.adapter.NativeToValue(fv) +} + +func (o *testStructVal) IsSet(field ref.Val) ref.Val { + fieldName, ok := field.(String) + if !ok { + return MaybeNoSuchOverloadErr(field) + } + ft, found := o.st.FindFieldType(string(fieldName)) + if !found { + return NewErr("no such field: %s", field) + } + if ft.IsSet == nil { + return False + } + return Bool(ft.IsSet(o.value)) +} + +func (o *testStructVal) Type() ref.Type { + return o.st +} + +func (o *testStructVal) Value() any { + return o.value +} + +func newTestStructTypeRegistry(t *testing.T) *Registry { + t.Helper() + desc := &testStructType{ + typeName: "custom.MyStruct", + reflectType: reflect.TypeOf(dummyNativeStruct{}), + fields: map[string]*FieldType{ + "Foo": { + Type: StringType, + GetFrom: func(obj any) (any, error) { + if s, ok := obj.(dummyNativeStruct); ok { + return s.Foo, nil + } + if s, ok := obj.(*dummyNativeStruct); ok { + return s.Foo, nil + } + return nil, fmt.Errorf("unexpected type: %T", obj) + }, + IsSet: func(obj any) bool { return true }, + }, + "Bar": { + Type: IntType, + GetFrom: func(obj any) (any, error) { + if s, ok := obj.(dummyNativeStruct); ok { + return s.Bar, nil + } + if s, ok := obj.(*dummyNativeStruct); ok { + return s.Bar, nil + } + return nil, fmt.Errorf("unexpected type: %T", obj) + }, + IsSet: func(obj any) bool { return true }, + }, + }, + } + reg, err := NewRegistry(Types(desc)) + if err != nil { + t.Fatalf("NewRegistry() failed: %v", err) + } + return reg +} + +type testCustomProvider struct { + enumVal ref.Val + identVal ref.Val + structType *Type + fieldNames []string + fieldType *FieldType + newValue ref.Val +} + +func (p *testCustomProvider) EnumValue(enumName string) ref.Val { + if enumName == "custom.Enum.VAL" { + return p.enumVal + } + return NewErr("unknown enum name '%s'", enumName) +} + +func (p *testCustomProvider) FindIdent(identName string) (ref.Val, bool) { + if identName == "customIdent" { + return p.identVal, true + } + return nil, false +} + +func (p *testCustomProvider) FindStructType(structType string) (*Type, bool) { + if structType == "custom.ProviderStruct" { + return p.structType, true + } + return nil, false +} + +func (p *testCustomProvider) FindStructFieldNames(structType string) ([]string, bool) { + if structType == "custom.ProviderStruct" { + return p.fieldNames, true + } + return []string{}, false +} + +func (p *testCustomProvider) FindStructFieldType(structType, fieldName string) (*FieldType, bool) { + if structType == "custom.ProviderStruct" && fieldName == "customField" { + return p.fieldType, true + } + return nil, false +} + +func (p *testCustomProvider) NewValue(structType string, fields map[string]ref.Val) ref.Val { + if structType == "custom.ProviderStruct" { + return p.newValue + } + return NewErr("unknown type '%s'", structType) +} + +func (p *testCustomProvider) FindStructFieldDescription(structType, fieldName string) (string, bool) { + if structType == "custom.ProviderStruct" && fieldName == "customField" { + return "Custom field documentation", true + } + return "", false +} + +func (p *testCustomProvider) FindType(structType string) (*exprpb.Type, bool) { + if structType == "custom.ProviderStruct" { + return &exprpb.Type{ + TypeKind: &exprpb.Type_MessageType{MessageType: structType}, + }, true + } + return nil, false +} + +func (p *testCustomProvider) FindFieldType(structType, fieldName string) (*ref.FieldType, bool) { + if structType == "custom.ProviderStruct" && fieldName == "customField" { + return &ref.FieldType{ + Type: &exprpb.Type{TypeKind: &exprpb.Type_Primitive{Primitive: exprpb.Type_STRING}}, + }, true + } + return nil, false +} + +type customNativeType struct{} + +type testCustomAdapter struct { + adaptedVal ref.Val +} + +func (a *testCustomAdapter) NativeToValue(value any) ref.Val { + if _, ok := value.(customNativeType); ok { + return a.adaptedVal + } + return UnsupportedRefValConversionErr(value) +} + +type testCustomCombined struct { + testCustomProvider + testCustomAdapter +} + +func TestComposeTypes_SameRegistry(t *testing.T) { + reg, err := NewRegistry() + if err != nil { + t.Fatalf("NewRegistry() failed: %v", err) + } + + p, a, err := ComposeTypes(reg, reg, ProtoTypeDefs(&proto3pb.TestAllTypes{})) + if err != nil { + t.Fatalf("ComposeTypes() failed: %v", err) + } + if p != reg || a != reg { + t.Errorf("ComposeTypes() with same registry instance returned different instances: p=%v, a=%v, wanted reg=%v", p, a, reg) + } + + // Verify type was registered directly on reg + _, found := reg.FindStructType("google.expr.proto3.test.TestAllTypes") + if !found { + t.Errorf("FindStructType() did not find registered type on same registry instance") + } +} + +func TestComposeTypes_DifferentRegistries(t *testing.T) { + reg1, err := NewRegistry(ProtoTypeDefs(&proto3pb.TestAllTypes{})) + if err != nil { + t.Fatalf("NewRegistry(reg1) failed: %v", err) + } + reg2, err := NewRegistry(ProtoTypeDefs(&exprpb.ParsedExpr{})) + if err != nil { + t.Fatalf("NewRegistry(reg2) failed: %v", err) + } + + p, a, err := ComposeTypes(reg1, reg2, ProtoTypeDefs(&exprpb.SourceInfo{})) + if err != nil { + t.Fatalf("ComposeTypes() failed: %v", err) + } + if p == reg1 || p == reg2 { + t.Errorf("ComposeTypes() should return a new composed registry, got p=%v", p) + } + if any(p) != any(a) { + t.Errorf("ComposeTypes() returned different provider and adapter: p=%v, a=%v", p, a) + } + + composedReg := p.(*Registry) + + t.Run("registered type on composed registry", func(t *testing.T) { + _, found := composedReg.FindStructType("google.api.expr.v1alpha1.SourceInfo") + if !found { + t.Errorf("FindStructType() failed for newly registered type on composed registry") + } + }) + + t.Run("provider type via proxy", func(t *testing.T) { + _, found := composedReg.FindStructType("google.expr.proto3.test.TestAllTypes") + if !found { + t.Errorf("FindStructType() failed for provider (reg1) type on composed registry") + } + }) + + t.Run("adapter NativeToValue via proxy", func(t *testing.T) { + parsedExpr := &exprpb.ParsedExpr{} + val := composedReg.NativeToValue(parsedExpr) + if IsError(val) { + t.Errorf("NativeToValue() failed for adapter (reg2) type on composed registry: %v", val) + } + }) +} + +func TestComposeTypes_RegistryProviderCustomAdapter(t *testing.T) { + reg, err := NewRegistry(ProtoTypeDefs(&proto3pb.TestAllTypes{})) + if err != nil { + t.Fatalf("NewRegistry() failed: %v", err) + } + customAdapt := &testCustomAdapter{adaptedVal: String("adapted_success")} + + p, a, err := ComposeTypes(reg, customAdapt) + if err != nil { + t.Fatalf("ComposeTypes() failed: %v", err) + } + if any(p) != any(a) { + t.Errorf("ComposeTypes() returned different provider and adapter: p=%v, a=%v", p, a) + } + composedReg := p.(*Registry) + + t.Run("provider delegation", func(t *testing.T) { + _, found := composedReg.FindStructType("google.expr.proto3.test.TestAllTypes") + if !found { + t.Errorf("FindStructType() failed to delegate to provider registry") + } + }) + + t.Run("adapter delegation", func(t *testing.T) { + val := composedReg.NativeToValue(customNativeType{}) + if IsError(val) || val.(String) != "adapted_success" { + t.Errorf("NativeToValue() failed to delegate to custom adapter: got %v, wanted adapted_success", val) + } + }) +} + +func TestComposeTypes_CustomProviderRegistryAdapter(t *testing.T) { + customProv := &testCustomProvider{ + enumVal: Int(42), + identVal: String("ident_ok"), + structType: NewTypeTypeWithParam(NewObjectType("custom.ProviderStruct")), + fieldNames: []string{"customField"}, + fieldType: &FieldType{Type: StringType}, + newValue: String("new_val_ok"), + } + reg, err := NewRegistry(ProtoTypeDefs(&proto3pb.TestAllTypes{})) + if err != nil { + t.Fatalf("NewRegistry() failed: %v", err) + } + + p, a, err := ComposeTypes(customProv, reg) + if err != nil { + t.Fatalf("ComposeTypes() failed: %v", err) + } + if any(p) != any(a) { + t.Errorf("ComposeTypes() returned different provider and adapter: p=%v, a=%v", p, a) + } + composedReg := p.(*Registry) + + t.Run("EnumValue", func(t *testing.T) { + if enumVal := composedReg.EnumValue("custom.Enum.VAL"); IsError(enumVal) || enumVal.(Int) != 42 { + t.Errorf("EnumValue() proxy failed: got %v, wanted 42", enumVal) + } + }) + + t.Run("FindIdent", func(t *testing.T) { + if ident, found := composedReg.FindIdent("customIdent"); !found || ident.(String) != "ident_ok" { + t.Errorf("FindIdent() proxy failed: got %v, found %v", ident, found) + } + }) + + t.Run("FindStructType", func(t *testing.T) { + if st, found := composedReg.FindStructType("custom.ProviderStruct"); !found || st == nil { + t.Errorf("FindStructType() proxy failed: got %v, found %v", st, found) + } + }) + + t.Run("FindStructFieldNames", func(t *testing.T) { + if fields, found := composedReg.FindStructFieldNames("custom.ProviderStruct"); !found || len(fields) != 1 || fields[0] != "customField" { + t.Errorf("FindStructFieldNames() proxy failed: got %v, found %v", fields, found) + } + }) + + t.Run("FindStructFieldType", func(t *testing.T) { + if ft, found := composedReg.FindStructFieldType("custom.ProviderStruct", "customField"); !found || ft.Type != StringType { + t.Errorf("FindStructFieldType() proxy failed: got %v, found %v", ft, found) + } + }) + + t.Run("NewValue", func(t *testing.T) { + if nv := composedReg.NewValue("custom.ProviderStruct", nil); IsError(nv) || nv.(String) != "new_val_ok" { + t.Errorf("NewValue() proxy failed: got %v", nv) + } + }) + + t.Run("NativeToValue adapter proxy", func(t *testing.T) { + msg := &proto3pb.TestAllTypes{} + val := composedReg.NativeToValue(msg) + if IsError(val) { + t.Errorf("NativeToValue() proxy to registry adapter failed: %v", val) + } + }) +} + +func TestComposeTypes_CustomProviderAndAdapter(t *testing.T) { + customProv := &testCustomProvider{ + identVal: String("ident_val"), + } + customAdapt := &testCustomAdapter{ + adaptedVal: String("adapted_val"), + } + + p, a, err := ComposeTypes(customProv, customAdapt) + if err != nil { + t.Fatalf("ComposeTypes() failed: %v", err) + } + if any(p) != any(a) { + t.Errorf("ComposeTypes() returned different provider and adapter: p=%v, a=%v", p, a) + } + composedReg := p.(*Registry) + + t.Run("FindIdent", func(t *testing.T) { + if ident, found := composedReg.FindIdent("customIdent"); !found || ident.(String) != "ident_val" { + t.Errorf("FindIdent() failed on composed registry: got %v", ident) + } + }) + + t.Run("NativeToValue", func(t *testing.T) { + if val := composedReg.NativeToValue(customNativeType{}); IsError(val) || val.(String) != "adapted_val" { + t.Errorf("NativeToValue() failed on composed registry: got %v", val) + } + }) +} + +func TestComposeTypes_SameCustomInstance(t *testing.T) { + customBoth := &testCustomCombined{ + testCustomProvider: testCustomProvider{identVal: String("combined_ident")}, + testCustomAdapter: testCustomAdapter{adaptedVal: String("combined_adapt")}, + } + + p, a, err := ComposeTypes(customBoth, customBoth) + if err != nil { + t.Fatalf("ComposeTypes() failed: %v", err) + } + if any(p) != any(a) { + t.Errorf("ComposeTypes() returned different provider and adapter: p=%v, a=%v", p, a) + } + if _, ok := p.(*Registry); !ok { + t.Fatalf("ComposeTypes() should return a *Registry instance, got %T", p) + } + + composedReg := p.(*Registry) + + t.Run("FindIdent", func(t *testing.T) { + if ident, found := composedReg.FindIdent("customIdent"); !found || ident.(String) != "combined_ident" { + t.Errorf("FindIdent() failed: got %v", ident) + } + }) + + t.Run("NativeToValue", func(t *testing.T) { + if val := composedReg.NativeToValue(customNativeType{}); IsError(val) || val.(String) != "combined_adapt" { + t.Errorf("NativeToValue() failed: got %v", val) + } + }) +} + +func TestComposeTypes_ErrorCases(t *testing.T) { + reg, err := NewRegistry() + if err != nil { + t.Fatalf("NewRegistry() failed: %v", err) + } + + tests := []struct { + name string + types []any + }{ + { + name: "unsupported type", + types: []any{12345}, + }, + { + name: "conflicting type definitions", + types: []any{ + NewTypeValue("http.Request", traits.ReceiverType), + NewObjectType("http.Request", traits.ReceiverType), + }, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + _, _, err := ComposeTypes(reg, reg, tc.types...) + if err == nil { + t.Errorf("ComposeTypes() expected error, got nil") + } + }) + } +} + +func TestComposeTypes_CopyPreservesProxy(t *testing.T) { + customProv := &testCustomProvider{identVal: String("copied_ident")} + customAdapt := &testCustomAdapter{adaptedVal: String("copied_adapt")} + + p, _, err := ComposeTypes(customProv, customAdapt) + if err != nil { + t.Fatalf("ComposeTypes() failed: %v", err) + } + + reg := p.(*Registry) + copiedReg := reg.Copy() + + t.Run("FindIdent", func(t *testing.T) { + if ident, found := copiedReg.FindIdent("customIdent"); !found || ident.(String) != "copied_ident" { + t.Errorf("Copied registry FindIdent() failed: got %v", ident) + } + }) + + t.Run("NativeToValue", func(t *testing.T) { + if val := copiedReg.NativeToValue(customNativeType{}); IsError(val) || val.(String) != "copied_adapt" { + t.Errorf("Copied registry NativeToValue() failed: got %v", val) + } + }) +} + +func TestRegistryJSONFieldNamesDefault(t *testing.T) { + reg, err := NewRegistry(ProtoTypeDefs(&proto3pb.TestAllTypes{})) + if err != nil { + t.Fatalf("NewRegistry() failed: %v", err) + } + if reg.JSONFieldNames() { + t.Errorf("JSONFieldNames() default expected false, got true") + } +} + +func TestRegistryWithJSONFieldNames(t *testing.T) { + reg, err := NewRegistry(ProtoTypeDefs(&proto3pb.TestAllTypes{})) + if err != nil { + t.Fatalf("NewRegistry() failed: %v", err) + } + err = reg.WithJSONFieldNames(true) + if err != nil { + t.Fatalf("WithJSONFieldNames(true) failed: %v", err) + } + if !reg.JSONFieldNames() { + t.Errorf("JSONFieldNames() after enabling expected true, got false") + } +} + +func TestRegistryWithJSONFieldNamesIdempotent(t *testing.T) { + reg, err := NewRegistry(ProtoTypeDefs(&proto3pb.TestAllTypes{})) + if err != nil { + t.Fatalf("NewRegistry() failed: %v", err) + } + err = reg.WithJSONFieldNames(true) + if err != nil { + t.Fatalf("WithJSONFieldNames(true) failed: %v", err) + } + err = reg.WithJSONFieldNames(true) + if err != nil { + t.Fatalf("WithJSONFieldNames(true) idempotent failed: %v", err) + } + if !reg.JSONFieldNames() { + t.Errorf("JSONFieldNames() expected true, got false") + } +} + +func TestRegistryWithJSONFieldNamesDisabled(t *testing.T) { + reg, err := NewRegistry(ProtoTypeDefs(&proto3pb.TestAllTypes{})) + if err != nil { + t.Fatalf("NewRegistry() failed: %v", err) + } + err = reg.WithJSONFieldNames(true) + if err != nil { + t.Fatalf("WithJSONFieldNames(true) failed: %v", err) + } + err = reg.WithJSONFieldNames(false) + if err != nil { + t.Fatalf("WithJSONFieldNames(false) failed: %v", err) + } + if reg.JSONFieldNames() { + t.Errorf("JSONFieldNames() after disabling expected false, got true") + } +} + +func TestRegistry_EnumValueEdgeCases(t *testing.T) { + customProv := &testCustomProvider{ + enumVal: Int(99), + } + reg := newTestRegistry(t, ProtoTypeDefs(&proto3pb.TestAllTypes{})) + err := reg.RegisterDescriptor(proto3pb.GlobalEnum_GOO.Descriptor().ParentFile()) + if err != nil { + t.Fatalf("RegisterDescriptor() failed: %v", err) + } + + p, _, err := ComposeTypes(customProv, reg) + if err != nil { + t.Fatalf("ComposeTypes() failed: %v", err) + } + composedReg := p.(*Registry) + + tests := []struct { + enumName string + target *Registry + wantVal ref.Val + isErr bool + }{ + { + enumName: "google.expr.proto3.test.GlobalEnum.GOO", + target: reg, + wantVal: Int(proto3pb.GlobalEnum_GOO.Number()), + }, + { + enumName: "custom.Enum.VAL", + target: composedReg, + wantVal: Int(99), + }, + { + enumName: "non.existent.Enum", + target: reg, + isErr: true, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.enumName, func(t *testing.T) { + got := tc.target.EnumValue(tc.enumName) + if tc.isErr { + if !IsError(got) { + t.Errorf("EnumValue(%s) expected error, got %v", tc.enumName, got) + } + } else { + if IsError(got) || got.Equal(tc.wantVal) != True { + t.Errorf("EnumValue(%s) got %v, wanted %v", tc.enumName, got, tc.wantVal) + } + } + }) + } +} + +func TestRegistry_FindIdentEdgeCases(t *testing.T) { + customProv := &testCustomProvider{ + identVal: String("ident_found"), + } + reg := newTestRegistry(t, ProtoTypeDefs(&proto3pb.TestAllTypes{})) + err := reg.RegisterDescriptor(proto3pb.GlobalEnum_GOO.Descriptor().ParentFile()) + if err != nil { + t.Fatalf("RegisterDescriptor() failed: %v", err) + } + + p, _, err := ComposeTypes(customProv, reg) + if err != nil { + t.Fatalf("ComposeTypes() failed: %v", err) + } + composedReg := p.(*Registry) + + tests := []struct { + identName string + target *Registry + wantFound bool + }{ + { + identName: "int", + target: composedReg, + wantFound: true, + }, + { + identName: "google.expr.proto3.test.GlobalEnum.GOO", + target: reg, + wantFound: true, + }, + { + identName: "customIdent", + target: composedReg, + wantFound: true, + }, + { + identName: "nonExistentIdent", + target: composedReg, + wantFound: false, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.identName, func(t *testing.T) { + _, found := tc.target.FindIdent(tc.identName) + if found != tc.wantFound { + t.Errorf("FindIdent(%s) found=%v, wanted %v", tc.identName, found, tc.wantFound) + } + }) + } +} + +func TestRegistry_FindTypeEdgeCases(t *testing.T) { + customProv := &testCustomProvider{} + reg := newTestRegistry(t, ProtoTypeDefs(&proto3pb.TestAllTypes{})) + + p, _, err := ComposeTypes(reg, &testCustomAdapter{}) + if err != nil { + t.Fatalf("ComposeTypes() failed: %v", err) + } + composedReg := p.(*Registry) + + p2, _, err := ComposeTypes(customProv, reg) + if err != nil { + t.Fatalf("ComposeTypes(customProv, reg) failed: %v", err) + } + composedReg2 := p2.(*Registry) + + tests := []struct { + typeName string + target *Registry + wantFound bool + }{ + { + typeName: "google.expr.proto3.test.TestAllTypes", + target: composedReg, + wantFound: true, + }, + { + typeName: ".google.expr.proto3.test.TestAllTypes", + target: composedReg, + wantFound: true, + }, + { + typeName: "custom.ProviderStruct", + target: composedReg2, + wantFound: true, + }, + { + typeName: "non.existent.Type", + target: composedReg, + wantFound: false, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.typeName, func(t *testing.T) { + got, found := tc.target.FindType(tc.typeName) + if found != tc.wantFound { + t.Errorf("FindType(%s) found=%v, wanted %v", tc.typeName, found, tc.wantFound) + } + if found && got == nil { + t.Errorf("FindType(%s) returned nil type despite found=true", tc.typeName) + } + }) + } +} + +func TestRegistry_FindFieldTypeEdgeCases(t *testing.T) { + customProv := &testCustomProvider{} + reg := newTestRegistry(t, ProtoTypeDefs(&proto3pb.TestAllTypes{})) + + p, _, err := ComposeTypes(reg, &testCustomAdapter{}) + if err != nil { + t.Fatalf("ComposeTypes() failed: %v", err) + } + composedReg := p.(*Registry) + + p2, _, err := ComposeTypes(customProv, reg) + if err != nil { + t.Fatalf("ComposeTypes(customProv, reg) failed: %v", err) + } + composedReg2 := p2.(*Registry) + + tests := []struct { + structType string + fieldName string + target *Registry + wantFound bool + }{ + { + structType: "google.expr.proto3.test.TestAllTypes", + fieldName: "single_int32", + target: composedReg, + wantFound: true, + }, + { + structType: ".google.expr.proto3.test.TestAllTypes", + fieldName: "single_int32", + target: composedReg, + wantFound: true, + }, + { + structType: "custom.ProviderStruct", + fieldName: "customField", + target: composedReg2, + wantFound: false, + }, + { + structType: "google.expr.proto3.test.TestAllTypes", + fieldName: "non_existent_field", + target: composedReg, + wantFound: false, + }, + { + structType: "non.existent.Type", + fieldName: "some_field", + target: composedReg, + wantFound: false, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.structType+"."+tc.fieldName, func(t *testing.T) { + got, found := tc.target.FindFieldType(tc.structType, tc.fieldName) + if found != tc.wantFound { + t.Errorf("FindFieldType(%s, %s) found=%v, wanted %v", tc.structType, tc.fieldName, found, tc.wantFound) + } + if found && got == nil { + t.Errorf("FindFieldType(%s, %s) returned nil field type despite found=true", tc.structType, tc.fieldName) + } + }) + } +} + +func TestRegistry_FindStructFieldDescriptionEdgeCases(t *testing.T) { + customProv := &testCustomProvider{} + reg := newTestRegistry(t, ProtoTypeDefs(&proto3pb.TestAllTypes{})) + + p, _, err := ComposeTypes(customProv, reg) + if err != nil { + t.Fatalf("ComposeTypes() failed: %v", err) + } + composedReg := p.(*Registry) + + tests := []struct { + structType string + fieldName string + wantFound bool + }{ + { + structType: "custom.ProviderStruct", + fieldName: "customField", + wantFound: true, + }, + { + structType: "google.expr.proto3.test.TestAllTypes", + fieldName: "non_existent_field", + wantFound: false, + }, + { + structType: "non.existent.Type", + fieldName: "some_field", + wantFound: false, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.structType+"."+tc.fieldName, func(t *testing.T) { + _, found := composedReg.FindStructFieldDescription(tc.structType, tc.fieldName) + if found != tc.wantFound { + t.Errorf("FindStructFieldDescription(%s, %s) found=%v, wanted %v", tc.structType, tc.fieldName, found, tc.wantFound) + } + }) + } +} + +type testDummyStructDescriptor struct { + *Type + reflectType reflect.Type + fieldType *FieldType +} + +func (d *testDummyStructDescriptor) ReflectType() reflect.Type { + return d.reflectType +} + +func (d *testDummyStructDescriptor) FieldNames() []string { + return []string{"DummyField"} +} + +func (d *testDummyStructDescriptor) FindFieldType(fieldName string) (*FieldType, bool) { + if fieldName == "DummyField" { + return d.fieldType, true + } + return nil, false +} + +func (d *testDummyStructDescriptor) NewValue(adapter Adapter, fields map[string]ref.Val) ref.Val { + return String("dummy_struct_new") +} + +func (d *testDummyStructDescriptor) Adapt(adapter Adapter, value any) ref.Val { + return String("dummy_struct_adapted") +} + +type testSimpleProvider struct{} + +func (p *testSimpleProvider) EnumValue(enumName string) ref.Val { return NewErr("no enum") } +func (p *testSimpleProvider) FindIdent(identName string) (ref.Val, bool) { return nil, false } +func (p *testSimpleProvider) FindStructType(structType string) (*Type, bool) { + if structType == "simple.Struct" { + return NewTypeTypeWithParam(NewObjectType("simple.Struct")), true + } + return nil, false +} +func (p *testSimpleProvider) FindStructFieldNames(structType string) ([]string, bool) { + return nil, false +} +func (p *testSimpleProvider) FindStructFieldType(structType, fieldName string) (*FieldType, bool) { + if structType == "simple.Struct" && fieldName == "simpleField" { + return &FieldType{Type: StringType}, true + } + return nil, false +} +func (p *testSimpleProvider) NewValue(structType string, fields map[string]ref.Val) ref.Val { + return NewErr("no value") +} + +func TestRegistry_FindStructDescriptorByReflectType(t *testing.T) { + reg, err := NewRegistry() + if err != nil { + t.Fatalf("NewRegistry() failed: %v", err) + } + + st := &testDummyStructDescriptor{ + Type: NewObjectType("dummy.Struct"), + reflectType: reflect.TypeOf(dummyNativeStruct{}), + fieldType: &FieldType{Type: StringType}, + } + + err = reg.RegisterType(st) + if err != nil { + t.Fatalf("RegisterType() failed: %v", err) + } + + t.Run("Pointer lookup when registered as value", func(t *testing.T) { + val := reg.NativeToValue(&dummyNativeStruct{}) + if IsError(val) || val.(String) != "dummy_struct_adapted" { + t.Errorf("NativeToValue(&dummyNativeStruct{}) = %v, wanted dummy_struct_adapted", val) + } + }) + + t.Run("Value lookup when registered as value", func(t *testing.T) { + val := reg.NativeToValue(dummyNativeStruct{}) + if IsError(val) || val.(String) != "dummy_struct_adapted" { + t.Errorf("NativeToValue(dummyNativeStruct{}) = %v, wanted dummy_struct_adapted", val) + } + }) + + t.Run("Direct findStructDescriptorByReflectType tests", func(t *testing.T) { + pNil, foundNil := reg.findStructDescriptorByReflectType(nil) + if foundNil || pNil != nil { + t.Errorf("findStructDescriptorByReflectType(nil) expected false, got %v", foundNil) + } + + // Manually set pointer-only key in reflectTypes + reg.reflectTypes[reflect.TypeOf(&dummyNativeStruct{})] = st + delete(reg.reflectTypes, reflect.TypeOf(dummyNativeStruct{})) + + // Pass value type (kind != Ptr) -> hits else branch looking up PointerTo + stFound, foundVal := reg.findStructDescriptorByReflectType(reflect.TypeOf(dummyNativeStruct{})) + if !foundVal || stFound != st { + t.Errorf("findStructDescriptorByReflectType(value) expected st, got %v", stFound) + } + + // Manually set value-only key in reflectTypes + reg.reflectTypes[reflect.TypeOf(dummyNativeStruct{})] = st + delete(reg.reflectTypes, reflect.TypeOf(&dummyNativeStruct{})) + + // Pass pointer type (kind == Ptr) -> hits Ptr branch looking up Elem + stFound, foundPtr := reg.findStructDescriptorByReflectType(reflect.TypeOf(&dummyNativeStruct{})) + if !foundPtr || stFound != st { + t.Errorf("findStructDescriptorByReflectType(pointer) expected st, got %v", stFound) + } + }) + + t.Run("FindFieldType on registered StructTypeDescriptor", func(t *testing.T) { + ft, found := reg.FindFieldType("dummy.Struct", "DummyField") + if !found || ft == nil { + t.Fatalf("FindFieldType(dummy.Struct, DummyField) failed") + } + }) + + t.Run("Invalid field type conversion error", func(t *testing.T) { + stInvalid := &testDummyStructDescriptor{ + Type: NewObjectType("invalid.Struct"), + reflectType: reflect.TypeOf(customNativeType{}), + fieldType: &FieldType{Type: &Type{}}, + } + err := reg.RegisterType(stInvalid) + if err != nil { + t.Fatalf("RegisterType(stInvalid) failed: %v", err) + } + _, found := reg.FindFieldType("invalid.Struct", "DummyField") + if found { + t.Errorf("FindFieldType(invalid.Struct, DummyField) expected false due to TypeToExprType error, got true") + } + }) +} + +func TestRegistry_FindFieldType_SimpleProviderFallback(t *testing.T) { + baseReg, err := NewRegistry() + if err != nil { + t.Fatalf("NewRegistry() failed: %v", err) + } + + simpleProv := &testSimpleProvider{} + p, _, err := ComposeTypes(simpleProv, baseReg) + if err != nil { + t.Fatalf("ComposeTypes() failed: %v", err) + } + composedReg := p.(*Registry) + + t.Run("fallback to FindStructFieldType", func(t *testing.T) { + ft, found := composedReg.FindFieldType("simple.Struct", "simpleField") + if !found || ft == nil { + t.Fatalf("FindFieldType(simple.Struct, simpleField) failed on fallback provider") + } + }) + + t.Run("FindStructFieldDescription without interface", func(t *testing.T) { + _, found := composedReg.FindStructFieldDescription("simple.Struct", "simpleField") + if found { + t.Errorf("FindStructFieldDescription() on simpleProv expected false, got true") + } + }) +} + +func TestRegistry_NewRegistry_OptionErrors(t *testing.T) { + optErr := RegistryOption(func(r *Registry) (*Registry, error) { + return nil, fmt.Errorf("registry option error") + }) + + t.Run("NewProtoRegistry", func(t *testing.T) { + _, err := NewProtoRegistry(optErr) + if err == nil || err.Error() != "registry option error" { + t.Errorf("NewProtoRegistry() expected 'registry option error', got %v", err) + } + }) + + t.Run("NewRegistry", func(t *testing.T) { + _, err := NewRegistry(optErr) + if err == nil || err.Error() != "registry option error" { + t.Errorf("NewRegistry() expected 'registry option error', got %v", err) + } + }) +} + +func TestRegistry_RegisterTypeEdgeCases(t *testing.T) { + tests := []struct { + name string + targetType *Type + }{ + { + name: "conflicting traits", + targetType: NewTypeValue("bool", traits.ContainerType), + }, + { + name: "conflicting type definition", + targetType: NewObjectType("bool"), + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + reg, err := NewRegistry() + if err != nil { + t.Fatalf("NewRegistry() failed: %v", err) + } + err = reg.RegisterType(tc.targetType) + if err == nil { + t.Errorf("RegisterType() expected error, got nil") + } + }) } - return reg } diff --git a/common/types/struct.go b/common/types/struct.go new file mode 100644 index 000000000..61740a70e --- /dev/null +++ b/common/types/struct.go @@ -0,0 +1,39 @@ +// Copyright 2026 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 types + +import ( + "reflect" + + "github.com/google/cel-go/common/types/ref" +) + +// StructTypeDescriptor describes a CEL struct type, providing field metadata and value instantiation. +type StructTypeDescriptor interface { + // ReflectType returns the backing Go reflect.Type associated with the struct (or nil if non-reflected). + ReflectType() reflect.Type + + // FieldNames returns the list of field names defined on the struct. + FieldNames() []string + + // FindFieldType returns the field type and a boolean indicating if the field exists. + FindFieldType(fieldName string) (*FieldType, bool) + + // NewValue creates a new CEL struct value from the given map of field values. + NewValue(adapter Adapter, fields map[string]ref.Val) ref.Val + + // Adapt converts a native Go value (struct instance or pointer) to a CEL ref.Val. + Adapt(adapter Adapter, value any) ref.Val +} diff --git a/interpreter/interpreter_test.go b/interpreter/interpreter_test.go index b4b090ed8..2c3880251 100644 --- a/interpreter/interpreter_test.go +++ b/interpreter/interpreter_test.go @@ -2639,9 +2639,13 @@ func newTestEnv(t testing.TB, cont *containers.Container, reg *types.Registry) * func newTestRegistry(t testing.TB, opts ...types.RegistryOption) *types.Registry { t.Helper() - reg, err := types.NewProtoRegistry(opts...) + var o []any + for _, opt := range opts { + o = append(o, opt) + } + reg, err := types.NewRegistry(o...) if err != nil { - t.Fatalf("types.NewProtoRegistry() failed: %v", err) + t.Fatalf("types.NewRegistry() failed: %v", err) } return reg } From ab72257a23dbb7dc60711e83e127bb6f8ee19e5d Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Mon, 3 Aug 2026 21:11:04 -0700 Subject: [PATCH 09/37] Move native type support into the Core CEL library (#1396) --- common/types/BUILD.bazel | 8 + common/types/native.go | 578 ++++++++++++ common/types/native_test.go | 1615 +++++++++++++++++++++++++++++++++ common/types/provider.go | 72 +- common/types/provider_test.go | 44 + ext/native.go | 788 +--------------- ext/native_test.go | 48 +- 7 files changed, 2365 insertions(+), 788 deletions(-) create mode 100644 common/types/native.go create mode 100644 common/types/native_test.go diff --git a/common/types/BUILD.bazel b/common/types/BUILD.bazel index dae581820..4ecc40031 100644 --- a/common/types/BUILD.bazel +++ b/common/types/BUILD.bazel @@ -21,6 +21,7 @@ go_library( "format.go", "list.go", "map.go", + "native.go", "null.go", "object.go", "optional.go", @@ -69,6 +70,7 @@ go_test( "json_struct_test.go", "list_test.go", "map_test.go", + "native_test.go", "null_test.go", "object_test.go", "optional_test.go", @@ -83,13 +85,19 @@ go_test( ], embed = [":go_default_library"], deps = [ + "//cel:go_default_library", + "//common/types/pb:go_default_library", "//common/types/ref:go_default_library", + "//common/types/traits:go_default_library", + "//ext:go_default_library", "//test:go_default_library", "//test/proto3pb:test_all_types_go_proto", "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", "@org_golang_google_protobuf//encoding/protojson:go_default_library", + "@org_golang_google_protobuf//proto:go_default_library", "@org_golang_google_protobuf//types/known/anypb:go_default_library", "@org_golang_google_protobuf//types/known/durationpb:go_default_library", + "@org_golang_google_protobuf//types/known/structpb:go_default_library", "@org_golang_google_protobuf//types/known/timestamppb:go_default_library", ], ) diff --git a/common/types/native.go b/common/types/native.go new file mode 100644 index 000000000..802abdff4 --- /dev/null +++ b/common/types/native.go @@ -0,0 +1,578 @@ +// Copyright 2022 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 me. +// limitations under the License. + +package types + +import ( + "errors" + "fmt" + "reflect" + "strings" + "time" + + "google.golang.org/protobuf/reflect/protoreflect" + + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + + structpb "google.golang.org/protobuf/types/known/structpb" +) + +var ( + nativeObjTraitMask = traits.FieldTesterType | traits.IndexerType + jsonValueType = reflect.TypeFor[*structpb.Value]() + jsonStructType = reflect.TypeFor[*structpb.Struct]() + + pbMsgInterfaceType = reflect.TypeFor[protoreflect.ProtoMessage]() + refValType = reflect.TypeFor[ref.Val]() + timestampType = reflect.TypeFor[time.Time]() + durationType = reflect.TypeFor[time.Duration]() + + errDuplicatedFieldName = errors.New("field name already exists in struct") +) + +// NewNativeType constructs a NativeType instance for a Go struct reflect.Type. +func NewNativeType(rawType reflect.Type, opts ...NativeTypeOption) (*NativeType, error) { + tpOptions := NativeTypeOptions{} + for _, opt := range opts { + if err := opt(&tpOptions); err != nil { + return nil, err + } + } + return newNativeType(rawType, tpOptions.fieldNameHandler) +} + +// NativeTypesFieldNameHandler is a handler for mapping a reflect.StructField to a CEL field name. +// This can be used to override the default Go struct field to CEL field name mapping. +type NativeTypesFieldNameHandler = func(field reflect.StructField) string + +// NativeTypeOptions holds options for native types. +type NativeTypeOptions struct { + fieldNameHandler NativeTypesFieldNameHandler +} + +// NativeTypeOption is a functional option for configuring handling of native types. +type NativeTypeOption func(*NativeTypeOptions) error + +// ParseStructTags configures if native types field names should be overridable by CEL struct tags. +// This is equivalent to ParseStructTag("cel"). +func ParseStructTags(enabled bool) NativeTypeOption { + if enabled { + return ParseStructTag("cel") + } + return ParseStructField(nil) +} + +// ParseStructTag configures the struct tag to parse. The 0th item in the tag is used as the name of the CEL field. +func ParseStructTag(tag string) NativeTypeOption { + return ParseStructField(fieldNameByTag(tag)) +} + +// ParseStructField configures how to parse Go struct fields. It can be used to customize struct field parsing. +func ParseStructField(handler NativeTypesFieldNameHandler) NativeTypeOption { + return func(opts *NativeTypeOptions) error { + opts.fieldNameHandler = handler + return nil + } +} + +func fieldNameByTag(structTagToParse string) func(field reflect.StructField) string { + return func(field reflect.StructField) string { + tag, found := field.Tag.Lookup(structTagToParse) + if found { + splits := strings.Split(tag, ",") + if len(splits) > 0 { + name := splits[0] + return name + } + } + return field.Name + } +} + +func isSkippedFieldName(name string) bool { + return name == "" || name == "-" +} + +// NativeType represents a CEL struct type descriptor generated from a native Go struct. +type NativeType struct { + typeName string + refType reflect.Type + fieldsByName map[string]reflect.StructField +} + +// ReflectType implements StructTypeDescriptor. +func (t *NativeType) ReflectType() reflect.Type { + return t.refType +} + +// Adapt implements StructTypeDescriptor. +func (t *NativeType) Adapt(adapter Adapter, value any) ref.Val { + if value == nil { + return NullValue + } + return &nativeObj{ + Adapter: adapter, + val: value, + valType: t, + refValue: reflect.ValueOf(value), + } +} + +// ConvertToNative implements ref.Val.ConvertToNative. +func (t *NativeType) ConvertToNative(typeDesc reflect.Type) (any, error) { + return nil, fmt.Errorf("type conversion error for type to '%v'", typeDesc) +} + +// ConvertToType implements ref.Val.ConvertToType. +func (t *NativeType) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case TypeType: + return TypeType + } + return NewErr("type conversion error from '%s' to '%s'", TypeType, typeVal) +} + +// Equal returns true if both type names are equal to each other. +func (t *NativeType) Equal(other ref.Val) ref.Val { + otherType, ok := other.(ref.Type) + return Bool(ok && t.TypeName() == otherType.TypeName()) +} + +// HasTrait implements the ref.Type interface method. +func (t *NativeType) HasTrait(trait int) bool { + return nativeObjTraitMask&trait == trait +} + +// String implements the fmt.Stringer interface method. +func (t *NativeType) String() string { + return t.typeName +} + +// Type implements the ref.Val interface method. +func (t *NativeType) Type() ref.Type { + return TypeType +} + +// TypeName implements the ref.Type interface method. +func (t *NativeType) TypeName() string { + return t.typeName +} + +// Value implements the ref.Val interface method. +func (t *NativeType) Value() any { + return t.typeName +} + +func (t *NativeType) hasField(fieldName string) (reflect.StructField, bool) { + f, found := t.fieldsByName[fieldName] + if !found { + return reflect.StructField{}, false + } + return f, true +} + +// FieldNames provides the list of field names for this type. +func (t *NativeType) FieldNames() []string { + fields := make([]string, 0, len(t.fieldsByName)) + for fieldName := range t.fieldsByName { + fields = append(fields, fieldName) + } + return fields +} + +// FindFieldType looks up a field by name and provides the type and accessors. +func (t *NativeType) FindFieldType(fieldName string) (*FieldType, bool) { + refField, found := t.hasField(fieldName) + if !found { + return nil, false + } + celType, ok := convertToCelType(refField.Type) + if !ok { + return nil, false + } + return &FieldType{ + Type: celType, + IsSet: func(obj any) bool { + refVal := reflect.Indirect(reflect.ValueOf(obj)) + refFieldVal := safeGetFieldByIndex(refVal, refField.Index) + return refFieldVal.IsValid() && !refFieldVal.IsZero() + }, + GetFrom: func(obj any) (any, error) { + refVal := reflect.Indirect(reflect.ValueOf(obj)) + refFieldVal := safeGetFieldByIndex(refVal, refField.Index) + return getFieldValue(refFieldVal), nil + }, + }, true +} + +// NewValue constructs a new native Go struct instance populated with given field values. +func (t *NativeType) NewValue(adapter Adapter, fields map[string]ref.Val) ref.Val { + refPtr := reflect.New(t.refType) + refVal := refPtr.Elem() + for fieldName, val := range fields { + refFieldDef, isDefined := t.hasField(fieldName) + if !isDefined { + return NewErr("no such field: %s", fieldName) + } + fieldVal, err := val.ConvertToNative(refFieldDef.Type) + if err != nil { + return NewErrFromString(err.Error()) + } + refField := safeSetFieldByIndex(refVal, refFieldDef.Index) + if !refField.IsValid() { + return NewErr("cannot set field: %s", fieldName) + } + refField.Set(reflect.ValueOf(fieldVal)) + } + return adapter.NativeToValue(refPtr.Interface()) +} + +type nativeObj struct { + Adapter + val any + valType *NativeType + refValue reflect.Value +} + +func (o *nativeObj) ConvertToNative(typeDesc reflect.Type) (any, error) { + if o.refValue.Type() == typeDesc { + return o.val, nil + } + if o.refValue.Kind() == reflect.Pointer && o.refValue.Type().Elem() == typeDesc { + return o.refValue.Elem().Interface(), nil + } + if typeDesc.Kind() == reflect.Pointer && o.refValue.Type() == typeDesc.Elem() { + ptr := reflect.New(typeDesc.Elem()) + ptr.Elem().Set(o.refValue) + return ptr.Interface(), nil + } + switch typeDesc { + case jsonValueType: + jsonStruct, err := o.ConvertToNative(jsonStructType) + if err != nil { + return nil, err + } + return structpb.NewStructValue(jsonStruct.(*structpb.Struct)), nil + case jsonStructType: + refVal := reflect.Indirect(o.refValue) + fields := make(map[string]*structpb.Value, refVal.NumField()) + for fieldName, fieldType := range o.valType.fieldsByName { + fieldValue := refVal.FieldByIndex(fieldType.Index) + if !fieldValue.IsValid() || fieldValue.IsZero() { + continue + } + fieldCELVal := o.NativeToValue(fieldValue.Interface()) + fieldJSONVal, err := fieldCELVal.ConvertToNative(jsonValueType) + if err != nil { + return nil, err + } + fields[fieldName] = fieldJSONVal.(*structpb.Value) + } + return &structpb.Struct{Fields: fields}, nil + } + return nil, fmt.Errorf("type conversion error from '%v' to '%v'", o.Type(), typeDesc) +} + +func (o *nativeObj) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case TypeType: + return o.valType + default: + if typeVal.TypeName() == o.valType.typeName { + return o + } + } + return NewErr("type conversion error from '%s' to '%s'", o.Type(), typeVal) +} + +func (o *nativeObj) Equal(other ref.Val) ref.Val { + otherNtv, ok := other.(*nativeObj) + if !ok { + return False + } + val := o.val + otherVal := otherNtv.val + refVal := o.refValue + otherRefVal := otherNtv.refValue + if refVal.Kind() != otherRefVal.Kind() { + if refVal.Kind() == reflect.Pointer { + val = refVal.Elem().Interface() + } else if otherRefVal.Kind() == reflect.Pointer { + otherVal = otherRefVal.Elem().Interface() + } + } + return Bool(reflect.DeepEqual(val, otherVal)) +} + +func (o *nativeObj) IsZeroValue() bool { + return reflect.Indirect(o.refValue).IsZero() +} + +func (o *nativeObj) IsSet(field ref.Val) ref.Val { + refField, refErr := o.getReflectedField(field) + if refErr != nil { + return refErr + } + return Bool(!refField.IsZero()) +} + +func (o *nativeObj) Get(field ref.Val) ref.Val { + refField, refErr := o.getReflectedField(field) + if refErr != nil { + return refErr + } + return adaptFieldValue(o, refField) +} + +func (o *nativeObj) getReflectedField(field ref.Val) (reflect.Value, ref.Val) { + fieldName, ok := field.(String) + if !ok { + return reflect.Value{}, MaybeNoSuchOverloadErr(field) + } + fieldNameStr := string(fieldName) + refField, isDefined := o.valType.hasField(fieldNameStr) + if !isDefined { + return reflect.Value{}, NewErr("no such field: %s", fieldName) + } + refVal := reflect.Indirect(o.refValue) + return safeGetFieldByIndex(refVal, refField.Index), nil +} + +func (o *nativeObj) Type() ref.Type { + return o.valType +} + +func (o *nativeObj) Value() any { + return o.val +} + +func newNativeTypes(rawType reflect.Type, fieldNameHandler NativeTypesFieldNameHandler) ([]*NativeType, error) { + nt, err := newNativeType(rawType, fieldNameHandler) + if err != nil { + return nil, err + } + result := []*NativeType{nt} + + alreadySeen := make(map[string]struct{}) + var iterateStructMembers func(reflect.Type) + iterateStructMembers = func(t reflect.Type) { + if t.Implements(reflect.TypeFor[ref.Val]()) { + return + } + if k := t.Kind(); k == reflect.Pointer || k == reflect.Slice || k == reflect.Array || k == reflect.Map { + iterateStructMembers(t.Elem()) + return + } + if t.Kind() != reflect.Struct { + return + } + if _, seen := alreadySeen[t.String()]; seen { + return + } + alreadySeen[t.String()] = struct{}{} + nt, ntErr := newNativeType(t, fieldNameHandler) + if ntErr != nil { + err = ntErr + return + } + result = append(result, nt) + + for _, field := range reflect.VisibleFields(t) { + if !field.IsExported() || !isSupportedType(field.Type) { + continue + } + iterateStructMembers(field.Type) + } + } + iterateStructMembers(rawType) + + return result, err +} + +func toFieldName(f reflect.StructField, fieldNameHandler NativeTypesFieldNameHandler) string { + if fieldNameHandler == nil { + return f.Name + } + return fieldNameHandler(f) +} + +func newNativeType(rawType reflect.Type, fieldNameHandler NativeTypesFieldNameHandler) (*NativeType, error) { + refType := rawType + if refType.Kind() == reflect.Pointer { + refType = refType.Elem() + } + if !isValidObjectType(refType) { + return nil, fmt.Errorf("unsupported reflect.Type %v, must be reflect.Struct", rawType) + } + + fieldsByName := make(map[string]reflect.StructField) + for _, field := range reflect.VisibleFields(refType) { + if !field.IsExported() || !isSupportedType(field.Type) { + continue + } + fieldName := toFieldName(field, fieldNameHandler) + if isSkippedFieldName(fieldName) { + continue + } + if _, found := fieldsByName[fieldName]; found { + return nil, fmt.Errorf("invalid field name `%s` in struct `%s`: %w", fieldName, refType.Name(), errDuplicatedFieldName) + } + fieldsByName[fieldName] = field + } + + return &NativeType{ + typeName: fmt.Sprintf("%s.%s", simplePkgAlias(refType.PkgPath()), refType.Name()), + refType: refType, + fieldsByName: fieldsByName, + }, nil +} + +func adaptFieldValue(adapter Adapter, refField reflect.Value) ref.Val { + return adapter.NativeToValue(getFieldValue(refField)) +} + +func safeSetFieldByIndex(v reflect.Value, index []int) reflect.Value { + for _, i := range index { + if v.Kind() == reflect.Pointer { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + if v.Kind() != reflect.Struct || i >= v.NumField() { + return reflect.Value{} + } + v = v.Field(i) + } + return v +} + +func safeGetFieldByIndex(v reflect.Value, index []int) reflect.Value { + for _, i := range index { + if v.Kind() == reflect.Pointer { + if v.IsNil() { + v = reflect.New(v.Type().Elem()).Elem() + } else { + v = v.Elem() + } + } + if v.Kind() != reflect.Struct || i >= v.NumField() { + return reflect.Value{} + } + v = v.Field(i) + } + return v +} + +func getFieldValue(refField reflect.Value) any { + if !refField.IsValid() { + return nil + } + if refField.IsZero() { + switch refField.Kind() { + case reflect.Struct: + if refField.Type() == timestampType { + return time.Unix(0, 0) + } + case reflect.Pointer: + return reflect.New(refField.Type().Elem()).Interface() + } + } + return refField.Interface() +} + +func simplePkgAlias(pkgPath string) string { + paths := strings.Split(pkgPath, "/") + if len(paths) == 0 { + return "" + } + return paths[len(paths)-1] +} + +func isValidObjectType(refType reflect.Type) bool { + return refType.Kind() == reflect.Struct +} + +func isSupportedType(refType reflect.Type) bool { + switch refType.Kind() { + case reflect.Chan, reflect.Complex64, reflect.Complex128, reflect.Func, reflect.UnsafePointer, reflect.Uintptr: + return false + case reflect.Array, reflect.Slice: + return isSupportedType(refType.Elem()) + case reflect.Map: + return isSupportedType(refType.Key()) && isSupportedType(refType.Elem()) + } + return true +} + +func convertToCelType(refType reflect.Type) (*Type, bool) { + switch refType.Kind() { + case reflect.Bool: + return BoolType, true + case reflect.Float32, reflect.Float64: + return DoubleType, true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if refType == durationType { + return DurationType, true + } + return IntType, true + case reflect.String: + return StringType, true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return UintType, true + case reflect.Array, reflect.Slice: + refElem := refType.Elem() + if refElem == reflect.TypeOf(byte(0)) { + return BytesType, true + } + elemType, ok := convertToCelType(refElem) + if !ok { + return nil, false + } + return NewListType(elemType), true + case reflect.Map: + keyType, ok := convertToCelType(refType.Key()) + if !ok { + return nil, false + } + elemType, ok := convertToCelType(refType.Elem()) + if !ok { + return nil, false + } + return NewMapType(keyType, elemType), true + case reflect.Struct: + if refType == timestampType { + return TimestampType, true + } + if refType.Implements(refValType) { + emptyCelVal := reflect.New(refType).Elem().Interface().(ref.Val) + return emptyCelVal.Type().(*Type), true + } + return NewObjectType( + fmt.Sprintf("%s.%s", simplePkgAlias(refType.PkgPath()), refType.Name()), + ), true + case reflect.Pointer: + if refType.Implements(refValType) { + emptyCelVal := reflect.New(refType.Elem()).Interface().(ref.Val) + return emptyCelVal.Type().(*Type), true + } + if refType.Implements(pbMsgInterfaceType) { + pbMsg := reflect.New(refType.Elem()).Interface().(protoreflect.ProtoMessage) + return NewObjectType(string(pbMsg.ProtoReflect().Descriptor().FullName())), true + } + return convertToCelType(refType.Elem()) + } + return nil, false +} diff --git a/common/types/native_test.go b/common/types/native_test.go new file mode 100644 index 000000000..161337538 --- /dev/null +++ b/common/types/native_test.go @@ -0,0 +1,1615 @@ +// Copyright 2022 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 types_test + +import ( + "errors" + "fmt" + "reflect" + "sort" + "strings" + "testing" + "time" + + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/pb" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/ext" + "github.com/google/cel-go/test" + + structpb "google.golang.org/protobuf/types/known/structpb" + + proto3pb "github.com/google/cel-go/test/proto3pb" +) + +func TestNativeTypes(t *testing.T) { + var nativeTests = []struct { + expr string + out any + in any + envOpts []any + }{ + { + expr: `types_test.TestAllTypes{ + NestedVal: types_test.TestNestedType{NestedMapVal: {1: false}}, + BoolVal: true, + BytesVal: b'hello', + DurationVal: duration('5s'), + DoubleVal: 1.5, + FloatVal: 2.5, + Int32Val: 10, + Int64Val: 20, + StringVal: 'hello world', + TimestampVal: timestamp('2011-08-06T01:23:45Z'), + Uint32Val: 100u, + Uint64Val: 200u, + ListVal: [ + types_test.TestNestedType{ + NestedListVal:['goodbye', 'cruel', 'world'], + NestedMapVal: {42: true}, + custom_name: 'name', + }, + ], + ArrayVal: [ + types_test.TestNestedType{ + NestedListVal:['goodbye', 'cruel', 'world'], + NestedMapVal: {42: true}, + custom_name: 'name', + }, + ], + MapVal: {'map-key': types_test.TestAllTypes{BoolVal: true}}, + CustomSliceVal: [types_test.TestNestedSliceType{Value: 'none'}], + CustomMapVal: {'even': types_test.TestMapVal{Value: 'more'}}, + custom_name: 'name', + }`, + out: &TestAllTypes{ + NestedVal: &TestNestedType{NestedMapVal: map[int64]bool{1: false}}, + BoolVal: true, + BytesVal: []byte("hello"), + DurationVal: time.Second * 5, + DoubleVal: 1.5, + FloatVal: 2.5, + Int32Val: 10, + Int64Val: 20, + StringVal: "hello world", + TimestampVal: mustParseTime(t, "2011-08-06T01:23:45Z"), + Uint32Val: uint32(100), + Uint64Val: uint64(200), + ListVal: []*TestNestedType{ + { + NestedListVal: []string{"goodbye", "cruel", "world"}, + NestedMapVal: map[int64]bool{42: true}, + NestedCustomName: "name", + }, + }, + ArrayVal: [1]*TestNestedType{{ + NestedListVal: []string{"goodbye", "cruel", "world"}, + NestedMapVal: map[int64]bool{42: true}, + NestedCustomName: "name", + }}, + MapVal: map[string]TestAllTypes{"map-key": {BoolVal: true}}, + CustomSliceVal: []TestNestedSliceType{{Value: "none"}}, + CustomMapVal: map[string]TestMapVal{"even": {Value: "more"}}, + CustomName: "name", + }, + envOpts: []any{types.ParseStructTags(true)}, + }, + + { + expr: `types_test.TestAllTypes{ + nestedVal: types_test.TestNestedType{NestedMapVal: {1: false}}, + boolVal: true, + BytesVal: b'hello', + DurationVal: duration('5s'), + DoubleVal: 1.5, + FloatVal: 2.5, + Int32Val: 10, + Int64Val: 20, + StringVal: 'hello world', + TimestampVal: timestamp('2011-08-06T01:23:45Z'), + Uint32Val: 100u, + Uint64Val: 200u, + ListVal: [ + types_test.TestNestedType{ + NestedListVal:['goodbye', 'cruel', 'world'], + NestedMapVal: {42: true}, + custom_name: 'name', + }, + ], + ArrayVal: [ + types_test.TestNestedType{ + NestedListVal:['goodbye', 'cruel', 'world'], + NestedMapVal: {42: true}, + custom_name: 'name', + }, + ], + MapVal: {'map-key': types_test.TestAllTypes{boolVal: true}}, + CustomSliceVal: [types_test.TestNestedSliceType{Value: 'none'}], + CustomMapVal: {'even': types_test.TestMapVal{Value: 'more'}}, + CustomName: 'name', + }`, + out: &TestAllTypes{ + NestedVal: &TestNestedType{NestedMapVal: map[int64]bool{1: false}}, + BoolVal: true, + BytesVal: []byte("hello"), + DurationVal: time.Second * 5, + DoubleVal: 1.5, + FloatVal: 2.5, + Int32Val: 10, + Int64Val: 20, + StringVal: "hello world", + TimestampVal: mustParseTime(t, "2011-08-06T01:23:45Z"), + Uint32Val: uint32(100), + Uint64Val: uint64(200), + ListVal: []*TestNestedType{ + { + NestedListVal: []string{"goodbye", "cruel", "world"}, + NestedMapVal: map[int64]bool{42: true}, + NestedCustomName: "name", + }, + }, + ArrayVal: [1]*TestNestedType{{ + NestedListVal: []string{"goodbye", "cruel", "world"}, + NestedMapVal: map[int64]bool{42: true}, + NestedCustomName: "name", + }}, + MapVal: map[string]TestAllTypes{"map-key": {BoolVal: true}}, + CustomSliceVal: []TestNestedSliceType{{Value: "none"}}, + CustomMapVal: map[string]TestMapVal{"even": {Value: "more"}}, + CustomName: "name", + }, + envOpts: []any{types.ParseStructTag("json")}, + }, + { + expr: `types_test.TestAllTypes{ + NestedVal: types_test.TestNestedType{NestedMapVal: {1: false}}, + BoolVal: true, + BytesVal: b'hello', + DurationVal: duration('5s'), + DoubleVal: 1.5, + FloatVal: 2.5, + Int32Val: 10, + Int64Val: 20, + StringVal: 'hello world', + TimestampVal: timestamp('2011-08-06T01:23:45Z'), + Uint32Val: 100u, + Uint64Val: 200u, + ListVal: [ + types_test.TestNestedType{ + NestedListVal:['goodbye', 'cruel', 'world'], + NestedMapVal: {42: true}, + NestedCustomName: 'name', + }, + ], + ArrayVal: [ + types_test.TestNestedType{ + NestedListVal:['goodbye', 'cruel', 'world'], + NestedMapVal: {42: true}, + NestedCustomName: 'name', + }, + ], + MapVal: {'map-key': types_test.TestAllTypes{BoolVal: true}}, + CustomSliceVal: [types_test.TestNestedSliceType{Value: 'none'}], + CustomMapVal: {'even': types_test.TestMapVal{Value: 'more'}}, + CustomName: 'name', + }`, + out: &TestAllTypes{ + NestedVal: &TestNestedType{NestedMapVal: map[int64]bool{1: false}}, + BoolVal: true, + BytesVal: []byte("hello"), + DurationVal: time.Second * 5, + DoubleVal: 1.5, + FloatVal: 2.5, + Int32Val: 10, + Int64Val: 20, + StringVal: "hello world", + TimestampVal: mustParseTime(t, "2011-08-06T01:23:45Z"), + Uint32Val: uint32(100), + Uint64Val: uint64(200), + ListVal: []*TestNestedType{ + { + NestedListVal: []string{"goodbye", "cruel", "world"}, + NestedMapVal: map[int64]bool{42: true}, + NestedCustomName: "name", + }, + }, + ArrayVal: [1]*TestNestedType{{ + NestedListVal: []string{"goodbye", "cruel", "world"}, + NestedMapVal: map[int64]bool{42: true}, + NestedCustomName: "name", + }}, + MapVal: map[string]TestAllTypes{"map-key": {BoolVal: true}}, + CustomSliceVal: []TestNestedSliceType{{Value: "none"}}, + CustomMapVal: map[string]TestMapVal{"even": {Value: "more"}}, + CustomName: "name", + }, + }, + { + expr: `types_test.TestAllTypes{ + PbVal: test.TestAllTypes{single_int32: 123} + }.PbVal`, + out: &proto3pb.TestAllTypes{SingleInt32: 123}, + }, + { + expr: `types_test.TestAllTypes{PbVal: test.TestAllTypes{}} == + types_test.TestAllTypes{PbVal: test.TestAllTypes{single_bool: false}}`, + }, + {expr: `types_test.TestNestedType{} == TestNestedType{}`}, + {expr: `types_test.TestAllTypes{}.BoolVal != true`}, + {expr: `!has(types_test.TestAllTypes{}.BoolVal) && !has(types_test.TestAllTypes{}.NestedVal)`}, + {expr: `type(types_test.TestAllTypes) == type`}, + {expr: `type(types_test.TestAllTypes{}) == types_test.TestAllTypes`}, + {expr: `type(types_test.TestAllTypes{}) == types_test.TestAllTypes`}, + {expr: `types_test.TestAllTypes != test.TestAllTypes`}, + {expr: `types_test.TestAllTypes{BoolVal: true} != dyn(test.TestAllTypes{single_bool: true})`}, + {expr: `types_test.TestAllTypes{}.NestedVal == types_test.TestNestedType{}`}, + {expr: `types_test.TestNestedType{} == types_test.TestAllTypes{}.NestedStructVal`}, + {expr: `types_test.TestAllTypes{}.NestedStructVal == types_test.TestNestedType{}`}, + {expr: `types_test.TestAllTypes{}.ListVal.size() == 0`}, + {expr: `types_test.TestAllTypes{}.MapVal.size() == 0`}, + {expr: `types_test.TestAllTypes{}.TimestampVal == timestamp(0)`}, + {expr: `test.TestAllTypes{}.single_timestamp == timestamp(0)`}, + {expr: `[TestAllTypes{BoolVal: true}, TestAllTypes{BoolVal: false}].exists(t, t.BoolVal == true)`}, + {expr: `[TestAllTypes{CustomName: 'Alice'}, TestAllTypes{CustomName: 'Bob'}].exists(t, t.CustomName == 'Alice')`}, + {expr: `[TestAllTypes{custom_name: 'Alice'}, TestAllTypes{custom_name: 'Bob'}].exists(t, t.custom_name == 'Alice')`, envOpts: []any{types.ParseStructTags(true)}}, + {expr: `TestAllTypes{BytesArrayVal: b'1234'}.BytesArrayVal != b'123'`}, + {expr: `TestAllTypes{BytesArrayVal: b'1234'}.BytesArrayVal == b'1234'`}, + { + expr: `tests.all(t, t.Int32Val > 17)`, + in: map[string]any{ + "tests": []*TestAllTypes{{Int32Val: 18}, {Int32Val: 19}, {Int32Val: 20}}, + }, + }, + } + for i, tst := range nativeTests { + tc := tst + t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { + env := testNativeEnv(t, tc.envOpts...) + var asts []*cel.Ast + pAst, iss := env.Parse(tc.expr) + if iss.Err() != nil { + t.Fatalf("env.Parse(%v) failed: %v", tc.expr, iss.Err()) + } + asts = append(asts, pAst) + cAst, iss := env.Check(pAst) + if iss.Err() != nil { + t.Fatalf("env.Check(%v) failed: %v", tc.expr, iss.Err()) + } + asts = append(asts, cAst) + for _, ast := range asts { + prg, err := env.Program(ast) + if err != nil { + t.Fatal(err) + } + in := tc.in + if in == nil { + in = cel.NoVars() + } + out, _, err := prg.Eval(in) + if err != nil { + t.Fatal(err) + } + want := tc.out + if want == nil { + want = true + } + wantPB, isPB := want.(proto.Message) + if isPB && !pb.Equal(wantPB, out.Value().(proto.Message)) { + t.Errorf("got %v, wanted %v for expr: %s", out.Value(), want, tc.expr) + } + if !isPB && !reflect.DeepEqual(out.Value(), want) { + t.Errorf("got %v, wanted %v for expr: %s", out.Value(), want, tc.expr) + } + } + }) + } +} + +func TestNativeFindStructFieldNames(t *testing.T) { + env := testNativeEnv(t, types.ParseStructTags(true)) + provider := env.CELTypeProvider() + tests := []struct { + typeName string + fields []string + }{ + { + typeName: "types_test.TestNestedType", + fields: []string{"NestedListVal", "NestedMapVal", "custom_name"}, + }, + { + typeName: "google.expr.proto3.test.TestAllTypes.NestedMessage", + fields: []string{"bb"}, + }, + { + typeName: "invalid.TypeName", + fields: []string{}, + }, + } + + for _, tst := range tests { + tc := tst + t.Run(fmt.Sprintf("%s", tc.typeName), func(t *testing.T) { + fields, _ := provider.FindStructFieldNames(tc.typeName) + sort.Strings(fields) + sort.Strings(tc.fields) + if !reflect.DeepEqual(fields, tc.fields) { + t.Errorf("got %v, wanted %v", fields, tc.fields) + } + }) + } +} + +func TestNativeTypesStaticErrors(t *testing.T) { + var nativeTests = []struct { + expr string + err string + }{ + { + expr: `TestAllTypos{}`, + err: `ERROR: :1:13: undeclared reference to 'TestAllTypos' (in container 'types_test') + | TestAllTypos{} + | ............^`, + }, + { + expr: `types_test.TestAllTypes{bool_val: false}`, + err: `ERROR: :1:33: undefined field 'bool_val' + | types_test.TestAllTypes{bool_val: false} + | ................................^`, + }, + { + expr: `types_test.TestAllTypes{UnsupportedVal: null}`, + err: `ERROR: :1:39: undefined field 'UnsupportedVal' + | types_test.TestAllTypes{UnsupportedVal: null} + | ......................................^`, + }, + { + expr: `types_test.TestAllTypes{UnsupportedListVal: null}`, + err: `ERROR: :1:43: undefined field 'UnsupportedListVal' + | types_test.TestAllTypes{UnsupportedListVal: null} + | ..........................................^`, + }, + { + expr: `types_test.TestAllTypes{UnsupportedMapVal: null}`, + err: `ERROR: :1:42: undefined field 'UnsupportedMapVal' + | types_test.TestAllTypes{UnsupportedMapVal: null} + | .........................................^`, + }, + } + env := testNativeEnv(t) + for i, tst := range nativeTests { + tc := tst + t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { + _, iss := env.Compile(tc.expr) + if iss.Err() == nil { + t.Fatalf("env.Compile(%v) succeeded, wanted error", tc.expr) + } + if !test.Compare(iss.Err().Error(), tc.err) { + t.Errorf("env.Compile(%v) got %v, wanted error %s", tc.expr, iss.Err(), tc.err) + } + }) + } +} + +func TestNativeTypesJsonSerialization(t *testing.T) { + tests := []struct { + expr string + out string + additionalEnvOptions []any + }{ + { + expr: `[b'string']`, + out: `["c3RyaW5n"]`, + }, + { + expr: `TestAllTypes{ + BoolVal: true, + DurationVal: duration('5s'), + DoubleVal: 1.5, + FloatVal: 2.0, + Int32Val: 23, + Int64Val: 64, + MapVal: { + 'map-key': types_test.TestAllTypes{ + BoolVal: true + } + }, + NestedVal: TestNestedType{ + NestedListVal: ["first", "second"], + }, + StringVal: "string", + CustomName: "name", + }`, + out: `{ + "BoolVal": true, + "CustomName": "name", + "DoubleVal": 1.5, + "DurationVal": "5s", + "FloatVal": 2, + "Int32Val": 23, + "Int64Val": 64, + "MapVal": { + "map-key": { + "BoolVal": true + } + }, + "NestedVal": { + "NestedListVal": [ + "first", + "second" + ] + }, + "StringVal": "string" + }`, + }, + { + expr: `TestAllTypes{ + BoolVal: true, + DurationVal: duration('5s'), + DoubleVal: 1.5, + FloatVal: 2.0, + Int32Val: 23, + Int64Val: 64, + MapVal: { + 'map-key': types_test.TestAllTypes{ + BoolVal: true + } + }, + NestedVal: TestNestedType{ + NestedListVal: ["first", "second"], + }, + StringVal: "string", + custom_name: "name", + }`, + out: `{ + "BoolVal": true, + "DoubleVal": 1.5, + "DurationVal": "5s", + "FloatVal": 2, + "Int32Val": 23, + "Int64Val": 64, + "MapVal": { + "map-key": { + "BoolVal": true + } + }, + "NestedVal": { + "NestedListVal": [ + "first", + "second" + ] + }, + "StringVal": "string", + "custom_name": "name" + }`, + additionalEnvOptions: []any{types.ParseStructTags(true)}, + }, + } + for i, tst := range tests { + tc := tst + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + env := testNativeEnv(t, tst.additionalEnvOptions...) + ast, iss := env.Compile(tc.expr) + if iss.Err() != nil { + t.Fatalf("env.Compile(%v) failed: %v", tc.expr, iss.Err()) + } + prg, err := env.Program(ast) + if err != nil { + t.Fatalf("env.Program() failed: %v", err) + } + out, _, err := prg.Eval(cel.NoVars()) + if err != nil { + t.Fatalf("prg.Eval() failed: %v", err) + } + conv, err := out.ConvertToNative(reflect.TypeOf(&structpb.Value{})) + if err != nil { + t.Fatalf("out.ConvertToNative(Value) failed: %v", err) + } + json := protojson.Format(conv.(proto.Message)) + if !test.Compare(json, tc.out) { + t.Errorf("expr %v converted to %v, wanted %v", tc.expr, json, tc.out) + } + }) + } +} + +func TestNativeTypesRuntimeErrors(t *testing.T) { + var nativeTests = []struct { + expr string + err string + }{ + { + expr: `TestAllTypos{}`, + err: `unknown type: TestAllTypos`, + }, + { + expr: `types_test.TestAllTypes{bool_val: false}`, + err: `no such field: bool_val`, + }, + { + expr: `types_test.TestAllTypes{UnsupportedVal: null}`, + err: `no such field: UnsupportedVal`, + }, + { + expr: `types_test.TestAllTypes{UnsupportedListVal: null}`, + err: `no such field: UnsupportedListVal`, + }, + { + expr: `types_test.TestAllTypes{UnsupportedMapVal: null}`, + err: `no such field: UnsupportedMapVal`, + }, + { + expr: `types_test.TestAllTypes{privateVal: null}`, + err: `no such field: privateVal`, + }, + { + expr: `types_test.TestAllTypes{}.UnsupportedMapVal`, + err: `no such field: UnsupportedMapVal`, + }, + { + expr: `types_test.TestAllTypes{}.privateVal`, + err: `no such field: privateVal`, + }, + { + expr: `types_test.TestAllTypes{BoolVal: 'false'}`, + err: `unsupported native conversion from string to 'bool'`, + }, + { + expr: `has(types_test.TestAllTypes{}.BadFieldName)`, + err: `no such field: BadFieldName`, + }, + { + expr: `types_test.TestAllTypes{}[42]`, + err: `no such overload`, + }, + { + expr: `types_test.TestAllTypes{Int32Val: 9223372036854775807}`, + err: `integer overflow`, + }, + { + expr: `types_test.TestAllTypes{Uint32Val: 9223372036854775807u}`, + err: `unsigned integer overflow`, + }, + } + env := testNativeEnv(t) + for i, tst := range nativeTests { + tc := tst + t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { + ast, iss := env.Parse(tc.expr) + if iss.Err() != nil { + t.Fatalf("env.Parse(%v) failed: %v", tc.expr, iss.Err()) + } + prg, err := env.Program(ast) + if err != nil { + if !strings.Contains(err.Error(), tc.err) { + t.Fatal(err) + } + return + } + out, _, err := prg.Eval(cel.NoVars()) + if err == nil || !strings.Contains(err.Error(), tc.err) { + var got any = err + if err == nil { + got = out + } + t.Fatalf("prg.Eval() got %v, wanted error %v", got, tc.err) + } + }) + } +} + +func TestNativeTypesErrors(t *testing.T) { + envTests := []struct { + nativeType any + err string + }{ + { + nativeType: reflect.TypeOf(1), + err: "unsupported reflect.Type", + }, + { + nativeType: reflect.ValueOf(1), + err: "unsupported reflect.Type", + }, + { + nativeType: 1, + err: "must be reflect.Type or reflect.Value", + }, + } + for i, tst := range envTests { + tc := tst + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + _, err := cel.NewEnv(ext.NativeTypes(tc.nativeType)) + if err == nil || !strings.Contains(err.Error(), tc.err) { + t.Errorf("cel.NewEnv(NativeTypes(%v)) got error %v, wanted %v", tc.nativeType, err, tc.err) + } + }) + } +} + +func TestNativeTypesConvertToNative(t *testing.T) { + env := testNativeEnv(t, ext.NativeTypes(reflect.TypeOf(TestNestedType{}))) + adapter := env.CELTypeAdapter() + conversions := []struct { + in any + inType *cel.Type + out any + err string + }{ + { + in: &TestAllTypes{BoolVal: true}, + inType: cel.ObjectType("types_test.TestAllTypes"), + out: &TestAllTypes{BoolVal: true}, + }, + { + in: TestAllTypes{BoolVal: true}, + inType: cel.ObjectType("types_test.TestAllTypes"), + out: &TestAllTypes{BoolVal: true}, + }, + { + in: &TestAllTypes{BoolVal: true}, + inType: cel.ObjectType("types_test.TestAllTypes"), + out: TestAllTypes{BoolVal: true}, + }, + { + in: nil, + inType: cel.NullType, + out: types.NullValue, + }, + { + in: &TestAllTypes{BoolVal: true}, + inType: cel.ObjectType("types_test.TestAllTypes"), + out: &proto3pb.TestAllTypes{}, + err: "type conversion error", + }, + { + in: [3]int32{1, 2, 3}, + inType: cel.ListType(cel.IntType), + out: []int32{1, 2, 3}, + }, + { + in: &[3]byte{1, 2, 3}, + inType: cel.BytesType, + out: []byte{1, 2, 3}, + }, + { + in: [3]byte{1, 2, 3}, + inType: cel.BytesType, + out: []byte{1, 2, 3}, + }, + } + for _, c := range conversions { + inVal := adapter.NativeToValue(c.in) + if types.IsError(inVal) { + t.Fatalf("adapter.NativeToValue(%v) failed: %v", c.in, inVal) + } + if inVal.Type().TypeName() != c.inType.TypeName() { + t.Fatalf("adapter.NativeToValue() got type %v, wanted type %v", inVal.Type(), c.inType) + } + out, err := inVal.ConvertToNative(reflect.TypeOf(c.out)) + if err != nil { + if c.err != "" { + if !strings.Contains(err.Error(), c.err) { + t.Fatalf("%v.ConvertToNative(%T) got %v, wanted error %v", c.in, c.out, err, c.err) + } + return + } + t.Fatalf("%v.ConvertToNative(%T) failed: %v", c.in, c.out, err) + } + if !reflect.DeepEqual(out, c.out) { + t.Errorf("%v.ConvertToNative(%T) got %v, wanted %v", c.in, c.out, out, c.out) + } + } +} + +func TestConvertToTypeErrors(t *testing.T) { + env := testNativeEnv(t, ext.NativeTypes(reflect.TypeOf(TestNestedType{}))) + adapter := env.CELTypeAdapter() + conversions := []struct { + in any + out any + err string + }{ + { + in: &TestAllTypes{BoolVal: true}, + out: &TestAllTypes{BoolVal: true}, + }, + { + in: TestAllTypes{BoolVal: true}, + out: &TestAllTypes{BoolVal: true}, + }, + { + in: &TestAllTypes{BoolVal: true}, + out: TestAllTypes{BoolVal: true}, + }, + { + in: &TestAllTypes{BoolVal: true}, + out: &proto3pb.TestAllTypes{}, + err: "type conversion error", + }, + } + for _, c := range conversions { + inVal := adapter.NativeToValue(c.in) + outVal := adapter.NativeToValue(c.out) + if types.IsError(inVal) { + t.Fatalf("adapter.NativeToValue(%v) failed: %v", c.in, inVal) + } + if types.IsError(outVal) { + t.Fatalf("adapter.NativeToValue(%v) failed: %v", c.out, outVal) + } + conv := inVal.ConvertToType(outVal.Type()) + if c.err != "" { + if !types.IsError(conv) { + t.Fatalf("%v.ConvertToType(%v) got %v, wanted error %v", c.in, outVal.Type(), conv, c.err) + } + convErr := conv.(*types.Err) + if !strings.Contains(convErr.Error(), c.err) { + t.Fatalf("%v.ConvertToType(%v) got %v, wanted error %v", c.in, outVal.Type(), conv, c.err) + } + return + } + if conv != inVal { + t.Errorf("%v.ConvertToType(%v) got %v, wanted %v", c.in, outVal.Type(), conv, c.err) + } + conv = inVal.ConvertToType(types.TypeType) + if conv.Type() != types.TypeType || conv.(ref.Type) != inVal.Type() { + t.Errorf("%v.ConvertToType(Type) got %v, wanted %v", inVal, conv, inVal.Type()) + } + } +} + +func TestNativeTypesWithOptional(t *testing.T) { + var nativeTests = []struct { + expr string + }{ + {expr: `!optional.ofNonZeroValue(types_test.TestAllTypes{}).hasValue()`}, + {expr: `!types_test.TestAllTypes{}.?BoolVal.orValue(false)`}, + {expr: `!types_test.TestAllTypes{}.?BoolVal.hasValue()`}, + {expr: `!types_test.TestAllTypes{BoolVal: false}.?BoolVal.hasValue()`}, + {expr: `types_test.TestAllTypes{BoolVal: true}.?BoolVal.hasValue()`}, + {expr: `types_test.TestAllTypes{}.NestedVal.?NestedMapVal.orValue({}).size() == 0`}, + } + env := testNativeEnv(t, cel.OptionalTypes()) + for i, tst := range nativeTests { + tc := tst + t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { + var asts []*cel.Ast + pAst, iss := env.Parse(tc.expr) + if iss.Err() != nil { + t.Fatalf("env.Parse(%v) failed: %v", tc.expr, iss.Err()) + } + asts = append(asts, pAst) + cAst, iss := env.Check(pAst) + if iss.Err() != nil { + t.Fatalf("env.Check(%v) failed: %v", tc.expr, iss.Err()) + } + asts = append(asts, cAst) + for _, ast := range asts { + prg, err := env.Program(ast) + if err != nil { + t.Fatal(err) + } + out, _, err := prg.Eval(cel.NoVars()) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(out.Value(), true) { + t.Errorf("got %v, wanted true for expr: %s", out.Value(), tc.expr) + } + } + }) + } +} + +func TestNativeTypesWithCELTypedFields(t *testing.T) { + var nativeTests = []struct { + expr string + }{ + { + expr: `types_test.TestRefValFieldType{optional_name: optional.of('my name')}.optional_name.orValue('') == 'my name'`, + }, + { + expr: `types_test.TestRefValFieldType{IntVal: 2}.IntVal >= 1`, + }, + { + expr: `types_test.TestRefValFieldType{time: timestamp('2001-01-01T00:00:00Z')}.time > timestamp('1970-01-01T00:00:00Z')`, + }, + } + env := testNativeEnv(t, cel.OptionalTypes(), types.ParseStructTag("cel")) + for i, tst := range nativeTests { + tc := tst + t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { + var asts []*cel.Ast + pAst, iss := env.Parse(tc.expr) + if iss.Err() != nil { + t.Fatalf("env.Parse(%v) failed: %v", tc.expr, iss.Err()) + } + asts = append(asts, pAst) + cAst, iss := env.Check(pAst) + if iss.Err() != nil { + t.Fatalf("env.Check(%v) failed: %v", tc.expr, iss.Err()) + } + asts = append(asts, cAst) + for _, ast := range asts { + prg, err := env.Program(ast) + if err != nil { + t.Fatal(err) + } + out, _, err := prg.Eval(cel.NoVars()) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(out.Value(), true) { + t.Errorf("got %v, wanted true for expr: %s", out.Value(), tc.expr) + } + } + }) + } +} + +func TestNativeTypeConvertToType(t *testing.T) { + var nativeTests = []struct { + tag string + }{ + {tag: "cel"}, + {tag: "json"}, + } + + for i, tst := range nativeTests { + tc := tst + t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { + handler := func(f reflect.StructField) string { + tag, found := f.Tag.Lookup(tc.tag) + if found { + splits := strings.Split(tag, ",") + if len(splits) > 0 { + return splits[0] + } + } + return f.Name + } + nt, err := types.NewNativeType(reflect.TypeFor[*TestAllTypes](), types.ParseStructField(handler)) + if err != nil { + t.Fatalf("NewNativeType() failed: %v", err) + } + if nt.ConvertToType(types.TypeType) != types.TypeType { + t.Error("ConvertToType(Type) failed") + } + if !types.IsError(nt.ConvertToType(types.StringType)) { + t.Errorf("ConvertToType(String) got %v, wanted error", nt.ConvertToType(types.StringType)) + } + }) + } +} + +func TestNativeTypeConvertToNative(t *testing.T) { + nt, err := types.NewNativeType(reflect.TypeFor[*TestAllTypes]()) + if err != nil { + t.Fatalf("NewNativeType() failed: %v", err) + } + out, err := nt.ConvertToNative(reflect.TypeOf(1)) + if err == nil { + t.Errorf("nt.ConvertToNative(1) produced %v, wanted error", out) + } +} + +func TestNativeTypeHasTrait(t *testing.T) { + nt, err := types.NewNativeType(reflect.TypeFor[*TestAllTypes]()) + if err != nil { + t.Fatalf("NewNativeType() failed: %v", err) + } + if !nt.HasTrait(traits.IndexerType) || !nt.HasTrait(traits.FieldTesterType) { + t.Error("nt.HasTrait() failed indicate support for presence test and field access.") + } +} + +func TestNativeTypeValue(t *testing.T) { + nt, err := types.NewNativeType(reflect.TypeFor[*TestAllTypes]()) + if err != nil { + t.Fatalf("NewNativeType() failed: %v", err) + } + if nt.Value() != nt.String() { + t.Errorf("nt.Value() got %v, wanted %v", nt.Value(), nt.String()) + } +} + +func TestNativeStructWithMultipleSameFieldNames(t *testing.T) { + tagHandler := func(f reflect.StructField) string { + tag, found := f.Tag.Lookup("cel") + if found { + splits := strings.Split(tag, ",") + if len(splits) > 0 { + return splits[0] + } + } + return f.Name + } + _, err := types.NewNativeType( + reflect.TypeFor[TestStructWithMultipleSameNames](), + types.ParseStructField(tagHandler), + ) + if err == nil { + t.Fatal("NewNativeType() did not fail as expected") + } + if !strings.Contains(err.Error(), "field name already exists") { + t.Fatalf("NewNativeType() expected duplicated field name error, but got: %v", err) + } +} + +func TestNativeStructEmbedded(t *testing.T) { + var nativeTests = []struct { + expr string + in any + out any + }{ + { + expr: `test.embedded.custom_name == "name"`, + in: map[string]any{ + "test": &TestEmbeddedTypes{ + TestNestedType: TestNestedType{NestedCustomName: "name"}, + Skipped: "should-be-hidden", + }, + }, + out: true, + }, + { + expr: `dyn(test.embedded)["-"] == "error"`, + in: map[string]any{ + "test": &TestEmbeddedTypes{ + TestNestedType: TestNestedType{NestedCustomName: "name"}, + Skipped: "should-be-hidden", + }, + }, + out: errors.New("no such field: -"), + }, + { + expr: `test.embedded == types_test.TestNestedType{custom_name: "name"}`, + in: map[string]any{ + "test": &TestEmbeddedTypes{ + TestNestedType: TestNestedType{NestedCustomName: "name"}, + Skipped: "should-be-hidden", + }, + }, + out: true, + }, + { + expr: `test.Name == "name"`, + in: map[string]any{ + "test": &TestEmbeddedTypes{ + Custom: Custom{Name: "name"}, + }, + }, + out: true, + }, + } + + envOpts := []cel.EnvOption{ + ext.NativeTypes( + reflect.TypeFor[*TestEmbeddedTypes](), + reflect.TypeFor[*TestNestedType](), + types.ParseStructTag("json"), + ), + cel.Variable("test", cel.ObjectType("types_test.TestEmbeddedTypes")), + } + + env, err := cel.NewEnv(envOpts...) + if err != nil { + t.Fatalf("cel.NewEnv(NativeTypes()) failed: %v", err) + } + + for i, tst := range nativeTests { + tc := tst + t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { + var asts []*cel.Ast + pAst, iss := env.Parse(tc.expr) + if iss.Err() != nil { + t.Fatalf("env.Parse(%v) failed: %v", tc.expr, iss.Err()) + } + asts = append(asts, pAst) + cAst, iss := env.Check(pAst) + if iss.Err() != nil { + t.Fatalf("env.Check(%v) failed: %v", tc.expr, iss.Err()) + } + asts = append(asts, cAst) + for _, ast := range asts { + prg, err := env.Program(ast) + if err != nil { + t.Fatal(err) + } + out, _, err := prg.Eval(tc.in) + if err != nil { + if !errors.Is(err, tc.out.(error)) { + t.Fatalf("got %v, wanted %v for expr: %s", err, tc.out, tc.expr) + } + continue + } + if !reflect.DeepEqual(out.Value(), tc.out) { + t.Errorf("got %v, wanted %v for expr: %s", out.Value(), tc.out, tc.expr) + } + } + }) + } +} + +func TestNativeStructEmbeddedPointer(t *testing.T) { + nativeTests := []struct { + expr string + in map[string]any + out any + }{ + { + expr: `!has(test.custom_name) && test.custom_name == ""`, + in: map[string]any{ + "test": &TestEmbeddedPointerTypes{ + TestNestedType: nil, + }, + }, + out: true, + }, + { + expr: `has(test.custom_name) && test.custom_name == "name"`, + in: map[string]any{ + "test": &TestEmbeddedPointerTypes{ + TestNestedType: &TestNestedType{NestedCustomName: "name"}, + }, + }, + out: true, + }, + { + expr: `types_test.TestEmbeddedPointerTypes{custom_name: "name"}.custom_name == "name"`, + in: nil, + out: true, + }, + } + + envOpts := []cel.EnvOption{ + ext.NativeTypes( + reflect.TypeFor[*TestEmbeddedPointerTypes](), + reflect.TypeFor[*TestNestedType](), + types.ParseStructTag("json"), + ), + cel.Variable("test", cel.ObjectType("types_test.TestEmbeddedPointerTypes")), + } + + env, err := cel.NewEnv(envOpts...) + if err != nil { + t.Fatalf("cel.NewEnv(NativeTypes()) failed: %v", err) + } + + for i, tst := range nativeTests { + tc := tst + t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { + pAst, iss := env.Parse(tc.expr) + if iss.Err() != nil { + t.Fatalf("env.Parse(%v) failed: %v", tc.expr, iss.Err()) + } + cAst, iss := env.Check(pAst) + if iss.Err() != nil { + t.Fatalf("env.Check(%v) failed: %v", tc.expr, iss.Err()) + } + for _, ast := range []*cel.Ast{pAst, cAst} { + prg, err := env.Program(ast) + if err != nil { + t.Fatal(err) + } + out, _, err := prg.Eval(tc.in) + if err != nil { + t.Fatalf("prg.Eval() failed: %v", err) + } + if !reflect.DeepEqual(out.Value(), tc.out) { + t.Errorf("got %v, wanted %v for expr: %s", out.Value(), tc.out, tc.expr) + } + } + }) + } +} + +func TestNativeStructHiddenField(t *testing.T) { + envOpts := []cel.EnvOption{ + ext.NativeTypes( + reflect.TypeFor[*TestEmbeddedTypes](), + types.ParseStructTag("json"), + ), + cel.Variable("test", cel.ObjectType("types_test.TestEmbeddedTypes")), + } + + env, err := cel.NewEnv(envOpts...) + if err != nil { + t.Fatalf("cel.NewEnv(NativeTypes()) failed: %v", err) + } + + // 1. Static reference compilation failure case + // Attempting to compile `test.Password` should fail static analysis because the field is skipped/hidden. + _, iss := env.Compile("test.Password") + if iss.Err() == nil { + t.Error("env.Compile('test.Password') succeeded, expected a compilation/check error") + } + + // 2. Dynamic reference runtime evaluation failure case + // Using dyn(test).Password should compile successfully (since dyn disables static type checks), + // but it must fail at runtime during evaluation because the field is not exposed. + ast, iss := env.Compile("dyn(test).Password") + if iss.Err() != nil { + t.Fatalf("env.Compile('dyn(test).Password') failed: %v", iss.Err()) + } + prg, err := env.Program(ast) + if err != nil { + t.Fatalf("env.Program() failed: %v", err) + } + in := map[string]any{ + "test": &TestEmbeddedTypes{ + Skipped: "sensitive_password", + }, + } + out, _, err := prg.Eval(in) + if err == nil { + t.Errorf("prg.Eval() succeeded and returned %v, expected runtime error accessing hidden field", out) + } +} + +type TestNestedStruct struct { + ListVal []*TestNestedType +} + +func TestNativeNestedStruct(t *testing.T) { + var nativeTests = []struct { + expr string + in any + }{ + { + expr: `test.ListVal.exists(x, x.custom_name == "name")`, + in: map[string]any{ + "test": &TestNestedStruct{ListVal: []*TestNestedType{{NestedCustomName: "name"}}}, + }, + }, + } + + envOpts := []cel.EnvOption{ + ext.NativeTypes( + reflect.ValueOf(&TestNestedStruct{}), + types.ParseStructTag("json"), + ), + cel.Variable("test", cel.ObjectType("types_test.TestNestedStruct")), + } + + env, err := cel.NewEnv(envOpts...) + if err != nil { + t.Fatalf("cel.NewEnv(NativeTypes()) failed: %v", err) + } + + for i, tst := range nativeTests { + tc := tst + t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { + var asts []*cel.Ast + pAst, iss := env.Parse(tc.expr) + if iss.Err() != nil { + t.Fatalf("env.Parse(%v) failed: %v", tc.expr, iss.Err()) + } + asts = append(asts, pAst) + cAst, iss := env.Check(pAst) + if iss.Err() != nil { + t.Fatalf("env.Check(%v) failed: %v", tc.expr, iss.Err()) + } + asts = append(asts, cAst) + for _, ast := range asts { + prg, err := env.Program(ast) + if err != nil { + t.Fatal(err) + } + out, _, err := prg.Eval(tc.in) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(out.Value(), true) { + t.Errorf("got %v, wanted true for expr: %s", out.Value(), tc.expr) + } + } + }) + } +} + +func TestNativeTypesVersion(t *testing.T) { + _, err := cel.NewEnv(ext.NativeTypes(ext.NativeTypesVersion(0))) + if err != nil { + t.Fatalf("NewEnv(NativeTypes(NativeTypesVersion(0))) failed: %v", err) + } +} + +func TestTypeResolutionRace(t *testing.T) { + customType := reflect.TypeFor[*Custom]() + env, err := cel.NewEnv( + cel.Container("types_test"), + ext.NativeTypes( + types.ParseStructTag("cel"), + customType, + ), + ) + if err != nil { + t.Fatal("NewEnv:", err) + } + + tests := []struct { + name string + expr string + }{ + {name: "custom1", expr: `Custom{ name: "name1" }`}, + {name: "custom2", expr: `Custom{ name: "name2" }`}, + {name: "custom3", expr: `Custom{ name: "name3" }`}, + {name: "custom4", expr: `Custom{ name: "name4" }`}, + {name: "custom5", expr: `Custom{ name: "name5" }`}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + ast, iss := env.Compile(test.expr) + if err := iss.Err(); err != nil { + t.Fatal("Compile:", err) + } + prg, err := env.Program(ast) + if err != nil { + t.Fatalf("env.Program() failed: %s", err) + } + prg.Eval(cel.NoVars()) + }) + } +} + +func TestNativeToValueDelegatesUnregisteredStructs(t *testing.T) { + custom := &recordingAdapter{base: types.DefaultTypeAdapter} + env, err := cel.NewEnv( + cel.CustomTypeAdapter(custom), + ext.NativeTypes(reflect.TypeOf(registeredNativeStruct{})), + ) + if err != nil { + t.Fatalf("cel.NewEnv() failed: %v", err) + } + adapter := env.CELTypeAdapter() + + // An unregistered struct must reach the composed base adapter. + got := adapter.NativeToValue(unregisteredNativeStruct{Name: "x"}) + if !custom.saw { + t.Error("base adapter was not consulted for an unregistered struct") + } + if got.Equal(types.String("from-base-adapter")) != types.True { + t.Errorf("NativeToValue(unregisteredNativeStruct) = %v, want the base adapter's value", got) + } + + // A registered native type must still be wrapped as a native object. + custom.saw = false + gotReg := adapter.NativeToValue(registeredNativeStruct{Name: "y"}) + if custom.saw { + t.Error("base adapter was consulted for a registered native type") + } + if tn := gotReg.Type().TypeName(); !strings.Contains(tn, "registeredNativeStruct") { + t.Errorf("NativeToValue(registeredNativeStruct).Type() = %q, want a native object type", tn) + } +} + +func BenchmarkNativeTypesEval(b *testing.B) { + benchmarks := []struct { + name string + expr string + in any + envOpts []any + }{ + { + name: "FieldAccess", + expr: "t.Int32Val + t.Int64Val", + in: map[string]any{ + "t": &TestAllTypes{Int32Val: 10, Int64Val: 20}, + }, + }, + { + name: "NestedFieldAccess", + expr: "t.NestedVal.NestedCustomName == 'name'", + in: map[string]any{ + "t": &TestAllTypes{ + NestedVal: &TestNestedType{NestedCustomName: "name"}, + }, + }, + }, + { + name: "StructCreation", + expr: `types_test.TestAllTypes{ + BoolVal: true, + Int32Val: 10, + Int64Val: 20, + StringVal: 'hello world', + }`, + }, + { + name: "FieldPresence", + expr: "has(t.BoolVal) && has(t.NestedVal)", + in: map[string]any{ + "t": &TestAllTypes{ + BoolVal: true, + NestedVal: &TestNestedType{}, + }, + }, + }, + { + name: "StructTagFieldAccess", + expr: "t.custom_name == 'name'", + envOpts: []any{types.ParseStructTags(true)}, + in: map[string]any{ + "t": &TestAllTypes{CustomName: "name"}, + }, + }, + { + name: "ListExists", + expr: "tests.exists(t, t.Int32Val > 15)", + in: map[string]any{ + "tests": []*TestAllTypes{ + {Int32Val: 10}, + {Int32Val: 20}, + }, + }, + }, + } + + for _, bm := range benchmarks { + b.Run(bm.name, func(b *testing.B) { + envOpts := append([]any{ + cel.Variable("t", cel.ObjectType("types_test.TestAllTypes")), + }, bm.envOpts...) + env := testNativeEnv(b, envOpts...) + ast, iss := env.Compile(bm.expr) + if iss.Err() != nil { + b.Fatalf("env.Compile(%q) failed: %v", bm.expr, iss.Err()) + } + prg, err := env.Program(ast, cel.EvalOptions(cel.OptOptimize)) + if err != nil { + b.Fatalf("env.Program() failed: %v", err) + } + input := bm.in + if input == nil { + input = cel.NoVars() + } + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + prg.Eval(input) + } + }) + } +} + +func BenchmarkNativeToValue(b *testing.B) { + env := testNativeEnv(b) + adapter := env.CELTypeAdapter() + + nested := &TestNestedType{ + NestedListVal: []string{"a", "b", "c"}, + NestedMapVal: map[int64]bool{1: true}, + NestedCustomName: "test", + } + allTypes := &TestAllTypes{ + BoolVal: true, + Int32Val: 10, + Int64Val: 20, + StringVal: "hello world", + NestedVal: nested, + ListVal: []*TestNestedType{nested}, + } + allTypesSlice := []*TestAllTypes{allTypes, allTypes} + + benchmarks := []struct { + name string + val any + }{ + {name: "TestNestedType", val: nested}, + {name: "TestAllTypes", val: allTypes}, + {name: "SliceTestAllTypes", val: allTypesSlice}, + } + + for _, bm := range benchmarks { + b.Run(bm.name, func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + adapter.NativeToValue(bm.val) + } + }) + } +} + +func BenchmarkConvertToNative(b *testing.B) { + env := testNativeEnv(b) + adapter := env.CELTypeAdapter() + + allTypes := &TestAllTypes{ + BoolVal: true, + Int32Val: 10, + Int64Val: 20, + StringVal: "hello world", + } + celVal := adapter.NativeToValue(allTypes) + targetType := reflect.TypeOf(&TestAllTypes{}) + + allTypesSlice := []*TestAllTypes{allTypes, allTypes} + celSliceVal := adapter.NativeToValue(allTypesSlice) + sliceTargetType := reflect.TypeOf([]*TestAllTypes{}) + + b.Run("TestAllTypes", func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, err := celVal.ConvertToNative(targetType) + if err != nil { + b.Fatalf("ConvertToNative failed: %v", err) + } + } + }) + + b.Run("SliceTestAllTypes", func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, err := celSliceVal.ConvertToNative(sliceTargetType) + if err != nil { + b.Fatalf("ConvertToNative failed: %v", err) + } + } + }) +} + +// testEnv initializes the test environment common to all tests. +func testNativeEnv(t testing.TB, opts ...any) *cel.Env { + t.Helper() + envOpts := []cel.EnvOption{ + cel.Container("types_test"), + cel.Abbrevs("google.expr.proto3.test"), + cel.Types(&proto3pb.TestAllTypes{}), + cel.Variable("tests", cel.ListType(cel.ObjectType("types_test.TestAllTypes"))), + } + nativeOpts := []any{ + reflect.ValueOf(&TestAllTypes{}), + reflect.ValueOf(&TestRefValFieldType{}), + } + for _, o := range opts { + switch opt := o.(type) { + case types.NativeTypeOption: + nativeOpts = append(nativeOpts, opt) + case cel.EnvOption: + envOpts = append(envOpts, opt) + default: + t.Fatalf("invalid option type: %s", reflect.TypeOf(o).Name()) + } + } + + envOpts = append(envOpts, + ext.NativeTypes( + nativeOpts..., + ), + ) + env, err := cel.NewEnv(envOpts...) + if err != nil { + t.Fatalf("cel.NewEnv(NativeTypes()) failed: %v", err) + } + return env +} + +func mustParseTime(t *testing.T, timestamp string) time.Time { + t.Helper() + out, err := time.Parse(time.RFC3339, timestamp) + if err != nil { + t.Fatalf("time.Parse(%q) failed: %v", timestamp, err) + } + return out +} + +type Custom struct { + Name string `cel:"name"` +} + +type TestStructWithMultipleSameNames struct { + Name string + CustomName string `cel:"Name"` +} + +type TestNestedType struct { + NestedListVal []string + NestedMapVal map[int64]bool + NestedCustomName string `cel:"custom_name" json:"custom_name"` +} + +type TestAllTypes struct { + NestedVal *TestNestedType `json:"nestedVal,omitempty"` + NestedStructVal TestNestedType `json:"nestedStructVal"` + BoolVal bool `json:"boolVal"` + BytesVal []byte + DurationVal time.Duration + DoubleVal float64 + FloatVal float32 + Int32Val int32 + Int64Val int64 + StringVal string + TimestampVal time.Time + Uint32Val uint32 + Uint64Val uint64 + ListVal []*TestNestedType + ArrayVal [1]*TestNestedType + BytesArrayVal [4]byte + MapVal map[string]TestAllTypes + PbVal *proto3pb.TestAllTypes + CustomSliceVal []TestNestedSliceType + CustomMapVal map[string]TestMapVal + CustomName string `cel:"custom_name"` + + // channel types are not supported + UnsupportedVal chan string + UnsupportedListVal []chan string + UnsupportedMapVal map[int]chan string + + // unexported types can be found but not set or accessed + privateVal map[string]string +} + +type TestNestedSliceType struct { + Value string +} + +type TestMapVal struct { + Value string +} + +type TestEmbeddedTypes struct { + Custom + TestNestedType `json:"embedded,omitempty"` + Skipped string `json:"-"` +} + +type TestEmbeddedPointerTypes struct { + *TestNestedType `json:"embedded,omitempty"` +} + +type TestRefValFieldType struct { + OptionalName *types.Optional `cel:"optional_name"` + IntVal types.Int + CELTime types.Timestamp `cel:"time"` +} + +// registeredNativeStruct is registered with NativeTypes in the delegation test. +type registeredNativeStruct struct { + Name string +} + +// unregisteredNativeStruct is not registered, so NativeToValue should hand it to +// the composed base adapter rather than wrapping it as a native object. +type unregisteredNativeStruct struct { + Name string +} + +// recordingAdapter converts unregisteredNativeStruct into a sentinel string and +// records that it was asked to, so the test can confirm nativeTypeProvider +// delegated the value. Everything else falls through to the base adapter. +type recordingAdapter struct { + base types.Adapter + saw bool +} + +func (a *recordingAdapter) NativeToValue(value any) ref.Val { + if _, ok := value.(unregisteredNativeStruct); ok { + a.saw = true + return types.String("from-base-adapter") + } + return a.base.NativeToValue(value) +} diff --git a/common/types/provider.go b/common/types/provider.go index 54111b2be..175d25fd2 100644 --- a/common/types/provider.go +++ b/common/types/provider.go @@ -89,12 +89,13 @@ type FieldType struct { // Registry provides type information for a set of registered types. type Registry struct { - revTypeMap map[string]*Type - structTypes map[string]StructTypeDescriptor - reflectTypes map[reflect.Type]StructTypeDescriptor - pbdb *pb.Db - provider Provider - adapter Adapter + revTypeMap map[string]*Type + structTypes map[string]StructTypeDescriptor + reflectTypes map[reflect.Type]StructTypeDescriptor + pbdb *pb.Db + provider Provider + adapter Adapter + nativeOptions NativeTypeOptions } // NewRegistry accepts a list of proto message instances, ref.Type instances, or RegistryOption @@ -104,10 +105,8 @@ func NewRegistry(types ...any) (*Registry, error) { if err != nil { return nil, err } - for _, t := range types { - if err := registerTypeItem(r, t); err != nil { - return nil, err - } + if err := registerTypeItems(r, types...); err != nil { + return nil, err } return r, nil } @@ -202,10 +201,8 @@ func ComposeTypes(provider Provider, adapter Adapter, types ...any) (Provider, A reg, isReg := provider.(*Registry) aReg, isAdapterReg := adapter.(*Registry) if isReg && isAdapterReg && reg == aReg { - for _, t := range types { - if err := registerTypeItem(reg, t); err != nil { - return nil, nil, err - } + if err := registerTypeItems(reg, types...); err != nil { + return nil, nil, err } return reg, reg, nil } @@ -225,6 +222,7 @@ func (p *Registry) Copy() *Registry { copy.pbdb = p.pbdb.Copy() copy.provider = p.provider copy.adapter = p.adapter + copy.nativeOptions = p.nativeOptions maps.Copy(copy.revTypeMap, p.revTypeMap) maps.Copy(copy.structTypes, p.structTypes) maps.Copy(copy.reflectTypes, p.reflectTypes) @@ -508,6 +506,20 @@ func (p *Registry) RegisterType(types ...ref.Type) error { return nil } +// RegisterNativeType creates nativeType instances for the given reflect.Type and registers them. +func (p *Registry) RegisterNativeType(refType reflect.Type) error { + result, err := newNativeTypes(refType, p.nativeOptions.fieldNameHandler) + if err != nil { + return err + } + for _, nt := range result { + if err := p.RegisterType(nt); err != nil { + return err + } + } + return nil +} + func (p *Registry) findStructDescriptorByReflectType(rt reflect.Type) (StructTypeDescriptor, bool) { if rt == nil { return nil, false @@ -612,6 +624,30 @@ func sanitizeStructTypeName(structType string) string { return structType } +func registerTypeItems(r *Registry, types ...any) error { + opts := make([]any, 0, len(types)) + items := make([]any, 0, len(types)) + for _, t := range types { + switch t.(type) { + case NativeTypeOption: + opts = append(opts, t) + default: + items = append(items, t) + } + } + for _, opt := range opts { + if err := registerTypeItem(r, opt); err != nil { + return err + } + } + for _, item := range items { + if err := registerTypeItem(r, item); err != nil { + return err + } + } + return nil +} + func registerTypeItem(r *Registry, t any) error { switch v := t.(type) { case proto.Message: @@ -620,11 +656,17 @@ func registerTypeItem(r *Registry, t any) error { return r.RegisterDescriptor(v) case ref.Type: return r.RegisterType(v) + case reflect.Type: + return r.RegisterNativeType(v) + case reflect.Value: + return r.RegisterNativeType(v.Type()) + case NativeTypeOption: + return v(&r.nativeOptions) case RegistryOption: _, err := v(r) return err default: - return fmt.Errorf("unsupported type: %T", t) + return fmt.Errorf("unsupported type: %v (%T) must be reflect.Type or reflect.Value", t, t) } } diff --git a/common/types/provider_test.go b/common/types/provider_test.go index 60b61c46d..f6fcd36b7 100644 --- a/common/types/provider_test.go +++ b/common/types/provider_test.go @@ -2418,3 +2418,47 @@ func TestRegistry_RegisterTypeEdgeCases(t *testing.T) { }) } } + +type sampleTaggedStruct struct { + Greeting string `cel:"hello_str"` + Count int `cel:"count_int"` +} + +func TestRegistry_NativeReflectTypes(t *testing.T) { + reg, err := NewRegistry( + ParseStructTags(true), + reflect.TypeFor[sampleTaggedStruct](), + ) + if err != nil { + t.Fatalf("NewRegistry(reflect.Type) failed: %v", err) + } + + t.Run("FindStructType", func(t *testing.T) { + st, found := reg.FindStructType("types.sampleTaggedStruct") + if !found || st == nil { + t.Fatalf("FindStructType(types.sampleTaggedStruct) not found") + } + }) + + t.Run("FindStructFieldType with tag", func(t *testing.T) { + ft, found := reg.FindStructFieldType("types.sampleTaggedStruct", "hello_str") + if !found || ft == nil { + t.Fatalf("FindStructFieldType(hello_str) not found") + } + if ft.Type != StringType { + t.Errorf("FindStructFieldType(hello_str) got %v, want StringType", ft.Type) + } + }) + + t.Run("NativeToValue", func(t *testing.T) { + inst := sampleTaggedStruct{Greeting: "world", Count: 42} + val := reg.NativeToValue(&inst) + if IsError(val) { + t.Fatalf("NativeToValue() failed: %v", val) + } + gotGreeting := val.(traits.Indexer).Get(String("hello_str")) + if gotGreeting.Equal(String("world")) != True { + t.Errorf("Get(hello_str) = %v, want 'world'", gotGreeting) + } + }) +} diff --git a/ext/native.go b/ext/native.go index 5ac2f9328..2598b38d6 100644 --- a/ext/native.go +++ b/ext/native.go @@ -15,31 +15,38 @@ package ext import ( - "errors" - "fmt" - "math" - "reflect" - "strings" - "time" - - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "github.com/google/cel-go/cel" "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/pb" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" - - structpb "google.golang.org/protobuf/types/known/structpb" ) +// NativeTypesOption is a functional interface for configuring handling of native types. +type NativeTypesOption = types.NativeTypeOption + +// NativeTypesFieldNameHandler is a handler for mapping a reflect.StructField to a CEL field name. +// This can be used to override the default Go struct field to CEL field name mapping. +type NativeTypesFieldNameHandler = types.NativeTypesFieldNameHandler + var ( - nativeObjTraitMask = traits.FieldTesterType | traits.IndexerType - jsonValueType = reflect.TypeOf(&structpb.Value{}) - jsonStructType = reflect.TypeOf(&structpb.Struct{}) + // ParseStructTags configures if native types field names should be overridable by CEL struct tags. + // This is equivalent to ParseStructTag("cel") + ParseStructTags = types.ParseStructTags + + // ParseStructTag configures the struct tag to parse. The 0th item in the tag is used as the name of the CEL field. + ParseStructTag = types.ParseStructTag + + // ParseStructField configures how to parse Go struct fields. It can be used to customize struct field parsing. + ParseStructField = types.ParseStructField ) +// NativeTypesVersion sets the native types version support for native extensions functions. +// +// Deprecated: NativeTypesVersion is a no-op and will be removed in a future release. +func NativeTypesVersion(version uint32) NativeTypesOption { + return func(*types.NativeTypeOptions) error { + return nil + } +} + // NativeTypes creates a type provider which uses reflect.Type and reflect.Value instances // to produce type definitions that can be used within CEL. // @@ -98,753 +105,14 @@ var ( // In case there are duplicated field names in the struct, an error will be returned. func NativeTypes(args ...any) cel.EnvOption { return func(env *cel.Env) (*cel.Env, error) { - nativeTypes := make([]any, 0, len(args)) - tpOptions := nativeTypeOptions{ - version: math.MaxUint32, - } - - for _, v := range args { - switch v := v.(type) { - case NativeTypesOption: - err := v(&tpOptions) - if err != nil { - return nil, err - } - default: - nativeTypes = append(nativeTypes, v) - } - } - - tp, err := newNativeTypeProvider(tpOptions, env.CELTypeAdapter(), env.CELTypeProvider(), nativeTypes...) - if err != nil { - return nil, err - } - - env, err = cel.CustomTypeAdapter(tp)(env) + p, a, err := types.ComposeTypes(env.CELTypeProvider(), env.CELTypeAdapter(), args...) if err != nil { return nil, err } - return cel.CustomTypeProvider(tp)(env) - } -} - -// NativeTypesOption is a functional interface for configuring handling of native types. -type NativeTypesOption func(*nativeTypeOptions) error - -// NativeTypesVersion sets the native types version support for native extensions functions. -func NativeTypesVersion(version uint32) NativeTypesOption { - return func(opts *nativeTypeOptions) error { - opts.version = version - return nil - } -} - -// NativeTypesFieldNameHandler is a handler for mapping a reflect.StructField to a CEL field name. -// This can be used to override the default Go struct field to CEL field name mapping. -type NativeTypesFieldNameHandler = func(field reflect.StructField) string - -func fieldNameByTag(structTagToParse string) func(field reflect.StructField) string { - return func(field reflect.StructField) string { - tag, found := field.Tag.Lookup(structTagToParse) - if found { - splits := strings.Split(tag, ",") - if len(splits) > 0 { - // We make the assumption that the leftmost entry in the tag is the name. - // This seems to be true for most tags that have the concept of a name/key, such as: - // https://pkg.go.dev/encoding/xml#Marshal - // https://pkg.go.dev/encoding/json#Marshal - // https://pkg.go.dev/go.mongodb.org/mongo-driver/bson#hdr-Structs - // https://pkg.go.dev/go.yaml.in/yaml/v3#Marshal - name := splits[0] - return name - } - } - - return field.Name - } -} - -func isSkippedFieldName(name string) bool { - return name == "" || name == "-" -} - -type nativeTypeOptions struct { - // fieldNameHandler controls how CEL should perform struct field renames. - // This is most commonly used for switching to parsing based off the struct field tag, - // such as "cel" or "json". - fieldNameHandler NativeTypesFieldNameHandler - - // version is the native types library version. - version uint32 -} - -// ParseStructTags configures if native types field names should be overridable by CEL struct tags. -// This is equivalent to ParseStructTag("cel") -func ParseStructTags(enabled bool) NativeTypesOption { - if enabled { - return ParseStructTag("cel") - } - return ParseStructField(nil) -} - -// ParseStructTag configures the struct tag to parse. The 0th item in the tag is used as the name of the CEL field. -// For example: -// If the tag to parse is "cel" and the struct field has tag cel:"foo", the CEL struct field will be "foo". -// If the tag to parse is "json" and the struct field has tag json:"foo,omitempty", the CEL struct field will be "foo". -func ParseStructTag(tag string) NativeTypesOption { - return ParseStructField(fieldNameByTag(tag)) -} - -// ParseStructField configures how to parse Go struct fields. It can be used to customize struct field parsing. -func ParseStructField(handler NativeTypesFieldNameHandler) NativeTypesOption { - return func(ntp *nativeTypeOptions) error { - ntp.fieldNameHandler = handler - return nil - } -} - -func newNativeTypeProvider(tpOptions nativeTypeOptions, adapter types.Adapter, provider types.Provider, refTypes ...any) (*nativeTypeProvider, error) { - nativeTypes := make(map[string]*nativeType, len(refTypes)) - for _, refType := range refTypes { - switch rt := refType.(type) { - case reflect.Type: - result, err := newNativeTypes(tpOptions.fieldNameHandler, rt) - if err != nil { - return nil, err - } - for idx := range result { - nativeTypes[result[idx].TypeName()] = result[idx] - } - case reflect.Value: - result, err := newNativeTypes(tpOptions.fieldNameHandler, rt.Type()) - if err != nil { - return nil, err - } - for idx := range result { - nativeTypes[result[idx].TypeName()] = result[idx] - } - default: - return nil, fmt.Errorf("unsupported native type: %v (%T) must be reflect.Type or reflect.Value", rt, rt) - } - } - return &nativeTypeProvider{ - nativeTypes: nativeTypes, - baseAdapter: adapter, - baseProvider: provider, - options: tpOptions, - }, nil -} - -type nativeTypeProvider struct { - nativeTypes map[string]*nativeType - baseAdapter types.Adapter - baseProvider types.Provider - options nativeTypeOptions -} - -// EnumValue proxies to the types.Provider configured at the times the NativeTypes -// option was configured. -func (tp *nativeTypeProvider) EnumValue(enumName string) ref.Val { - return tp.baseProvider.EnumValue(enumName) -} - -// FindIdent looks up natives type instances by qualified identifier, and if not found -// proxies to the composed types.Provider. -func (tp *nativeTypeProvider) FindIdent(typeName string) (ref.Val, bool) { - if t, found := tp.nativeTypes[typeName]; found { - return t, true - } - return tp.baseProvider.FindIdent(typeName) -} - -// FindStructType looks up the CEL type definition by qualified identifier, and if not found -// proxies to the composed types.Provider. -func (tp *nativeTypeProvider) FindStructType(typeName string) (*types.Type, bool) { - if _, found := tp.nativeTypes[typeName]; found { - return types.NewTypeTypeWithParam(types.NewObjectType(typeName)), true - } - return tp.baseProvider.FindStructType(typeName) -} - -// FindStructFieldNames looks up the type definition first from the native types, then from -// the backing provider type set. If found, a set of field names corresponding to the type -// will be returned. -func (tp *nativeTypeProvider) FindStructFieldNames(typeName string) ([]string, bool) { - if t, found := tp.nativeTypes[typeName]; found { - return t.FieldNames(), true - } - return tp.baseProvider.FindStructFieldNames(typeName) -} - -// FindStructFieldType looks up a native type's field definition, and if the type name is not a native -// type then proxies to the composed types.Provider -func (tp *nativeTypeProvider) FindStructFieldType(typeName, fieldName string) (*types.FieldType, bool) { - if t, found := tp.nativeTypes[typeName]; found { - return t.FindFieldType(fieldName) - } - return tp.baseProvider.FindStructFieldType(typeName, fieldName) -} - -// NewValue implements the ref.TypeProvider interface method. -func (tp *nativeTypeProvider) NewValue(typeName string, fields map[string]ref.Val) ref.Val { - if t, found := tp.nativeTypes[typeName]; found { - return t.NewValue(tp, fields) - } - return tp.baseProvider.NewValue(typeName, fields) -} - -// NewValue adapts native values to CEL values and will proxy to the composed type adapter -// for non-native types. -func (tp *nativeTypeProvider) NativeToValue(val any) ref.Val { - if val == nil { - return types.NullValue - } - if v, ok := val.(ref.Val); ok { - return v - } - rawVal := reflect.ValueOf(val) - refVal := rawVal - if refVal.Kind() == reflect.Ptr { - refVal = reflect.Indirect(refVal) - } - // This isn't quite right if you're also supporting proto, - // but maybe an acceptable limitation. - switch refVal.Kind() { - case reflect.Array, reflect.Slice: - switch val := val.(type) { - case []byte: - return tp.baseAdapter.NativeToValue(val) - default: - if refVal.Type().Elem() == reflect.TypeFor[byte]() { - return tp.baseAdapter.NativeToValue(val) - } - return types.NewDynamicList(tp, val) - } - case reflect.Map: - return types.NewDynamicMap(tp, val) - case reflect.Struct: - switch val := val.(type) { - case proto.Message, *pb.Map, protoreflect.List, protoreflect.Message, protoreflect.Value, - time.Time: - return tp.baseAdapter.NativeToValue(val) - default: - // Only claim struct values whose type was registered via - // ext.NativeTypes. Every other method on nativeTypeProvider checks - // the registry before handling a type; without the same check here - // a composed base adapter never sees unregistered structs it wants - // to convert itself. - typeName := fmt.Sprintf("%s.%s", simplePkgAlias(refVal.Type().PkgPath()), refVal.Type().Name()) - if ntype, found := tp.nativeTypes[typeName]; found { - return tp.newNativeObject(val, ntype, rawVal) - } - return tp.baseAdapter.NativeToValue(val) - } - default: - return tp.baseAdapter.NativeToValue(val) - } -} - -func convertToCelType(refType reflect.Type) (*cel.Type, bool) { - switch refType.Kind() { - case reflect.Bool: - return cel.BoolType, true - case reflect.Float32, reflect.Float64: - return cel.DoubleType, true - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if refType == durationType { - return cel.DurationType, true - } - return cel.IntType, true - case reflect.String: - return cel.StringType, true - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return cel.UintType, true - case reflect.Array, reflect.Slice: - refElem := refType.Elem() - if refElem == reflect.TypeOf(byte(0)) { - return cel.BytesType, true - } - elemType, ok := convertToCelType(refElem) - if !ok { - return nil, false - } - return cel.ListType(elemType), true - case reflect.Map: - keyType, ok := convertToCelType(refType.Key()) - if !ok { - return nil, false - } - // Ensure the key type is a int, bool, uint, string - elemType, ok := convertToCelType(refType.Elem()) - if !ok { - return nil, false - } - return cel.MapType(keyType, elemType), true - case reflect.Struct: - if refType == timestampType { - return cel.TimestampType, true - } - if refType.Implements(refValType) { - emptyCelVal := reflect.New(refType).Elem().Interface().(ref.Val) - return emptyCelVal.Type().(*cel.Type), true - } - return cel.ObjectType( - fmt.Sprintf("%s.%s", simplePkgAlias(refType.PkgPath()), refType.Name()), - ), true - case reflect.Pointer: - if refType.Implements(refValType) { - emptyCelVal := reflect.New(refType.Elem()).Interface().(ref.Val) - return emptyCelVal.Type().(*cel.Type), true - } - if refType.Implements(pbMsgInterfaceType) { - pbMsg := reflect.New(refType.Elem()).Interface().(protoreflect.ProtoMessage) - return cel.ObjectType(string(pbMsg.ProtoReflect().Descriptor().FullName())), true - } - return convertToCelType(refType.Elem()) - } - return nil, false -} - -func (tp *nativeTypeProvider) newNativeObject(val any, valType *nativeType, refValue reflect.Value) ref.Val { - return &nativeObj{ - Adapter: tp, - val: val, - valType: valType, - refValue: refValue, - } -} - -type nativeObj struct { - types.Adapter - val any - valType *nativeType - refValue reflect.Value -} - -// ConvertToNative implements the ref.Val interface method. -// -// CEL does not have a notion of pointers, so whether a field is a pointer or value -// is handled as part of this conversion step. -func (o *nativeObj) ConvertToNative(typeDesc reflect.Type) (any, error) { - if o.refValue.Type() == typeDesc { - return o.val, nil - } - if o.refValue.Kind() == reflect.Pointer && o.refValue.Type().Elem() == typeDesc { - return o.refValue.Elem().Interface(), nil - } - if typeDesc.Kind() == reflect.Pointer && o.refValue.Type() == typeDesc.Elem() { - ptr := reflect.New(typeDesc.Elem()) - ptr.Elem().Set(o.refValue) - return ptr.Interface(), nil - } - switch typeDesc { - case jsonValueType: - jsonStruct, err := o.ConvertToNative(jsonStructType) + env, err = cel.CustomTypeAdapter(a)(env) if err != nil { return nil, err } - return structpb.NewStructValue(jsonStruct.(*structpb.Struct)), nil - case jsonStructType: - refVal := reflect.Indirect(o.refValue) - fields := make(map[string]*structpb.Value, refVal.NumField()) - for fieldName, fieldType := range o.valType.fieldsByName { - fieldValue := refVal.FieldByIndex(fieldType.Index) - if !fieldValue.IsValid() || fieldValue.IsZero() { - continue - } - fieldCELVal := o.NativeToValue(fieldValue.Interface()) - fieldJSONVal, err := fieldCELVal.ConvertToNative(jsonValueType) - if err != nil { - return nil, err - } - fields[fieldName] = fieldJSONVal.(*structpb.Value) - } - return &structpb.Struct{Fields: fields}, nil - } - return nil, fmt.Errorf("type conversion error from '%v' to '%v'", o.Type(), typeDesc) -} - -// ConvertToType implements the ref.Val interface method. -func (o *nativeObj) ConvertToType(typeVal ref.Type) ref.Val { - switch typeVal { - case types.TypeType: - return o.valType - default: - if typeVal.TypeName() == o.valType.typeName { - return o - } - } - return types.NewErr("type conversion error from '%s' to '%s'", o.Type(), typeVal) -} - -// Equal implements the ref.Val interface method. -// -// Note, that in Golang a pointer to a value is not equal to the value it contains. -// In CEL pointers and values to which they point are equal. -func (o *nativeObj) Equal(other ref.Val) ref.Val { - otherNtv, ok := other.(*nativeObj) - if !ok { - return types.False - } - val := o.val - otherVal := otherNtv.val - refVal := o.refValue - otherRefVal := otherNtv.refValue - if refVal.Kind() != otherRefVal.Kind() { - if refVal.Kind() == reflect.Pointer { - val = refVal.Elem().Interface() - } else if otherRefVal.Kind() == reflect.Pointer { - otherVal = otherRefVal.Elem().Interface() - } - } - return types.Bool(reflect.DeepEqual(val, otherVal)) -} - -// IsZeroValue indicates whether the contained Golang value is a zero value. -// -// Golang largely follows proto3 semantics for zero values. -func (o *nativeObj) IsZeroValue() bool { - return reflect.Indirect(o.refValue).IsZero() -} - -// IsSet tests whether a field which is defined is set to a non-default value. -func (o *nativeObj) IsSet(field ref.Val) ref.Val { - refField, refErr := o.getReflectedField(field) - if refErr != nil { - return refErr - } - return types.Bool(!refField.IsZero()) -} - -// Get returns the value fo a field name. -func (o *nativeObj) Get(field ref.Val) ref.Val { - refField, refErr := o.getReflectedField(field) - if refErr != nil { - return refErr - } - return adaptFieldValue(o, refField) -} - -func (o *nativeObj) getReflectedField(field ref.Val) (reflect.Value, ref.Val) { - fieldName, ok := field.(types.String) - if !ok { - return reflect.Value{}, types.MaybeNoSuchOverloadErr(field) - } - fieldNameStr := string(fieldName) - refField, isDefined := o.valType.hasField(fieldNameStr) - if !isDefined { - return reflect.Value{}, types.NewErr("no such field: %s", fieldName) - } - refVal := reflect.Indirect(o.refValue) - return safeGetFieldByIndex(refVal, refField.Index), nil -} - -// Type implements the ref.Val interface method. -func (o *nativeObj) Type() ref.Type { - return o.valType -} - -// Value implements the ref.Val interface method. -func (o *nativeObj) Value() any { - return o.val -} - -func newNativeTypes(fieldNameHandler NativeTypesFieldNameHandler, rawType reflect.Type) ([]*nativeType, error) { - nt, err := newNativeType(fieldNameHandler, rawType) - if err != nil { - return nil, err - } - result := []*nativeType{nt} - - alreadySeen := make(map[string]struct{}) - var iterateStructMembers func(reflect.Type) - iterateStructMembers = func(t reflect.Type) { - if t.Implements(reflect.TypeFor[ref.Val]()) { - // skip this field since it's a CEL ref.Val instance. - return - } - if k := t.Kind(); k == reflect.Pointer || k == reflect.Slice || k == reflect.Array || k == reflect.Map { - iterateStructMembers(t.Elem()) - return - } - if t.Kind() != reflect.Struct { - return - } - if _, seen := alreadySeen[t.String()]; seen { - return - } - alreadySeen[t.String()] = struct{}{} - nt, ntErr := newNativeType(fieldNameHandler, t) - if ntErr != nil { - err = ntErr - return - } - result = append(result, nt) - - for _, field := range reflect.VisibleFields(t) { - if !field.IsExported() || !isSupportedType(field.Type) { - continue - } - iterateStructMembers(field.Type) - } - } - iterateStructMembers(rawType) - - return result, err -} - -var ( - errDuplicatedFieldName = errors.New("field name already exists in struct") -) - -func toFieldName(fieldNameHandler NativeTypesFieldNameHandler, f reflect.StructField) string { - if fieldNameHandler == nil { - return f.Name - } - return fieldNameHandler(f) -} - -func newNativeType(fieldNameHandler NativeTypesFieldNameHandler, rawType reflect.Type) (*nativeType, error) { - refType := rawType - if refType.Kind() == reflect.Pointer { - refType = refType.Elem() - } - if !isValidObjectType(refType) { - return nil, fmt.Errorf("unsupported reflect.Type %v, must be reflect.Struct", rawType) - } - - // Collect the set of visible / exported fields, ensuring that unsupported types and sentinel - // 'skip' tags such as `-` are filtered out. - fieldsByName := make(map[string]reflect.StructField) - for _, field := range reflect.VisibleFields(refType) { - if !field.IsExported() || !isSupportedType(field.Type) { - continue - } - fieldName := toFieldName(fieldNameHandler, field) - if isSkippedFieldName(fieldName) { - continue - } - if _, found := fieldsByName[fieldName]; found { - return nil, fmt.Errorf("invalid field name `%s` in struct `%s`: %w", fieldName, refType.Name(), errDuplicatedFieldName) - } - fieldsByName[fieldName] = field - } - - return &nativeType{ - typeName: fmt.Sprintf("%s.%s", simplePkgAlias(refType.PkgPath()), refType.Name()), - refType: refType, - fieldsByName: fieldsByName, - }, nil -} - -type nativeType struct { - typeName string - refType reflect.Type - fieldsByName map[string]reflect.StructField -} - -// ConvertToNative implements ref.Val.ConvertToNative. -func (t *nativeType) ConvertToNative(typeDesc reflect.Type) (any, error) { - return nil, fmt.Errorf("type conversion error for type to '%v'", typeDesc) -} - -// ConvertToType implements ref.Val.ConvertToType. -func (t *nativeType) ConvertToType(typeVal ref.Type) ref.Val { - switch typeVal { - case types.TypeType: - return types.TypeType - } - return types.NewErr("type conversion error from '%s' to '%s'", types.TypeType, typeVal) -} - -// Equal returns true of both type names are equal to each other. -func (t *nativeType) Equal(other ref.Val) ref.Val { - otherType, ok := other.(ref.Type) - return types.Bool(ok && t.TypeName() == otherType.TypeName()) -} - -// HasTrait implements the ref.Type interface method. -func (t *nativeType) HasTrait(trait int) bool { - return nativeObjTraitMask&trait == trait -} - -// String implements the strings.Stringer interface method. -func (t *nativeType) String() string { - return t.typeName -} - -// Type implements the ref.Val interface method. -func (t *nativeType) Type() ref.Type { - return types.TypeType -} - -// TypeName implements the ref.Type interface method. -func (t *nativeType) TypeName() string { - return t.typeName -} - -// Value implements the ref.Val interface method. -func (t *nativeType) Value() any { - return t.typeName -} - -// hasField returns whether a field name has a corresponding Golang reflect.StructField -func (t *nativeType) hasField(fieldName string) (reflect.StructField, bool) { - f, found := t.fieldsByName[fieldName] - if !found { - return reflect.StructField{}, false - } - return f, true -} - -// FieldNames provides the list of field names for this type. -func (t *nativeType) FieldNames() []string { - fields := make([]string, 0, len(t.fieldsByName)) - for fieldName := range t.fieldsByName { - fields = append(fields, fieldName) - } - return fields -} - -// FindFieldType looks up a field by name and provides the type and accessor functions -// required for type identification at check-time and accessors for use at runtime. -func (t *nativeType) FindFieldType(fieldName string) (*types.FieldType, bool) { - refField, found := t.hasField(fieldName) - if !found { - return nil, false - } - celType, ok := convertToCelType(refField.Type) - if !ok { - return nil, false - } - return &types.FieldType{ - Type: celType, - IsSet: func(obj any) bool { - // TODO: determine what to do if refVal is Invalid() - refVal := reflect.Indirect(reflect.ValueOf(obj)) - // Check if field path exists and is set - refFieldVal := safeGetFieldByIndex(refVal, refField.Index) - return refFieldVal.IsValid() && !refFieldVal.IsZero() - }, - GetFrom: func(obj any) (any, error) { - // TODO: determine what to do if refVal is Invalid() - refVal := reflect.Indirect(reflect.ValueOf(obj)) - // Check if field path exists and is set - refFieldVal := safeGetFieldByIndex(refVal, refField.Index) - return getFieldValue(refFieldVal), nil - }, - }, true -} - -// NewValue constructs a new native Go struct instance populated with the given field values. -func (t *nativeType) NewValue(adapter types.Adapter, fields map[string]ref.Val) ref.Val { - refPtr := reflect.New(t.refType) - refVal := refPtr.Elem() - for fieldName, val := range fields { - refFieldDef, isDefined := t.hasField(fieldName) - if !isDefined { - return types.NewErr("no such field: %s", fieldName) - } - fieldVal, err := val.ConvertToNative(refFieldDef.Type) - if err != nil { - return types.NewErrFromString(err.Error()) - } - refField := safeSetFieldByIndex(refVal, refFieldDef.Index) - if !refField.IsValid() { - return types.NewErr("cannot set field: %s", fieldName) - } - refField.Set(reflect.ValueOf(fieldVal)) + return cel.CustomTypeProvider(p)(env) } - return adapter.NativeToValue(refPtr.Interface()) } - -func adaptFieldValue(adapter types.Adapter, refField reflect.Value) ref.Val { - return adapter.NativeToValue(getFieldValue(refField)) -} - -// safeSetFieldByIndex traverses refField.Index to set a field value. -// If an intermediate pointer along the path is nil, it allocates a new -// instance of the struct that the pointer references. -func safeSetFieldByIndex(v reflect.Value, index []int) reflect.Value { - for _, i := range index { - if v.Kind() == reflect.Pointer { - if v.IsNil() { - v.Set(reflect.New(v.Type().Elem())) - } - v = v.Elem() - } - if v.Kind() != reflect.Struct || i >= v.NumField() { - return reflect.Value{} - } - v = v.Field(i) - } - return v -} - -// safeGetFieldByIndex traverses refField.Index. If an intermediate pointer along -// the path is nil, it substitutes a pointer to an empty struct instance of that type. -func safeGetFieldByIndex(v reflect.Value, index []int) reflect.Value { - for _, i := range index { - if v.Kind() == reflect.Pointer { - if v.IsNil() { - // Intermediate pointer to struct is nil: instantiate an empty struct - v = reflect.New(v.Type().Elem()).Elem() - } else { - v = v.Elem() - } - } - if v.Kind() != reflect.Struct || i >= v.NumField() { - return reflect.Value{} - } - v = v.Field(i) - } - return v -} - -func getFieldValue(refField reflect.Value) any { - if !refField.IsValid() { - return nil - } - if refField.IsZero() { - switch refField.Kind() { - case reflect.Struct: - if refField.Type() == timestampType { - return time.Unix(0, 0) - } - case reflect.Pointer: - return reflect.New(refField.Type().Elem()).Interface() - } - } - return refField.Interface() -} - -func simplePkgAlias(pkgPath string) string { - paths := strings.Split(pkgPath, "/") - if len(paths) == 0 { - return "" - } - return paths[len(paths)-1] -} - -func isValidObjectType(refType reflect.Type) bool { - return refType.Kind() == reflect.Struct -} - -func isSupportedType(refType reflect.Type) bool { - switch refType.Kind() { - case reflect.Chan, reflect.Complex64, reflect.Complex128, reflect.Func, reflect.UnsafePointer, reflect.Uintptr: - return false - case reflect.Array, reflect.Slice: - return isSupportedType(refType.Elem()) - case reflect.Map: - return isSupportedType(refType.Key()) && isSupportedType(refType.Elem()) - } - return true -} - -var ( - pbMsgInterfaceType = reflect.TypeFor[protoreflect.ProtoMessage]() - refValType = reflect.TypeFor[ref.Val]() - timestampType = reflect.TypeFor[time.Time]() - durationType = reflect.TypeFor[time.Duration]() -) diff --git a/ext/native_test.go b/ext/native_test.go index a0bef47f9..c6af35c9a 100644 --- a/ext/native_test.go +++ b/ext/native_test.go @@ -873,10 +873,19 @@ func TestNativeTypeConvertToType(t *testing.T) { for i, tst := range nativeTests { tc := tst t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { - handler := fieldNameByTag(tc.tag) - nt, err := newNativeType(handler, reflect.TypeFor[*TestAllTypes]()) + handler := func(f reflect.StructField) string { + tag, found := f.Tag.Lookup(tc.tag) + if found { + splits := strings.Split(tag, ",") + if len(splits) > 0 { + return splits[0] + } + } + return f.Name + } + nt, err := types.NewNativeType(reflect.TypeFor[*TestAllTypes](), types.ParseStructField(handler)) if err != nil { - t.Fatalf("newNativeType() failed: %v", err) + t.Fatalf("NewNativeType() failed: %v", err) } if nt.ConvertToType(types.TypeType) != types.TypeType { t.Error("ConvertToType(Type) failed") @@ -889,9 +898,9 @@ func TestNativeTypeConvertToType(t *testing.T) { } func TestNativeTypeConvertToNative(t *testing.T) { - nt, err := newNativeType(fieldNameByTag("cel"), reflect.TypeFor[*TestAllTypes]()) + nt, err := types.NewNativeType(reflect.TypeFor[*TestAllTypes]()) if err != nil { - t.Fatalf("newNativeType() failed: %v", err) + t.Fatalf("NewNativeType() failed: %v", err) } out, err := nt.ConvertToNative(reflect.TypeOf(1)) if err == nil { @@ -900,9 +909,9 @@ func TestNativeTypeConvertToNative(t *testing.T) { } func TestNativeTypeHasTrait(t *testing.T) { - nt, err := newNativeType(fieldNameByTag("cel"), reflect.TypeFor[*TestAllTypes]()) + nt, err := types.NewNativeType(reflect.TypeFor[*TestAllTypes]()) if err != nil { - t.Fatalf("newNativeType() failed: %v", err) + t.Fatalf("NewNativeType() failed: %v", err) } if !nt.HasTrait(traits.IndexerType) || !nt.HasTrait(traits.FieldTesterType) { t.Error("nt.HasTrait() failed indicate support for presence test and field access.") @@ -910,9 +919,9 @@ func TestNativeTypeHasTrait(t *testing.T) { } func TestNativeTypeValue(t *testing.T) { - nt, err := newNativeType(fieldNameByTag("cel"), reflect.TypeFor[*TestAllTypes]()) + nt, err := types.NewNativeType(reflect.TypeFor[*TestAllTypes]()) if err != nil { - t.Fatalf("newNativeType() failed: %v", err) + t.Fatalf("NewNativeType() failed: %v", err) } if nt.Value() != nt.String() { t.Errorf("nt.Value() got %v, wanted %v", nt.Value(), nt.String()) @@ -920,12 +929,25 @@ func TestNativeTypeValue(t *testing.T) { } func TestNativeStructWithMultipleSameFieldNames(t *testing.T) { - _, err := newNativeType(fieldNameByTag("cel"), reflect.TypeFor[TestStructWithMultipleSameNames]()) + tagHandler := func(f reflect.StructField) string { + tag, found := f.Tag.Lookup("cel") + if found { + splits := strings.Split(tag, ",") + if len(splits) > 0 { + return splits[0] + } + } + return f.Name + } + _, err := types.NewNativeType( + reflect.TypeFor[TestStructWithMultipleSameNames](), + types.ParseStructField(tagHandler), + ) if err == nil { - t.Fatal("newNativeType() did not fail as expected") + t.Fatal("NewNativeType() did not fail as expected") } - if !errors.Is(err, errDuplicatedFieldName) { - t.Fatalf("newNativeType() exepected duplicated field name error, but got: %v", err) + if !strings.Contains(err.Error(), "field name already exists") { + t.Fatalf("NewNativeType() expected duplicated field name error, but got: %v", err) } } From f10a2e6441f175adc281484251a0fcbab92bd8a6 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Wed, 5 Aug 2026 14:11:26 -0700 Subject: [PATCH 10/37] Optimize NativeToValue call paths (#1400) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Optimize NativeToValue to minimize allocs - **Unified `NativeToValue` Dispatch**: Consolidated private `nativeToValue` and `Registry.NativeToValue` into a single authoritative receiver method. `DefaultTypeAdapter` delegates directly to `emptyRegistry.NativeToValue`. - **Reflection & Allocation Reduction**: - Replaced dynamic `.Convert().Interface().(T)` reflection conversions for type aliases with direct zero-allocation primitive getters (`.Int()`, `.Uint()`, `.Float()`, `.Bool()`, `.String()`). - Integrated custom struct descriptor lookups directly into the reflection branch using `reflect.TypeOf(value)` for instant map matching. - Eliminated duplicate `reflect.ValueOf()` instantiations and redundant nil pointer checks across conversion paths. --- | Benchmark Target | `master` Baseline | Refactored Branch | Delta | Allocs / Op | | :--- | :---: | :---: | :---: | :---: | | `proto/TestAllTypes` | 177.0 ns/op | **87.91 ns/op** | 🚀 **+50.3% faster** | 1 alloc | | `nativeStruct/pointer` | 20.59 ns/op | **13.18 ns/op** | 🚀 **+36.0% faster** | 0 allocs | | `nativeStruct/value` | 17.25 ns/op | **13.52 ns/op** | 🚀 **+21.6% faster** | 0 allocs | | `ref.Val/String` | 4.81 ns/op | **2.62 ns/op** | 🚀 **+45.5% faster** | 0 allocs | | `ref.Val/Int` | 3.73 ns/op | **2.69 ns/op** | 🚀 **+27.9% faster** | 0 allocs | | `int/1` | 3.86 ns/op | **2.55 ns/op** | 🚀 **+33.9% faster** | 0 allocs | | `bool/true` | 2.91 ns/op | **2.11 ns/op** | 🚀 **+27.5% faster** | 0 allocs | | Benchmark Target | `master` Baseline | Refactored Branch | Delta | | :--- | :---: | :---: | :---: | | `nested_proto_field` | 141.3 ns/op | **124.7 ns/op** | 🚀 **+11.7% faster** | | `nested_proto_field_with_index` | 577.5 ns/op | **540.6 ns/op** | 🚀 **+6.4% faster** | | `index` | 122.3 ns/op | **108.1 ns/op** | 🚀 **+11.6% faster** | | `index_list_int_uint_type_index` | 42.87 ns/op | **38.22 ns/op** | 🚀 **+10.8% faster** | | `index_cross_type_float_uint` | 264.4 ns/op | **240.0 ns/op** | 🚀 **+9.2% faster** | | `select_subsumed_field` | 14.27 ns/op | **13.05 ns/op** | 🚀 **+8.5% faster** | | `select_custom_pb3_optional_field` | 43.96 ns/op | **41.71 ns/op** | 🚀 **+5.1% faster** | | `complex_qual_vars` | 338.4 ns/op | **323.1 ns/op** | 🚀 **+4.5% faster** | * Consistency fix for safe field getter * Remove test added by mistake --- cel/program_async_test.go | 2 +- common/types/native.go | 37 +-- common/types/provider.go | 410 ++++++++++++++-------------------- common/types/provider_test.go | 8 + 4 files changed, 202 insertions(+), 255 deletions(-) diff --git a/cel/program_async_test.go b/cel/program_async_test.go index 60750047d..e3d1746dd 100644 --- a/cel/program_async_test.go +++ b/cel/program_async_test.go @@ -188,7 +188,7 @@ func TestConcurrentEval(t *testing.T) { { name: "drain_ready_partial_debounce", expr: `delayed_rpc("a", 1) + delayed_rpc("b", 2) + delayed_rpc("c", 10)`, - opts: []any{cel.ConcurrentDrainStrategy(async.DrainReady(2 * time.Millisecond))}, + opts: []any{cel.ConcurrentDrainStrategy(async.DrainReady(3 * time.Millisecond))}, trackCost: true, wantCost: 15, want: "abc", diff --git a/common/types/native.go b/common/types/native.go index 802abdff4..33c897b43 100644 --- a/common/types/native.go +++ b/common/types/native.go @@ -122,11 +122,18 @@ func (t *NativeType) Adapt(adapter Adapter, value any) ref.Val { if value == nil { return NullValue } + refVal := reflect.ValueOf(value) + if refVal.Kind() == reflect.Ptr { + if refVal.IsNil() { + return NullValue + } + refVal = refVal.Elem() + } return &nativeObj{ Adapter: adapter, val: value, valType: t, - refValue: reflect.ValueOf(value), + refValue: refVal, } } @@ -248,13 +255,16 @@ type nativeObj struct { func (o *nativeObj) ConvertToNative(typeDesc reflect.Type) (any, error) { if o.refValue.Type() == typeDesc { - return o.val, nil - } - if o.refValue.Kind() == reflect.Pointer && o.refValue.Type().Elem() == typeDesc { - return o.refValue.Elem().Interface(), nil + if reflect.TypeOf(o.val) == typeDesc { + return o.val, nil + } + return o.refValue.Interface(), nil } if typeDesc.Kind() == reflect.Pointer && o.refValue.Type() == typeDesc.Elem() { - ptr := reflect.New(typeDesc.Elem()) + if reflect.TypeOf(o.val) == typeDesc { + return o.val, nil + } + ptr := reflect.New(o.refValue.Type()) ptr.Elem().Set(o.refValue) return ptr.Interface(), nil } @@ -269,7 +279,7 @@ func (o *nativeObj) ConvertToNative(typeDesc reflect.Type) (any, error) { refVal := reflect.Indirect(o.refValue) fields := make(map[string]*structpb.Value, refVal.NumField()) for fieldName, fieldType := range o.valType.fieldsByName { - fieldValue := refVal.FieldByIndex(fieldType.Index) + fieldValue := safeGetFieldByIndex(refVal, fieldType.Index) if !fieldValue.IsValid() || fieldValue.IsZero() { continue } @@ -304,20 +314,15 @@ func (o *nativeObj) Equal(other ref.Val) ref.Val { } val := o.val otherVal := otherNtv.val - refVal := o.refValue - otherRefVal := otherNtv.refValue - if refVal.Kind() != otherRefVal.Kind() { - if refVal.Kind() == reflect.Pointer { - val = refVal.Elem().Interface() - } else if otherRefVal.Kind() == reflect.Pointer { - otherVal = otherRefVal.Elem().Interface() - } + if reflect.TypeOf(val).Kind() != reflect.TypeOf(otherVal).Kind() { + val = o.refValue.Interface() + otherVal = otherNtv.refValue.Interface() } return Bool(reflect.DeepEqual(val, otherVal)) } func (o *nativeObj) IsZeroValue() bool { - return reflect.Indirect(o.refValue).IsZero() + return o.refValue.IsZero() } func (o *nativeObj) IsSet(field ref.Val) ref.Val { diff --git a/common/types/provider.go b/common/types/provider.go index 175d25fd2..76285143a 100644 --- a/common/types/provider.go +++ b/common/types/provider.go @@ -544,13 +544,145 @@ func (p *Registry) findStructDescriptorByReflectType(rt reflect.Type) (StructTyp // // This method should be the inverse of ref.Val.ConvertToNative. func (p *Registry) NativeToValue(value any) ref.Val { - if val, found := nativeToValue(p, value); found { - return val - } switch v := value.(type) { + case nil: + return NullValue + case *Bool: + if v != nil { + return *v + } + case *Bytes: + if v != nil { + return *v + } + case *Double: + if v != nil { + return *v + } + case *Int: + if v != nil { + return *v + } + case *String: + if v != nil { + return *v + } + case *Uint: + if v != nil { + return *v + } + case ref.Val: + return v + case bool: + return Bool(v) + case int: + return Int(v) + case int32: + return Int(v) + case int64: + return Int(v) + case uint: + return Uint(v) + case uint32: + return Uint(v) + case uint64: + return Uint(v) + case float32: + return Double(v) + case float64: + return Double(v) + case string: + return String(v) + case *dpb.Duration: + return Duration{Duration: v.AsDuration()} + case time.Duration: + return Duration{Duration: v} + case *tpb.Timestamp: + return Timestamp{Time: v.AsTime()} + case time.Time: + return Timestamp{Time: v} + case *bool: + if v != nil { + return Bool(*v) + } + case *float32: + if v != nil { + return Double(*v) + } + case *float64: + if v != nil { + return Double(*v) + } + case *int: + if v != nil { + return Int(*v) + } + case *int32: + if v != nil { + return Int(*v) + } + case *int64: + if v != nil { + return Int(*v) + } + case *string: + if v != nil { + return String(*v) + } + case *uint: + if v != nil { + return Uint(*v) + } + case *uint32: + if v != nil { + return Uint(*v) + } + case *uint64: + if v != nil { + return Uint(*v) + } + case []byte: + return Bytes(v) + // specializations for common lists types. + case []string: + return NewStringList(p, v) + case []ref.Val: + return NewRefValList(p, v) + // specializations for common map types. + case map[string]string: + return NewStringStringMap(p, v) + case map[string]any: + return NewStringInterfaceMap(p, v) + case map[ref.Val]ref.Val: + return NewRefValMap(p, v) + // additional specializations may be added upon request / need. + case *anypb.Any: + if v == nil { + return UnsupportedRefValConversionErr(v) + } + unpackedAny, err := v.UnmarshalNew() + if err != nil { + return NewErr("anypb.UnmarshalNew() failed for type %q: %v", v.GetTypeUrl(), err) + } + return p.NativeToValue(unpackedAny) + case *structpb.NullValue, structpb.NullValue: + return NullValue + case *structpb.ListValue: + return NewJSONList(p, v) + case *structpb.Struct: + return NewJSONStruct(p, v) + case protoreflect.EnumNumber: + return Int(v) case proto.Message: + if v == nil { + return UnsupportedRefValConversionErr(v) + } typeName := string(v.ProtoReflect().Descriptor().FullName()) - td, found := p.pbdb.DescribeType(typeName) + pbdb := p.pbdb + if pbdb == nil { + pbdb = pb.DefaultDb + } + td, found := pbdb.DescribeType(typeName) if !found { if p.adapter != nil { return p.adapter.NativeToValue(value) @@ -578,15 +710,43 @@ func (p *Registry) NativeToValue(value any) ref.Val { case protoreflect.Value: return p.NativeToValue(v.Interface()) default: - if len(p.reflectTypes) > 0 && value != nil { - if st, found := p.findStructDescriptorByReflectType(reflect.TypeOf(value)); found { - val := reflect.ValueOf(value) - if val.Kind() == reflect.Ptr && val.IsNil() { - return NullValue - } + rt := reflect.TypeOf(value) + if len(p.reflectTypes) > 0 { + if st, found := p.findStructDescriptorByReflectType(rt); found { return st.Adapt(p, value) } } + refVal := reflect.ValueOf(v) + if refVal.Kind() == reflect.Ptr { + if refVal.IsNil() { + break + } + refVal = refVal.Elem() + } + switch refVal.Kind() { + case reflect.Array, reflect.Slice: + if refVal.Type().Elem() == reflect.TypeOf(byte(0)) { + if refVal.CanAddr() { + return Bytes(refVal.Bytes()) + } + tmp := reflect.New(refVal.Type()) + tmp.Elem().Set(refVal) + return Bytes(tmp.Elem().Bytes()) + } + return NewDynamicList(p, v) + case reflect.Map: + return NewDynamicMap(p, v) + case reflect.Bool: + return Bool(refVal.Bool()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return Int(refVal.Int()) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return Uint(refVal.Uint()) + case reflect.Float32, reflect.Float64: + return Double(refVal.Float()) + case reflect.String: + return String(refVal.String()) + } } if p.adapter != nil { return p.adapter.NativeToValue(value) @@ -719,238 +879,12 @@ type defaultTypeAdapter struct{} var ( // DefaultTypeAdapter adapts canonical CEL types from their equivalent Go values. DefaultTypeAdapter = &defaultTypeAdapter{} + emptyRegistry = &Registry{pbdb: pb.DefaultDb} ) // NativeToValue implements the ref.TypeAdapter interface. func (a *defaultTypeAdapter) NativeToValue(value any) ref.Val { - if val, found := nativeToValue(a, value); found { - return val - } - return UnsupportedRefValConversionErr(value) -} - -// nativeToValue returns the converted (ref.Val, true) of a conversion is found, -// otherwise (nil, false) -func nativeToValue(a Adapter, value any) (ref.Val, bool) { - switch v := value.(type) { - case nil: - return NullValue, true - case *Bool: - if v != nil { - return *v, true - } - case *Bytes: - if v != nil { - return *v, true - } - case *Double: - if v != nil { - return *v, true - } - case *Int: - if v != nil { - return *v, true - } - case *String: - if v != nil { - return *v, true - } - case *Uint: - if v != nil { - return *v, true - } - case ref.Val: - return v, true - case bool: - return Bool(v), true - case int: - return Int(v), true - case int32: - return Int(v), true - case int64: - return Int(v), true - case uint: - return Uint(v), true - case uint32: - return Uint(v), true - case uint64: - return Uint(v), true - case float32: - return Double(v), true - case float64: - return Double(v), true - case string: - return String(v), true - case *dpb.Duration: - return Duration{Duration: v.AsDuration()}, true - case time.Duration: - return Duration{Duration: v}, true - case *tpb.Timestamp: - return Timestamp{Time: v.AsTime()}, true - case time.Time: - return Timestamp{Time: v}, true - case *bool: - if v != nil { - return Bool(*v), true - } - case *float32: - if v != nil { - return Double(*v), true - } - case *float64: - if v != nil { - return Double(*v), true - } - case *int: - if v != nil { - return Int(*v), true - } - case *int32: - if v != nil { - return Int(*v), true - } - case *int64: - if v != nil { - return Int(*v), true - } - case *string: - if v != nil { - return String(*v), true - } - case *uint: - if v != nil { - return Uint(*v), true - } - case *uint32: - if v != nil { - return Uint(*v), true - } - case *uint64: - if v != nil { - return Uint(*v), true - } - case []byte: - return Bytes(v), true - // specializations for common lists types. - case []string: - return NewStringList(a, v), true - case []ref.Val: - return NewRefValList(a, v), true - // specializations for common map types. - case map[string]string: - return NewStringStringMap(a, v), true - case map[string]any: - return NewStringInterfaceMap(a, v), true - case map[ref.Val]ref.Val: - return NewRefValMap(a, v), true - // additional specializations may be added upon request / need. - case *anypb.Any: - if v == nil { - return UnsupportedRefValConversionErr(v), true - } - unpackedAny, err := v.UnmarshalNew() - if err != nil { - return NewErr("anypb.UnmarshalNew() failed for type %q: %v", v.GetTypeUrl(), err), true - } - return a.NativeToValue(unpackedAny), true - case *structpb.NullValue, structpb.NullValue: - return NullValue, true - case *structpb.ListValue: - return NewJSONList(a, v), true - case *structpb.Struct: - return NewJSONStruct(a, v), true - case protoreflect.EnumNumber: - return Int(v), true - case proto.Message: - if v == nil { - return UnsupportedRefValConversionErr(v), true - } - typeName := string(v.ProtoReflect().Descriptor().FullName()) - td, found := pb.DefaultDb.DescribeType(typeName) - if !found { - return nil, false - } - val, unwrapped, err := td.MaybeUnwrap(v) - if err != nil { - return UnsupportedRefValConversionErr(v), true - } - if !unwrapped { - return nil, false - } - return a.NativeToValue(val), true - // Note: dynamicpb.Message implements the proto.Message _and_ protoreflect.Message interfaces - // which means that this case must appear after handling a proto.Message type. - case protoreflect.Message: - return a.NativeToValue(v.Interface()), true - default: - refValue := reflect.ValueOf(v) - if refValue.Kind() == reflect.Ptr { - if refValue.IsNil() { - return nil, false - } - refValue = refValue.Elem() - } - refKind := refValue.Kind() - switch refKind { - case reflect.Array, reflect.Slice: - if refValue.Type().Elem() == reflect.TypeOf(byte(0)) { - if refValue.CanAddr() { - return Bytes(refValue.Bytes()), true - } - tmp := reflect.New(refValue.Type()) - tmp.Elem().Set(refValue) - return Bytes(tmp.Elem().Bytes()), true - } - return NewDynamicList(a, v), true - case reflect.Map: - return NewDynamicMap(a, v), true - // type aliases of primitive types cannot be asserted as that type, but rather need - // to be downcast to int32 before being converted to a CEL representation. - case reflect.Bool: - boolTupe := reflect.TypeOf(false) - return Bool(refValue.Convert(boolTupe).Interface().(bool)), true - case reflect.Int: - intType := reflect.TypeOf(int(0)) - return Int(refValue.Convert(intType).Interface().(int)), true - case reflect.Int8: - intType := reflect.TypeOf(int8(0)) - return Int(refValue.Convert(intType).Interface().(int8)), true - case reflect.Int16: - intType := reflect.TypeOf(int16(0)) - return Int(refValue.Convert(intType).Interface().(int16)), true - case reflect.Int32: - intType := reflect.TypeOf(int32(0)) - return Int(refValue.Convert(intType).Interface().(int32)), true - case reflect.Int64: - intType := reflect.TypeOf(int64(0)) - return Int(refValue.Convert(intType).Interface().(int64)), true - case reflect.Uint: - uintType := reflect.TypeOf(uint(0)) - return Uint(refValue.Convert(uintType).Interface().(uint)), true - case reflect.Uint8: - uintType := reflect.TypeOf(uint8(0)) - return Uint(refValue.Convert(uintType).Interface().(uint8)), true - case reflect.Uint16: - uintType := reflect.TypeOf(uint16(0)) - return Uint(refValue.Convert(uintType).Interface().(uint16)), true - case reflect.Uint32: - uintType := reflect.TypeOf(uint32(0)) - return Uint(refValue.Convert(uintType).Interface().(uint32)), true - case reflect.Uint64: - uintType := reflect.TypeOf(uint64(0)) - return Uint(refValue.Convert(uintType).Interface().(uint64)), true - case reflect.Float32: - doubleType := reflect.TypeOf(float32(0)) - return Double(refValue.Convert(doubleType).Interface().(float32)), true - case reflect.Float64: - doubleType := reflect.TypeOf(float64(0)) - return Double(refValue.Convert(doubleType).Interface().(float64)), true - case reflect.String: - stringType := reflect.TypeOf("") - return String(refValue.Convert(stringType).Interface().(string)), true - } - } - return nil, false + return emptyRegistry.NativeToValue(value) } func msgSetField(target protoreflect.Message, field *pb.FieldDescription, val ref.Val) error { diff --git a/common/types/provider_test.go b/common/types/provider_test.go index f6fcd36b7..2197d38a1 100644 --- a/common/types/provider_test.go +++ b/common/types/provider_test.go @@ -947,6 +947,7 @@ func TestNativeToValue_Primitive(t *testing.T) { {name: "pointer to ref.Val bytes", in: &rBytes, want: rBytes}, // Extensions to core types. + {name: "custom testBool", in: testBool(true), want: True}, {name: "custom testInt", in: testInt(1), want: Int(1)}, {name: "custom testInt8", in: testInt8(1), want: Int(1)}, {name: "custom testInt16", in: testInt16(1), want: Int(1)}, @@ -1362,6 +1363,13 @@ func (d *testStructType) NewValue(adapter Adapter, fields map[string]ref.Val) re } func (d *testStructType) Adapt(adapter Adapter, value any) ref.Val { + if value == nil { + return NullValue + } + refVal := reflect.ValueOf(value) + if refVal.Kind() == reflect.Pointer && refVal.IsNil() { + return NullValue + } return &testStructVal{ adapter: adapter, st: d, From 1a53cb79284be4b96b5c289673d075e7f7377d33 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Wed, 5 Aug 2026 14:11:45 -0700 Subject: [PATCH 11/37] Release Tag Automation for go submodules (#1401) --- .github/workflows/tag-submodules.yml | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/tag-submodules.yml diff --git a/.github/workflows/tag-submodules.yml b/.github/workflows/tag-submodules.yml new file mode 100644 index 000000000..d2edb0678 --- /dev/null +++ b/.github/workflows/tag-submodules.yml @@ -0,0 +1,42 @@ +name: Tag Go Submodules + +on: + release: + types: [published] + +jobs: + tag-submodules: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + fetch-depth: 0 + + - name: Configure Git + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@://github.com" + + - name: Auto-Detect and Tag Submodules + env: + RELEASE_TAG: ${{ github.event.release.tag_name }} + run: | + echo "Finding all subdirectories containing go.mod..." + + # Find all go.mod files, exclude root (./go.mod), and get their directory paths + find . -mindepth 2 -name "go.mod" | while read -r gomod_path; do + # Extract directory path (e.g., ./pkg/submod1/go.mod -> pkg/submod1) + SUBDIR=$(dirname "$gomod_path" | sed 's|^\./||') + + SUB_TAG="${SUBDIR}/${RELEASE_TAG}" + + echo "----------------------------------------" + echo "Found Go module in: $SUBDIR" + echo "Creating tag: $SUB_TAG" + + git tag -a "$SUB_TAG" -m "Release $SUB_TAG via GitHub Action" + git push origin "$SUB_TAG" + done \ No newline at end of file From 9d5baaf22da5b1e59604d0912718331f4716d10c Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Wed, 5 Aug 2026 14:15:44 -0700 Subject: [PATCH 12/37] Program plan optimizations (#1399) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Program plan optimizations | Benchmark Case | Before (ns/op) | After (ns/op) | Δ Time | Before (B/op) | After (B/op) | Δ Memory | Before (allocs) | After (allocs) | Δ Allocs | | :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | | BenchmarkProgramPlan/Default | 8,153 | 930 | **-88.6%** | 8,320 | 1,416 | **-83.0%** | 36 | 26 | **-27.8%** | | BenchmarkProgramPlan/OptimizeUnneeded | 7,344 | 1,150 | **-84.3%** | 8,784 | 1,512 | **-82.8%** | 50 | 32 | **-36.0%** | | BenchmarkProgramPlan/OptimizeNeeded | 8,370 | 2,164 | **-74.1%** | 10,224 | 2,976 | **-70.9%** | 67 | 52 | **-22.4%** | * Minor refactor of the initialization logic to reduce program size * Shift the dispatcher-reuse to the environment rather than the program --- cel/cel_test.go | 51 ++++++++++++++ cel/decls.go | 4 ++ cel/env.go | 156 +++++++++++++++++++++++++++++++----------- cel/library.go | 1 + cel/options.go | 2 + cel/program.go | 90 ++++++++++++++---------- common/decls/decls.go | 26 ++++--- 7 files changed, 243 insertions(+), 87 deletions(-) diff --git a/cel/cel_test.go b/cel/cel_test.go index af6843ca9..713a129ff 100644 --- a/cel/cel_test.go +++ b/cel/cel_test.go @@ -4002,6 +4002,57 @@ func BenchmarkDynamicDispatch(b *testing.B) { }) } +func BenchmarkProgramPlan(b *testing.B) { + env, err := NewEnv( + Variable("ai", IntType), + Variable("ar", MapType(StringType, StringType)), + ) + if err != nil { + b.Fatalf("NewEnv() failed: %v", err) + } + astSimple, iss := env.Compile("ai == 20 || ar['foo'] == 'bar'") + if iss.Err() != nil { + b.Fatalf("env.Compile() failed: %v", iss.Err()) + } + astOpt, iss := env.Compile("ai in [10, 20, 30] || 'foo' in ar") + if iss.Err() != nil { + b.Fatalf("env.Compile() failed: %v", iss.Err()) + } + + b.Run("Default", func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := env.Program(astSimple) + if err != nil { + b.Fatalf("env.Program() failed: %v", err) + } + } + }) + + b.Run("OptimizeUnneeded", func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := env.Program(astSimple, EvalOptions(OptOptimize)) + if err != nil { + b.Fatalf("env.Program() failed: %v", err) + } + } + }) + + b.Run("OptimizeNeeded", func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := env.Program(astOpt, EvalOptions(OptOptimize)) + if err != nil { + b.Fatalf("env.Program() failed: %v", err) + } + } + }) +} + func TestAstProgramNilValue(t *testing.T) { var ast *Ast = nil env := testEnv(t) diff --git a/cel/decls.go b/cel/decls.go index c7c23fd51..553288056 100644 --- a/cel/decls.go +++ b/cel/decls.go @@ -220,6 +220,10 @@ func ExcludeOverloads(overloadIDs ...string) OverloadSelector { // FunctionDecls provides one or more fully formed function declarations to be added to the environment. func FunctionDecls(funcs ...*decls.FunctionDecl) EnvOption { return func(e *Env) (*Env, error) { + if len(funcs) == 0 { + return e, nil + } + e.ensureMutableFunctions() var err error for _, fn := range funcs { if existing, found := e.functions[fn.Name()]; found { diff --git a/cel/env.go b/cel/env.go index 6631417c0..3d0f8d102 100644 --- a/cel/env.go +++ b/cel/env.go @@ -17,6 +17,7 @@ package cel import ( "errors" "fmt" + "maps" "math" "slices" "strings" @@ -150,8 +151,24 @@ type Env struct { validators []ASTValidator costOptions []checker.CostOption - funcBindOnce sync.Once - functionBindings []*functions.Overload + // Flags for copy-on-write behavior with env.Extend. + funcsShared bool + featuresShared bool + appliedFeaturesShared bool + limitsShared bool + libsShared bool + + parent *Env + + // sharedDispatcher caches a dispatcher populated with the env's function + // bindings, built once and reused across every Program() constructed from + // this env. It is read-only after construction; each Program layers a thin + // child over it for per-program Functions(). Extended envs reuse the parent's + // dispatcher if functions are unchanged. + sharedDispatcher interpreter.Dispatcher + dispOnce sync.Once + hasAsync bool + dispErr error // Internal parser representation prsr *parser.Parser @@ -370,20 +387,19 @@ func NewCustomEnv(opts ...EnvOption) (*Env, error) { return nil, err } return (&Env{ - variables: []*decls.VariableDecl{}, - functions: map[string]*decls.FunctionDecl{}, - functionBindings: []*functions.Overload{}, - macros: []parser.Macro{}, - Container: containers.DefaultContainer, - adapter: registry, - provider: registry, - features: map[int]bool{}, - appliedFeatures: map[int]bool{}, - limits: map[limitID]int{}, - libraries: map[string]SingletonLibrary{}, - validators: []ASTValidator{}, - progOpts: []ProgramOption{}, - costOptions: []checker.CostOption{}, + 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{}, + limits: map[limitID]int{}, + libraries: map[string]SingletonLibrary{}, + validators: []ASTValidator{}, + progOpts: []ProgramOption{}, + costOptions: []checker.CostOption{}, }).configure(opts) } @@ -572,26 +588,6 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) { 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 - } - limitsCopy := make(map[limitID]int, len(e.limits)) - for k, v := range e.limits { - limitsCopy[k] = v - } - funcsCopy := make(map[string]*decls.FunctionDecl, len(e.functions)) - for k, v := range e.functions { - funcsCopy[k] = v - } - libsCopy := make(map[string]SingletonLibrary, len(e.libraries)) - for k, v := range e.libraries { - libsCopy[k] = v - } validatorsCopy := make([]ASTValidator, len(e.validators)) copy(validatorsCopy, e.validators) @@ -599,26 +595,68 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) { copy(costOptsCopy, e.costOptions) ext := &Env{ + parent: e, Container: e.Container, variables: varsCopy, - functions: funcsCopy, + functions: e.functions, macros: macsCopy, contextProto: e.contextProto, progOpts: progOptsCopy, adapter: adapter, - features: featuresCopy, - limits: limitsCopy, - appliedFeatures: appliedFeaturesCopy, - libraries: libsCopy, + features: e.features, + limits: e.limits, + appliedFeatures: e.appliedFeatures, + libraries: e.libraries, validators: validatorsCopy, provider: provider, chkOpts: chkOptsCopy, prsrOpts: prsrOptsCopy, costOptions: costOptsCopy, + // Copy-on-write flags. + funcsShared: true, + featuresShared: true, + limitsShared: true, + appliedFeaturesShared: true, + libsShared: true, } return ext.configure(opts) } +func (e *Env) ensureMutableFunctions() { + if e.funcsShared { + e.functions = maps.Clone(e.functions) + e.funcsShared = false + } +} + +func (e *Env) ensureMutableLibraries() { + if e.libsShared { + e.libraries = maps.Clone(e.libraries) + e.libsShared = false + } +} + +func (e *Env) ensureMutableFeatures() { + if e.featuresShared { + e.features = maps.Clone(e.features) + e.featuresShared = false + } +} + +func (e *Env) ensureMutableAppliedFeatures() { + if e.appliedFeaturesShared { + e.appliedFeatures = maps.Clone(e.appliedFeatures) + e.appliedFeaturesShared = false + } +} + +func (e *Env) ensureMutableLimits() { + if e.limitsShared { + e.limits = maps.Clone(e.limits) + e.limitsShared = false + } +} + // HasFeature checks whether the environment enables the given feature // flag, as enumerated in options.go. func (e *Env) HasFeature(flag int) bool { @@ -736,6 +774,41 @@ func (e *Env) PlanProgram(a *celast.AST, opts ...ProgramOption) (Program, error) return newProgram(e, a, optSet) } +func (e *Env) initDispatcher() (interpreter.Dispatcher, bool, error) { + e.dispOnce.Do(func() { + if e.parent != nil && e.funcsShared { + // The dispatcher setup is skipped when the child has mutated the function set. + // As the child function set contains a copy of all parent function declarations + // by virtue of copy on write semantics. + d, hasAsync, err := e.parent.initDispatcher() + e.sharedDispatcher = d + e.hasAsync = hasAsync + e.dispErr = err + return + } + hasAsync := false + var bindings []*functions.Overload + for _, fn := range e.functions { + bs, err := fn.Bindings() + if err != nil { + e.dispErr = err + return + } + for _, b := range bs { + if b.Async != nil { + hasAsync = true + } + } + bindings = append(bindings, bs...) + } + d := interpreter.NewDispatcher() + e.dispErr = d.Add(bindings...) + e.sharedDispatcher = d + e.hasAsync = hasAsync + }) + return e.sharedDispatcher, e.hasAsync, e.dispErr +} + // CELTypeAdapter returns the `types.Adapter` configured for the environment. func (e *Env) CELTypeAdapter() types.Adapter { return e.adapter @@ -861,6 +934,7 @@ func (e *Env) configure(opts []EnvOption) (*Env, error) { // If the default UTC timezone has been disabled, configure the legacy overloads if utcTime, isSet := e.features[featureDefaultUTCTimeZone]; isSet && !utcTime { if !e.appliedFeatures[featureDefaultUTCTimeZone] { + e.ensureMutableAppliedFeatures() e.appliedFeatures[featureDefaultUTCTimeZone] = true e, err = Lib(timeLegacyLibrary{})(e) if err != nil { diff --git a/cel/library.go b/cel/library.go index a14025222..34ca93551 100644 --- a/cel/library.go +++ b/cel/library.go @@ -98,6 +98,7 @@ func Lib(l Library) EnvOption { if e.HasLibrary(singleton.LibraryName()) { return e, nil } + e.ensureMutableLibraries() e.libraries[singleton.LibraryName()] = singleton } var err error diff --git a/cel/options.go b/cel/options.go index 957b1f699..709ce007a 100644 --- a/cel/options.go +++ b/cel/options.go @@ -977,6 +977,7 @@ func DefaultUTCTimeZone(enabled bool) EnvOption { // features sets the given feature flags. See list of Feature constants above. func features(flag int, enabled bool) EnvOption { return func(e *Env) (*Env, error) { + e.ensureMutableFeatures() e.features[flag] = enabled return e, nil } @@ -987,6 +988,7 @@ func setLimit(id limitID, limit int) EnvOption { limit = -1 } return func(e *Env) (*Env, error) { + e.ensureMutableLimits() e.limits[id] = limit return e, nil } diff --git a/cel/program.go b/cel/program.go index c97d99213..07849974a 100644 --- a/cel/program.go +++ b/cel/program.go @@ -22,7 +22,8 @@ import ( "github.com/google/cel-go/cel/async" "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/interpreter" @@ -200,13 +201,45 @@ type prog struct { asyncMaxConcurrency int } +// scanOptTargets walks the AST once and reports whether the Optimize() +// decorator (needOpt) and the regex-constant compiler (needRegex) have any +// target node present. The conditions are a superset of what each decorator +// acts on, so a false negative — skipping a decorator that would have +// optimized something — is impossible. +func scanOptTargets(root ast.Expr) (needOpt, needRegex bool) { + ast.PostOrderVisit(root, ast.NewExprVisitor(func(e ast.Expr) { + switch e.Kind() { + case ast.ListKind, ast.MapKind: + needOpt = true // maybeBuildListLiteral / maybeBuildMapLiteral + case ast.CallKind: + switch fn := e.AsCall().FunctionName(); { + case fn == overloads.Matches: + needRegex = true + case fn == operators.In || fn == operators.OldIn: + needOpt = true // maybeOptimizeSetMembership + case overloads.IsTypeConversionFunction(fn): + needOpt = true // maybeOptimizeConstUnary + } + } + })) + return +} + // newProgram creates a program instance with an environment, an ast, and an optional list of // ProgramOption values. // // If the program cannot be configured the prog will be nil, with a non-nil error response. func newProgram(e *Env, a *ast.AST, opts []ProgramOption) (Program, error) { - // Build the dispatcher, interpreter, and default program value. - disp := interpreter.NewDispatcher() + // Build the env's function bindings and shared dispatcher once (pure functions of the + // env). The dispatcher holding the env's function bindings is identical across every + // Program() built from it and read-only during planning — so assemble it once per env + // and layer a thin child over it here for per-program Functions() isolation, rather than + // re-indexing overloads on every Program() call. + sharedDisp, hasAsync, err := e.initDispatcher() + if err != nil { + return nil, err + } + disp := interpreter.ExtendDispatcher(sharedDisp) // Ensure the default attribute factory is set after the adapter and provider are // configured. @@ -216,10 +249,10 @@ func newProgram(e *Env, a *ast.AST, opts []ProgramOption) (Program, error) { dispatcher: disp, costOptions: []interpreter.CostTrackerOption{}, drainStrategy: async.DrainReady(100 * time.Microsecond), + hasAsync: hasAsync, } // Configure the program via the ProgramOption values. - var err error for _, opt := range opts { p, err = opt(p) if err != nil { @@ -227,38 +260,6 @@ func newProgram(e *Env, a *ast.AST, opts []ProgramOption) (Program, error) { } } - e.funcBindOnce.Do(func() { - var bindings []*functions.Overload - e.functionBindings = []*functions.Overload{} - for _, fn := range e.functions { - bindings, err = fn.Bindings() - if err != nil { - return - } - e.functionBindings = append(e.functionBindings, bindings...) - } - }) - if err != nil { - return nil, err - } - - // Add the function bindings created via Function() options. - err = disp.Add(e.functionBindings...) - if err != nil { - return nil, err - } - - // Determine whether the environment declares any asynchronous function. Async is a property of - // the binding, so its presence is known from the environment alone, without inspecting the - // program plan. The synchronous entry points (Eval, ContextEval) reject programs from an env - // with async functions; callers needing synchronous evaluation should use a non-async env. - for _, b := range e.functionBindings { - if b.Async != nil { - p.hasAsync = true - break - } - } - // Set the attribute factory after the options have been set. var attrFactory interpreter.AttributeFactory attrFactorOpts := []interpreter.AttrFactoryOption{ @@ -288,8 +289,21 @@ func newProgram(e *Env, a *ast.AST, opts []ProgramOption) (Program, error) { } // Enable constant folding first. if p.evalOpts&OptOptimize == OptOptimize { - plannerOptions = append(plannerOptions, interpreter.Optimize()) - p.regexOptimizations = append(p.regexOptimizations, interpreter.MatchesRegexOptimization) + // The Optimize() decorator (set-membership, constant list/map literals, + // const type conversions) and the regex-constant compiler each walk + // every planned node. When the AST provably contains no node they can + // act on, adding them is pure overhead — so gate each on a single AST + // scan. The scan condition is a superset of what the decorators touch, + // so a decorator is only skipped when its target node is definitely + // absent and evaluation is never affected (an ungated regex would + // recompile per eval, etc.). + addOptimize, addRegex := scanOptTargets(a.Expr()) + if addOptimize { + plannerOptions = append(plannerOptions, interpreter.Optimize()) + } + if addRegex { + p.regexOptimizations = append(p.regexOptimizations, interpreter.MatchesRegexOptimization) + } } // Enable regex compilation of constants immediately after folding constants. if len(p.regexOptimizations) > 0 { diff --git a/common/decls/decls.go b/common/decls/decls.go index 51cb689e5..5c50b8e99 100644 --- a/common/decls/decls.go +++ b/common/decls/decls.go @@ -78,6 +78,9 @@ type FunctionDecl struct { // overloadOrdinals indicates the order in which the overload was declared. overloadOrdinals []string + + // overloadDecls caches the slice of overloads in declaration order. + overloadDecls []*OverloadDecl } type declarationState int @@ -151,7 +154,8 @@ func (f *FunctionDecl) Merge(other *FunctionDecl) (*FunctionDecl, error) { name: f.Name(), overloads: make(map[string]*OverloadDecl, len(f.overloads)), singleton: f.singleton, - overloadOrdinals: make([]string, len(f.overloads)), + overloadOrdinals: make([]string, len(f.overloadOrdinals)), + overloadDecls: make([]*OverloadDecl, len(f.overloadDecls)), // if one function is expecting type-guards and the other is not, then they // must not be disabled. disableTypeGuards: f.disableTypeGuards && other.disableTypeGuards, @@ -170,6 +174,7 @@ func (f *FunctionDecl) Merge(other *FunctionDecl) (*FunctionDecl, error) { } // baseline copy of the overloads and their ordinals copy(merged.overloadOrdinals, f.overloadOrdinals) + copy(merged.overloadDecls, f.overloadDecls) for oID, o := range f.overloads { merged.overloads[oID] = o } @@ -232,11 +237,13 @@ func (f *FunctionDecl) Subset(selector OverloadSelector) *FunctionDecl { } overloads := make(map[string]*OverloadDecl) overloadOrdinals := make([]string, 0, len(f.overloadOrdinals)) + overloadDecls := make([]*OverloadDecl, 0, len(f.overloadDecls)) for _, oID := range f.overloadOrdinals { overload := f.overloads[oID] if selector(overload) { overloads[oID] = overload overloadOrdinals = append(overloadOrdinals, oID) + overloadDecls = append(overloadDecls, overload) } } if len(overloads) == 0 { @@ -250,6 +257,7 @@ func (f *FunctionDecl) Subset(selector OverloadSelector) *FunctionDecl { disableTypeGuards: f.disableTypeGuards, state: f.state, overloadOrdinals: overloadOrdinals, + overloadDecls: overloadDecls, } return subset } @@ -273,6 +281,12 @@ func (f *FunctionDecl) AddOverload(overload *OverloadDecl) error { // Allow redefinition of an overload implementation so long as the signatures match. if overload.HasBinding() { f.overloads[oID] = overload + for i, decl := range f.overloadDecls { + if decl.ID() == oID { + f.overloadDecls[i] = overload + break + } + } } // Allow redefinition of the doc string. if len(overload.doc) != 0 && o.doc != overload.doc { @@ -288,20 +302,16 @@ func (f *FunctionDecl) AddOverload(overload *OverloadDecl) error { } f.overloadOrdinals = append(f.overloadOrdinals, overload.ID()) f.overloads[overload.ID()] = overload + f.overloadDecls = append(f.overloadDecls, overload) return nil } // OverloadDecls returns the overload declarations in the order in which they were declared. func (f *FunctionDecl) OverloadDecls() []*OverloadDecl { - var emptySet []*OverloadDecl if f == nil { - return emptySet - } - overloads := make([]*OverloadDecl, 0, len(f.overloads)) - for _, oID := range f.overloadOrdinals { - overloads = append(overloads, f.overloads[oID]) + return nil } - return overloads + return f.overloadDecls } // HasSingletonBinding indicates whether the function has a singleton binding definition. From b74d3036464a222fb1ea1f1c7c89cdf2a6499dde Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Fri, 7 Aug 2026 10:01:16 -0700 Subject: [PATCH 13/37] Fold list concat expressions together (#1406) * Fold list concat expressions together * Test cases for optional tracking --- cel/folding.go | 27 ++++++++++++++++++++++++++- cel/folding_test.go | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/cel/folding.go b/cel/folding.go index 5525f0805..e3459034c 100644 --- a/cel/folding.go +++ b/cel/folding.go @@ -175,7 +175,7 @@ func evaluateExpr(ctx *OptimizerContext, a *ast.AST, navigableExpr ast.Navigable } prg, err := ctx.Program(subAST) if err != nil { - return nil, err + return nil, errCannotFold } // Folding will not attempt to call async functions which are all marked as late-bound, // but the presence of such functions requires the use of `ConcurrentEval` in order to @@ -244,6 +244,26 @@ func maybePruneBranches(ctx *OptimizerContext, a *ast.AST, expr ast.NavigableExp } } } + case operators.Add: + if len(args) == 2 && args[0].Kind() == ast.ListKind && args[1].Kind() == ast.ListKind { + leftList := args[0].AsList() + rightList := args[1].AsList() + + elems := make([]ast.Expr, 0, leftList.Size()+rightList.Size()) + elems = append(elems, leftList.Elements()...) + elems = append(elems, rightList.Elements()...) + + optIndices := make([]int32, 0, len(leftList.OptionalIndices())+len(rightList.OptionalIndices())) + optIndices = append(optIndices, leftList.OptionalIndices()...) + offset := int32(leftList.Size()) + for _, idx := range rightList.OptionalIndices() { + optIndices = append(optIndices, offset+idx) + } + + combinedList := ctx.NewList(elems, optIndices) + ctx.UpdateExpr(expr, combinedList) + return true + } } return false } @@ -630,6 +650,11 @@ func constantCallMatcher(e ast.NavigableExpr) bool { } } } + if fnName == operators.Add { + if len(children) == 2 && children[0].Kind() == ast.ListKind && children[1].Kind() == ast.ListKind { + return true + } + } // convert all other calls with constant arguments for _, child := range children { if !constantMatcher(child) { diff --git a/cel/folding_test.go b/cel/folding_test.go index 56dd789d4..8a292cb73 100644 --- a/cel/folding_test.go +++ b/cel/folding_test.go @@ -45,6 +45,38 @@ func TestConstantFoldingOptimizer(t *testing.T) { expr: `[1, 1 + 2, 1 + (2 + 3)]`, folded: `[1, 3, 6]`, }, + { + expr: `[1, 2] + [3, 4]`, + folded: `[1, 2, 3, 4]`, + }, + { + expr: `[1, ?optional.of(2)] + [3, 4]`, + folded: `[1, 2, 3, 4]`, + }, + { + expr: `[1, ?optional.none()] + [2]`, + folded: `[1, 2]`, + }, + { + expr: `[x, 1] + [2, y]`, + folded: `[x, 1, 2, y]`, + }, + { + expr: `[x, ?optional.of(1)] + [?optional.of(2), y]`, + folded: `[x, 1, 2, y]`, + }, + { + expr: `[1] + [x] + [2]`, + folded: `[1, x, 2]`, + }, + { + expr: `[1] + [?x] + [2]`, + folded: `[1, ?x, 2]`, + }, + { + expr: `[?x, 1] + [2, ?y]`, + folded: `[?x, 1, 2, ?y]`, + }, { expr: `6 in [1, 1 + 2, 1 + (2 + 3)]`, folded: `true`, @@ -516,7 +548,7 @@ func TestConstantFoldingOptimizer(t *testing.T) { }, { expr: `[1] + [x]`, - folded: `[1] + [x]`, + folded: `[1, x]`, }, { From 2cf1626d370454e40dff68b977f27382c59f170a Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Fri, 7 Aug 2026 13:32:57 -0700 Subject: [PATCH 14/37] Env copy on write (#1405) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Program plan optimizations | Benchmark Case | Before (ns/op) | After (ns/op) | Δ Time | Before (B/op) | After (B/op) | Δ Memory | Before (allocs) | After (allocs) | Δ Allocs | | :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | | BenchmarkProgramPlan/Default | 8,153 | 930 | **-88.6%** | 8,320 | 1,416 | **-83.0%** | 36 | 26 | **-27.8%** | | BenchmarkProgramPlan/OptimizeUnneeded | 7,344 | 1,150 | **-84.3%** | 8,784 | 1,512 | **-82.8%** | 50 | 32 | **-36.0%** | | BenchmarkProgramPlan/OptimizeNeeded | 8,370 | 2,164 | **-74.1%** | 10,224 | 2,976 | **-70.9%** | 67 | 52 | **-22.4%** | * Minor refactor of the initialization logic to reduce program size * Copy-on-write semantics for types.Registry and cel.Env internals * Ensure shared declarations aren't copied unless necessary within the checker * Capture NewEnv setup benchmarks as well * Fix race-related issue with copy-on-write mutability check * Eliminate dead-code from former Copy() approach. More tests * Bug fix to support disabling declarations when using inherited declarations --- cel/cel_test.go | 121 ++++++++++++++++++- cel/decls.go | 2 +- cel/env.go | 84 +++++--------- cel/env_test.go | 141 ++++++++++++++++++++++ cel/library.go | 3 + checker/checker_test.go | 47 ++++++++ checker/env.go | 2 +- checker/env_test.go | 14 --- checker/options.go | 5 +- checker/scopes.go | 73 +++++------- common/types/provider.go | 40 +++++-- common/types/provider_test.go | 212 ++++++++++++++++++++++++++++++++++ 12 files changed, 616 insertions(+), 128 deletions(-) diff --git a/cel/cel_test.go b/cel/cel_test.go index 713a129ff..7cb32afb0 100644 --- a/cel/cel_test.go +++ b/cel/cel_test.go @@ -95,6 +95,90 @@ func Test_ExampleWithBuiltins(t *testing.T) { } } +func TestExtendCheckerParity(t *testing.T) { + // Base environment carrying standard library functions + baseEnv, err := NewEnv( + Variable("baseVar", StringType), + ) + if err != nil { + t.Fatalf("NewEnv() failed: %v", err) + } + + // Extended environment adding child variables (K8s CRD pattern) + extEnv, err := baseEnv.Extend( + Variable("value", StringType), + Variable("oldValue", StringType), + ) + if err != nil { + t.Fatalf("baseEnv.Extend() failed: %v", err) + } + + // Equivalent flat environment created from scratch + flatEnv, err := NewEnv( + Variable("baseVar", StringType), + Variable("value", StringType), + Variable("oldValue", StringType), + ) + if err != nil { + t.Fatalf("flat NewEnv() failed: %v", err) + } + + testCases := []struct { + expr string + vars map[string]any + want ref.Val + }{ + { + expr: `value + " " + oldValue + " " + baseVar`, + vars: map[string]any{"value": "new", "oldValue": "old", "baseVar": "base"}, + want: types.String("new old base"), + }, + { + expr: `size(value) > 0 && [1, 2, 3].exists(x, x > 2)`, + vars: map[string]any{"value": "test"}, + want: types.True, + }, + } + + for _, tc := range testCases { + extAst, extIss := extEnv.Compile(tc.expr) + if extIss.Err() != nil { + t.Fatalf("extEnv.Compile(%q) failed: %v", tc.expr, extIss.Err()) + } + flatAst, flatIss := flatEnv.Compile(tc.expr) + if flatIss.Err() != nil { + t.Fatalf("flatEnv.Compile(%q) failed: %v", tc.expr, flatIss.Err()) + } + + if extAst.OutputType().TypeName() != flatAst.OutputType().TypeName() { + t.Errorf("OutputType mismatch for %q: ext %v, flat %v", tc.expr, extAst.OutputType(), flatAst.OutputType()) + } + + extPrg, err := extEnv.Program(extAst) + if err != nil { + t.Fatalf("extEnv.Program() failed: %v", err) + } + flatPrg, err := flatEnv.Program(flatAst) + if err != nil { + t.Fatalf("flatEnv.Program() failed: %v", err) + } + + extOut, _, err := extPrg.Eval(tc.vars) + if err != nil { + t.Fatalf("extPrg.Eval() failed: %v", err) + } + flatOut, _, err := flatPrg.Eval(tc.vars) + if err != nil { + t.Fatalf("flatPrg.Eval() failed: %v", err) + } + + if extOut.Equal(tc.want) != types.True || flatOut.Equal(tc.want) != types.True { + t.Errorf("Eval result mismatch for %q: ext %v, flat %v, want %v", tc.expr, extOut, flatOut, tc.want) + } + } +} + + func TestCompile(t *testing.T) { prg, err := Compile(`"hello " + name`, Variable("name", StringType)) if err != nil { @@ -4003,12 +4087,45 @@ func BenchmarkDynamicDispatch(b *testing.B) { } func BenchmarkProgramPlan(b *testing.B) { - env, err := NewEnv( + b.Run("NewEnv", func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := NewEnv( + Variable("ai", IntType), + Variable("ar", MapType(StringType, StringType)), + ) + if err != nil { + b.Fatalf("NewEnv() failed: %v", err) + } + } + }) + + baseEnv, err := NewEnv() + if err != nil { + b.Fatalf("NewEnv() failed: %v", err) + } + + b.Run("ExtendEnv", func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := baseEnv.Extend( + Variable("ai", IntType), + Variable("ar", MapType(StringType, StringType)), + ) + if err != nil { + b.Fatalf("baseEnv.Extend() failed: %v", err) + } + } + }) + + env, err := baseEnv.Extend( Variable("ai", IntType), Variable("ar", MapType(StringType, StringType)), ) if err != nil { - b.Fatalf("NewEnv() failed: %v", err) + b.Fatalf("Extend() failed: %v", err) } astSimple, iss := env.Compile("ai == 20 || ar['foo'] == 'bar'") if iss.Err() != nil { diff --git a/cel/decls.go b/cel/decls.go index 553288056..53941146d 100644 --- a/cel/decls.go +++ b/cel/decls.go @@ -223,8 +223,8 @@ func FunctionDecls(funcs ...*decls.FunctionDecl) EnvOption { if len(funcs) == 0 { return e, nil } - e.ensureMutableFunctions() var err error + e.ensureMutableFunctions() for _, fn := range funcs { if existing, found := e.functions[fn.Name()]; found { fn, err = existing.Merge(fn) diff --git a/cel/env.go b/cel/env.go index 3d0f8d102..794c62f78 100644 --- a/cel/env.go +++ b/cel/env.go @@ -532,34 +532,17 @@ func (e *Env) CompileSource(src Source) (*Ast, *Issues) { // 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 { + if _, chkErr := e.getCheckerOrError(); 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. - 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)) - } - 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) + prsrOptsCopy := slices.Clone(e.prsrOpts) + chkOptsCopy := slices.Clone(e.chkOpts) + varsCopy := slices.Clone(e.variables) + macsCopy := slices.Clone(e.macros) + progOptsCopy := slices.Clone(e.progOpts) + validatorsCopy := slices.Clone(e.validators) + costOptsCopy := slices.Clone(e.costOptions) // Copy the adapter / provider if they appear to be mutable. adapter := e.adapter @@ -588,12 +571,6 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) { adapter = adapterReg.Copy() } - validatorsCopy := make([]ASTValidator, len(e.validators)) - copy(validatorsCopy, e.validators) - - costOptsCopy := make([]checker.CostOption, len(e.costOptions)) - copy(costOptsCopy, e.costOptions) - ext := &Env{ parent: e, Container: e.Container, @@ -687,25 +664,17 @@ func (e *Env) HasFunction(functionName string) bool { // Functions returns a shallow copy of the Functions, keyed by function name, that have been configured in the environment. func (e *Env) Functions() map[string]*decls.FunctionDecl { - shallowCopy := make(map[string]*decls.FunctionDecl, len(e.functions)) - for nm, fn := range e.functions { - shallowCopy[nm] = fn - } - return shallowCopy + return maps.Clone(e.functions) } // Variables returns a shallow copy of the variables associated with the environment. func (e *Env) Variables() []*decls.VariableDecl { - shallowCopy := make([]*decls.VariableDecl, len(e.variables)) - copy(shallowCopy, e.variables) - return shallowCopy + return slices.Clone(e.variables) } // Macros returns a shallow copy of macros associated with the environment. func (e *Env) Macros() []Macro { - shallowCopy := make([]Macro, len(e.macros)) - copy(shallowCopy, e.macros) - return shallowCopy + return slices.Clone(e.macros) } // HasValidator returns whether a specific ASTValidator has been configured in the environment. @@ -718,9 +687,9 @@ func (e *Env) HasValidator(name string) bool { return false } -// Validators returns the set of ASTValidators configured on the environment. +// Validators returns a shallow copy of the set of ASTValidators configured on the environment. func (e *Env) Validators() []ASTValidator { - return e.validators[:] + return slices.Clone(e.validators) } // Parse parses the input expression value `txt` to a Ast and/or a set of Issues. @@ -1007,6 +976,15 @@ func (e *Env) initChecker() (*checker.Env, error) { chkOpts = append(chkOpts, checker.JSONFieldNames(e.HasFeature(featureJSONFieldNames))) + if e.parent != nil && e.funcsShared { + parentChk, err := e.parent.initChecker() + if err != nil { + e.setCheckerOrError(nil, err) + return + } + chkOpts = append(chkOpts, checker.ValidatedDeclarations(parentChk)) + } + ce, err := checker.NewEnv(e.Container, e.provider, chkOpts...) if err != nil { e.setCheckerOrError(nil, err) @@ -1019,14 +997,16 @@ func (e *Env) initChecker() (*checker.Env, error) { 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 + if e.parent == nil || !e.funcsShared { + 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. diff --git a/cel/env_test.go b/cel/env_test.go index 4957011ee..004ab9f0a 100644 --- a/cel/env_test.go +++ b/cel/env_test.go @@ -164,6 +164,37 @@ func TestFormatCELTypeEquivalence(t *testing.T) { } } +func TestEnvExtendDisableDeclaration(t *testing.T) { + baseEnv, err := NewCustomEnv( + Function("foo", + Overload("foo_bool", []*Type{BoolType}, BoolType), + ), + ) + if err != nil { + t.Fatalf("NewCustomEnv() failed: %v", err) + } + _, iss := baseEnv.Compile("foo(true)") + if iss.Err() != nil { + t.Fatalf("baseEnv.Compile(foo(true)) failed: %v", iss.Err()) + } + + childEnv, err := baseEnv.Extend( + Function("foo", + DisableDeclaration(true), + Overload("foo_bool", []*Type{BoolType}, BoolType), + ), + ) + if err != nil { + t.Fatalf("baseEnv.Extend() failed: %v", err) + } + + _, iss = childEnv.Compile("foo(true)") + if iss.Err() == nil { + t.Errorf("childEnv.Compile(foo(true)) succeeded, wanted error") + } +} + + func TestEnvCheckExtendRace(t *testing.T) { t.Parallel() for i := 0; i < 500; i++ { @@ -189,6 +220,116 @@ func TestEnvCheckExtendRace(t *testing.T) { } } +func TestEnvConcurrentExtend(t *testing.T) { + t.Parallel() + baseEnv, err := NewCustomEnv(StdLib()) + if err != nil { + t.Fatalf("NewCustomEnv() failed: %v", err) + } + var wg sync.WaitGroup + for i := 0; i < 50; i++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + _, err := baseEnv.Extend(Variable(fmt.Sprintf("v%d", id), StringType)) + if err != nil { + t.Errorf("Extend() failed: %v", err) + } + }(i) + } + wg.Wait() +} + +func TestEnvConcurrentExtendAndCompile(t *testing.T) { + t.Parallel() + baseEnv, err := NewCustomEnv(StdLib()) + if err != nil { + t.Fatalf("NewCustomEnv() failed: %v", err) + } + var wg sync.WaitGroup + for i := 0; i < 50; i++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + varName := fmt.Sprintf("v%d", id) + extEnv, err := baseEnv.Extend(Variable(varName, IntType)) + if err != nil { + t.Errorf("Extend() failed: %v", err) + return + } + ast, iss := extEnv.Compile(fmt.Sprintf("%s > 0", varName)) + if iss.Err() != nil { + t.Errorf("Compile() failed: %v", iss.Err()) + return + } + prg, err := extEnv.Program(ast) + if err != nil { + t.Errorf("Program() failed: %v", err) + return + } + out, _, err := prg.Eval(map[string]any{varName: 10}) + if err != nil { + t.Errorf("Eval() failed: %v", err) + return + } + if out.Value() != true { + t.Errorf("got %v, wanted true", out.Value()) + } + }(i) + } + wg.Wait() +} + +func TestEnvConcurrentExtendWithMutation(t *testing.T) { + t.Parallel() + baseEnv, err := NewCustomEnv(StdLib()) + if err != nil { + t.Fatalf("NewCustomEnv() failed: %v", err) + } + var wg sync.WaitGroup + for i := 0; i < 50; i++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + fnName := fmt.Sprintf("custom_func_%d", id) + extEnv, err := baseEnv.Extend( + Function(fnName, + Overload(fnName+"_int", []*Type{IntType}, IntType, + UnaryBinding(func(val ref.Val) ref.Val { + return val + }), + ), + ), + ) + if err != nil { + t.Errorf("Extend() failed: %v", err) + return + } + ast, iss := extEnv.Compile(fmt.Sprintf("%s(42) == 42", fnName)) + if iss.Err() != nil { + t.Errorf("Compile() failed: %v", iss.Err()) + return + } + prg, err := extEnv.Program(ast) + if err != nil { + t.Errorf("Program() failed: %v", err) + return + } + out, _, err := prg.Eval(NoVars()) + if err != nil { + t.Errorf("Eval() failed: %v", err) + return + } + if out.Value() != true { + t.Errorf("got %v, wanted true", out.Value()) + } + }(i) + } + wg.Wait() +} + + + func TestEnvPartialVarsError(t *testing.T) { env := testEnv(t) _, err := env.PartialVars(10) diff --git a/cel/library.go b/cel/library.go index 34ca93551..43912eaae 100644 --- a/cel/library.go +++ b/cel/library.go @@ -184,6 +184,9 @@ func (lib *stdLibrary) CompileOptions() []EnvOption { if err = lib.subset.Validate(); err != nil { return nil, err } + if len(funcs) > 0 { + e.ensureMutableFunctions() + } for _, fn := range funcs { existing, found := e.functions[fn.Name()] if found { diff --git a/checker/checker_test.go b/checker/checker_test.go index be84032f2..b61a226cb 100644 --- a/checker/checker_test.go +++ b/checker/checker_test.go @@ -2870,3 +2870,50 @@ func testFunction(t testing.TB, name string, opts ...decls.FunctionOpt) *decls.F } return fn } + +func TestVarsInheritance(t *testing.T) { + // Parent environment containing inherited variables 'y' and 'x' + parentEnv, err := NewEnv(containers.DefaultContainer, newTestRegistry(t)) + if err != nil { + t.Fatalf("NewEnv() failed: %v", err) + } + err = parentEnv.AddFunctions(stdlib.Functions()...) + if err != nil { + t.Fatalf("parentEnv.AddFunctions() failed: %v", err) + } + err = parentEnv.AddIdents(decls.NewVariable("z", types.IntType)) + if err != nil { + t.Fatalf("parentEnv.AddIdents() failed: %v", err) + } + + // Child environment inheriting declarations from parentEnv + childEnv, err := NewEnv(containers.DefaultContainer, newTestRegistry(t), ValidatedDeclarations(parentEnv)) + if err != nil { + t.Fatalf("NewEnv(ValidatedDeclarations) failed: %v", err) + } + err = childEnv.AddIdents(decls.NewVariable("y", types.NewListType(types.IntType))) + if err != nil { + t.Fatalf("childEnv.AddIdents() failed: %v", err) + } + + src := common.NewTextSource(`y + [1, 2, 3].filter(x, .z > x)`) + p, err := parser.NewParser(parser.Macros(parser.AllMacros...)) + if err != nil { + t.Fatalf("parser.NewParser() failed: %v", err) + } + parsedAst, iss := p.Parse(src) + if len(iss.GetErrors()) > 0 { + t.Fatalf("parser.Parse() failed: %v", iss.ToDisplayString()) + } + + checkedAst, iss := Check(parsedAst, src, childEnv) + if len(iss.GetErrors()) > 0 { + t.Fatalf("Check() failed: %v", iss.ToDisplayString()) + } + + wantType := types.NewListType(types.IntType) + gotType := checkedAst.GetType(checkedAst.Expr().ID()) + if !gotType.IsExactType(wantType) { + t.Errorf("got result type %v, wanted %v", gotType, wantType) + } +} diff --git a/checker/env.go b/checker/env.go index 477918c48..9c3de9517 100644 --- a/checker/env.go +++ b/checker/env.go @@ -97,7 +97,7 @@ func NewEnv(container *containers.Container, provider types.Provider, opts ...Op filteredOverloadIDs = make(map[string]struct{}) } if envOptions.validatedDeclarations != nil { - declarations = envOptions.validatedDeclarations.Copy() + declarations = envOptions.validatedDeclarations.PushInherited() } return &Env{ container: container, diff --git a/checker/env_test.go b/checker/env_test.go index 2ec7f13fa..c3a6aa353 100644 --- a/checker/env_test.go +++ b/checker/env_test.go @@ -77,20 +77,6 @@ func BenchmarkNewStdEnv(b *testing.B) { } } -func BenchmarkCopyDeclarations(b *testing.B) { - env, err := NewEnv(containers.DefaultContainer, newTestRegistry(b)) - if err != nil { - b.Fatalf("NewEnv() failed: %v", err) - } - err = env.AddFunctions(stdlib.Functions()...) - if err != nil { - b.Fatalf("env.AddFunctions(stdlib.Functions()...) failed: %v", err) - } - for i := 0; i < b.N; i++ { - env.validatedDeclarations().Copy() - } -} - func newStdEnv(t *testing.T) *Env { t.Helper() env, err := NewEnv(containers.DefaultContainer, newTestRegistry(t)) diff --git a/checker/options.go b/checker/options.go index af714323b..10d3bcbc0 100644 --- a/checker/options.go +++ b/checker/options.go @@ -33,8 +33,8 @@ func CrossTypeNumericComparisons(enabled bool) Option { } } -// ValidatedDeclarations provides a references to validated declarations which will be copied -// into new checker instances. +// ValidatedDeclarations provides a reference to validated declarations which will be inherited +// as a parent scope without copying. func ValidatedDeclarations(env *Env) Option { return func(opts *options) error { opts.validatedDeclarations = env.validatedDeclarations() @@ -49,3 +49,4 @@ func JSONFieldNames(enabled bool) Option { return nil } } + diff --git a/checker/scopes.go b/checker/scopes.go index 9ae9832e1..2fcfdf29e 100644 --- a/checker/scopes.go +++ b/checker/scopes.go @@ -25,8 +25,9 @@ import ( // Each Groups value is a mapping of names to Decls in the ident and function namespaces. // Lookups are performed such that bindings in inner scopes shadow those in outer scopes. type Scopes struct { - parent *Scopes - scopes *Group + parent *Scopes + inherited *Scopes + scopes *Group } // newScopes creates a new, empty Scopes. @@ -37,19 +38,6 @@ func newScopes() *Scopes { } } -// Copy creates a copy of the current Scopes values, including a copy of its parent if non-nil. -func (s *Scopes) Copy() *Scopes { - cpy := newScopes() - if s == nil { - return cpy - } - if s.parent != nil { - cpy.parent = s.parent.Copy() - } - cpy.scopes = s.scopes.copy() - return cpy -} - // Push creates a new Scopes value which references the current Scope as its parent. func (s *Scopes) Push() *Scopes { return &Scopes{ @@ -58,6 +46,14 @@ func (s *Scopes) Push() *Scopes { } } +// PushInherited creates a new Scopes value which references the current Scope as its inherited parent. +func (s *Scopes) PushInherited() *Scopes { + return &Scopes{ + inherited: s, + scopes: newGroup(), + } +} + // Pop returns the parent Scopes value for the current scope, or the current scope if the parent // is nil. func (s *Scopes) Pop() *Scopes { @@ -74,20 +70,6 @@ func (s *Scopes) AddIdent(decl *decls.VariableDecl) { s.scopes.idents[decl.Name()] = decl } -// FindIdent finds the first ident Decl with a matching name in Scopes, or nil if one cannot be -// found. -// Note: The search is performed from innermost to outermost. -func (s *Scopes) FindIdent(name string) *decls.VariableDecl { - name = strings.TrimPrefix(name, ".") - if ident, found := s.scopes.idents[name]; found { - return ident - } - if s.parent != nil { - return s.parent.FindIdent(name) - } - return nil -} - // FindIdentInScope finds the first ident Decl with a matching name in the current Scopes value, or // nil if one does not exist. // Note: The search is only performed on the current scope and does not search outer scopes. @@ -116,7 +98,13 @@ func (s *Scopes) FindGlobalIdent(name string) *decls.VariableDecl { for scope.parent != nil { scope = scope.parent } - return scope.FindIdentInScope(name) + if ident := scope.FindIdentInScope(name); ident != nil { + return ident + } + if scope.inherited != nil { + return scope.inherited.FindGlobalIdent(name) + } + return nil } // SetFunction adds the function Decl to the current scope. @@ -134,7 +122,14 @@ func (s *Scopes) FindFunction(name string) *decls.FunctionDecl { return fn } if s.parent != nil { - return s.parent.FindFunction(name) + if fn := s.parent.FindFunction(name); fn != nil { + return fn + } + } + if s.inherited != nil { + if fn := s.inherited.FindFunction(name); fn != nil { + return fn + } } return nil } @@ -147,22 +142,6 @@ type Group struct { functions map[string]*decls.FunctionDecl } -// copy creates a new Group instance with a shallow copy of the variables and functions. -// If callers need to mutate the exprpb.Decl definitions for a Function, they should copy-on-write. -func (g *Group) copy() *Group { - cpy := &Group{ - idents: make(map[string]*decls.VariableDecl, len(g.idents)), - functions: make(map[string]*decls.FunctionDecl, len(g.functions)), - } - for n, id := range g.idents { - cpy.idents[n] = id - } - for n, fn := range g.functions { - cpy.functions[n] = fn - } - return cpy -} - // newGroup creates a new Group with empty maps for identifiers and functions. func newGroup() *Group { return &Group{ diff --git a/common/types/provider.go b/common/types/provider.go index 76285143a..2321828d8 100644 --- a/common/types/provider.go +++ b/common/types/provider.go @@ -18,6 +18,7 @@ import ( "fmt" "maps" "reflect" + "sync/atomic" "time" "google.golang.org/protobuf/proto" @@ -92,6 +93,7 @@ type Registry struct { revTypeMap map[string]*Type structTypes map[string]StructTypeDescriptor reflectTypes map[reflect.Type]StructTypeDescriptor + shared atomic.Bool pbdb *pb.Db provider Provider adapter Adapter @@ -218,15 +220,31 @@ func ComposeTypes(provider Provider, adapter Adapter, types ...any) (Provider, A // Copy copies the current state of the registry into its own memory space. func (p *Registry) Copy() *Registry { - copy := NewEmptyRegistry() - copy.pbdb = p.pbdb.Copy() - copy.provider = p.provider - copy.adapter = p.adapter - copy.nativeOptions = p.nativeOptions - maps.Copy(copy.revTypeMap, p.revTypeMap) - maps.Copy(copy.structTypes, p.structTypes) - maps.Copy(copy.reflectTypes, p.reflectTypes) - return copy + if p == nil { + return nil + } + p.shared.Store(true) + cpy := &Registry{ + revTypeMap: p.revTypeMap, + structTypes: p.structTypes, + reflectTypes: p.reflectTypes, + nativeOptions: p.nativeOptions, + pbdb: p.pbdb, + provider: p.provider, + adapter: p.adapter, + } + cpy.shared.Store(true) + return cpy +} + +func (p *Registry) ensureMutable() { + if p.shared.Load() { + p.revTypeMap = maps.Clone(p.revTypeMap) + p.structTypes = maps.Clone(p.structTypes) + p.reflectTypes = maps.Clone(p.reflectTypes) + p.pbdb = p.pbdb.Copy() + p.shared.Store(false) + } } // JSONFieldNames returns whether json field names are enabled in this registry. @@ -239,6 +257,7 @@ func (p *Registry) WithJSONFieldNames(enabled bool) error { if enabled == p.pbdb.JSONFieldNames() { return nil } + p.ensureMutable() newDB := pb.NewDb(pb.JSONFieldNames(enabled)) files := p.pbdb.FileDescriptions() for _, fd := range files { @@ -447,6 +466,7 @@ func (p *Registry) NewValue(structType string, fields map[string]ref.Val) ref.Va // RegisterDescriptor registers the contents of a protocol buffer `FileDescriptor`. func (p *Registry) RegisterDescriptor(fileDesc protoreflect.FileDescriptor) error { + p.ensureMutable() fd, err := p.pbdb.RegisterDescriptor(fileDesc) if err != nil { return err @@ -456,6 +476,7 @@ func (p *Registry) RegisterDescriptor(fileDesc protoreflect.FileDescriptor) erro // RegisterMessage registers a protocol buffer message and its dependencies. func (p *Registry) RegisterMessage(message proto.Message) error { + p.ensureMutable() fd, err := p.pbdb.RegisterMessage(message) if err != nil { return err @@ -488,6 +509,7 @@ func (p *Registry) RegisterType(types ...ref.Type) error { continue } + p.ensureMutable() typeName := t.TypeName() p.revTypeMap[typeName] = celType if st, ok := t.(StructTypeDescriptor); ok { diff --git a/common/types/provider_test.go b/common/types/provider_test.go index 2197d38a1..65f695b11 100644 --- a/common/types/provider_test.go +++ b/common/types/provider_test.go @@ -20,6 +20,7 @@ import ( "reflect" "sort" "strings" + "sync" "testing" "time" @@ -60,8 +61,219 @@ func TestRegistryCopy(t *testing.T) { } }) } + + t.Run("nil registry", func(t *testing.T) { + var reg *Registry + if reg.Copy() != nil { + t.Error("expected nil registry copy to return nil") + } + }) +} + +func assertShared(t *testing.T, reg *Registry) { + t.Helper() + if !reg.shared.Load() { + t.Errorf("registry.shared = false, want true") + } +} + +func assertUnshared(t *testing.T, reg *Registry) { + t.Helper() + if reg.shared.Load() { + t.Errorf("registry.shared = true, want false") + } +} + +func newSharedRegistryPair(t *testing.T, opts ...RegistryOption) (*Registry, *Registry) { + t.Helper() + reg := newTestRegistry(t, opts...) + copied := reg.Copy() + assertShared(t, reg) + assertShared(t, copied) + return reg, copied +} + +func TestRegistrySharedOnCopy(t *testing.T) { + reg := NewEmptyRegistry() + assertUnshared(t, reg) + + copied := reg.Copy() + assertShared(t, reg) + assertShared(t, copied) + + if !reflect.DeepEqual(reg, copied) { + t.Errorf("reg.Copy() expected equivalent registries") + } +} + +func TestRegistryUnshared_RegisterTypeOnCopy(t *testing.T) { + reg, copied := newSharedRegistryPair(t) + + customType := NewObjectType("custom.TypeA") + if err := copied.RegisterType(customType); err != nil { + t.Fatalf("RegisterType() failed: %v", err) + } + + assertUnshared(t, copied) + assertShared(t, reg) + + if _, found := copied.FindIdent("custom.TypeA"); !found { + t.Errorf("copied.FindIdent('custom.TypeA') expected found == true") + } + if _, found := reg.FindIdent("custom.TypeA"); found { + t.Errorf("reg.FindIdent('custom.TypeA') expected found == false after mutating copy") + } + + // Subsequent mutation on already unshared copy stays unshared + customTypeB := NewObjectType("custom.TypeB") + if err := copied.RegisterType(customTypeB); err != nil { + t.Fatalf("RegisterType() failed: %v", err) + } + assertUnshared(t, copied) + if _, found := copied.FindIdent("custom.TypeB"); !found { + t.Errorf("copied.FindIdent('custom.TypeB') expected found == true") + } + if _, found := reg.FindIdent("custom.TypeB"); found { + t.Errorf("reg.FindIdent('custom.TypeB') expected found == false") + } +} + +func TestRegistryUnshared_RegisterTypeOnOriginal(t *testing.T) { + reg, copied := newSharedRegistryPair(t) + + customType := NewObjectType("custom.TypeOrig") + if err := reg.RegisterType(customType); err != nil { + t.Fatalf("RegisterType() failed: %v", err) + } + + assertUnshared(t, reg) + assertShared(t, copied) + + if _, found := reg.FindIdent("custom.TypeOrig"); !found { + t.Errorf("reg.FindIdent('custom.TypeOrig') expected found == true") + } + if _, found := copied.FindIdent("custom.TypeOrig"); found { + t.Errorf("copied.FindIdent('custom.TypeOrig') expected found == false after mutating original") + } +} + +func TestRegistryUnshared_RegisterMessage(t *testing.T) { + reg, copied := newSharedRegistryPair(t) + + if err := copied.RegisterMessage(&proto3pb.TestAllTypes{}); err != nil { + t.Fatalf("RegisterMessage() failed: %v", err) + } + + assertUnshared(t, copied) + assertShared(t, reg) + + if _, found := copied.FindStructType("google.expr.proto3.test.TestAllTypes"); !found { + t.Errorf("copied.FindStructType() expected found == true") + } + if _, found := reg.FindStructType("google.expr.proto3.test.TestAllTypes"); found { + t.Errorf("reg.FindStructType() expected found == false") + } +} + +func TestRegistryUnshared_RegisterDescriptor(t *testing.T) { + reg, copied := newSharedRegistryPair(t) + + err := copied.RegisterDescriptor(proto3pb.GlobalEnum_GOO.Descriptor().ParentFile()) + if err != nil { + t.Fatalf("RegisterDescriptor() failed: %v", err) + } + + assertUnshared(t, copied) + assertShared(t, reg) + + enumVal := copied.EnumValue("google.expr.proto3.test.GlobalEnum.GOO") + if IsError(enumVal) || enumVal.(Int) != Int(proto3pb.GlobalEnum_GOO.Number()) { + t.Errorf("copied.EnumValue() got %v, wanted %v", enumVal, proto3pb.GlobalEnum_GOO.Number()) + } + origEnumVal := reg.EnumValue("google.expr.proto3.test.GlobalEnum.GOO") + if !IsError(origEnumVal) { + t.Errorf("reg.EnumValue() expected error, got %v", origEnumVal) + } +} + +func TestRegistryUnshared_WithJSONFieldNames(t *testing.T) { + reg, copied := newSharedRegistryPair(t, ProtoTypeDefs(&proto3pb.TestAllTypes{})) + + if err := copied.WithJSONFieldNames(true); err != nil { + t.Fatalf("WithJSONFieldNames() failed: %v", err) + } + + assertUnshared(t, copied) + assertShared(t, reg) + + if !copied.JSONFieldNames() { + t.Errorf("copied.JSONFieldNames() expected true, got false") + } + if reg.JSONFieldNames() { + t.Errorf("reg.JSONFieldNames() expected false, got true") + } } +func TestRegistryUnshared_ChainedCopies(t *testing.T) { + r1 := NewEmptyRegistry() + r2 := r1.Copy() + r3 := r2.Copy() + + assertShared(t, r1) + assertShared(t, r2) + assertShared(t, r3) + + typeInR2 := NewObjectType("custom.InR2") + if err := r2.RegisterType(typeInR2); err != nil { + t.Fatalf("RegisterType() failed: %v", err) + } + + assertUnshared(t, r2) + assertShared(t, r1) + assertShared(t, r3) + + if _, found := r2.FindIdent("custom.InR2"); !found { + t.Errorf("r2.FindIdent('custom.InR2') expected found == true") + } + if _, found := r1.FindIdent("custom.InR2"); found { + t.Errorf("r1.FindIdent('custom.InR2') expected found == false") + } + if _, found := r3.FindIdent("custom.InR2"); found { + t.Errorf("r3.FindIdent('custom.InR2') expected found == false") + } + + typeInR3 := NewObjectType("custom.InR3") + if err := r3.RegisterType(typeInR3); err != nil { + t.Fatalf("RegisterType() failed: %v", err) + } + + assertUnshared(t, r3) + if _, found := r3.FindIdent("custom.InR3"); !found { + t.Errorf("r3.FindIdent('custom.InR3') expected found == true") + } + if _, found := r1.FindIdent("custom.InR3"); found { + t.Errorf("r1.FindIdent('custom.InR3') expected found == false") + } + if _, found := r2.FindIdent("custom.InR3"); found { + t.Errorf("r2.FindIdent('custom.InR3') expected found == false") + } +} + +func TestRegistryConcurrentCopy(t *testing.T) { + reg := NewEmptyRegistry() + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + _ = reg.Copy() + }() + } + wg.Wait() +} + + + func TestRegistryRegisterType(t *testing.T) { tests := []struct { name string From 424f95422e8b464379054ef4c822917be9e11ed4 Mon Sep 17 00:00:00 2001 From: l46kok Date: Thu, 13 Aug 2026 14:55:29 -0700 Subject: [PATCH 15/37] Fix shorthand type specifier parsing to allow newlines and tab characters (#1411) --- common/env/io.go | 6 +++++- common/env/io_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/common/env/io.go b/common/env/io.go index ec126f9ce..9ef6334da 100644 --- a/common/env/io.go +++ b/common/env/io.go @@ -234,11 +234,15 @@ func (p *typeDescParser) parseTypeParamIdent() (string, error) { } func (p *typeDescParser) skipWhitespace() { - for p.pos < p.length && p.text[p.pos] == ' ' { + for p.pos < p.length && isWhitespace(p.text[p.pos]) { p.pos++ } } +func isWhitespace(c byte) bool { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' +} + func isAlpha(c byte) bool { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') } diff --git a/common/env/io_test.go b/common/env/io_test.go index b4068e9b3..2aeeecc76 100644 --- a/common/env/io_test.go +++ b/common/env/io_test.go @@ -51,6 +51,18 @@ func TestParseTypeDesc(t *testing.T) { "map>", NewTypeDesc("map", NewTypeDesc("int"), NewTypeDesc("list", NewTypeDesc("string"))), }, + { + "list<\n\tint\n>", + NewTypeDesc("list", NewTypeDesc("int")), + }, + { + "map<\r\n string,\r\n list\r\n>", + NewTypeDesc("map", NewTypeDesc("string"), NewTypeDesc("list", NewTypeDesc("string"))), + }, + { + "map", + NewTypeDesc("map", NewTypeDesc("string"), NewTypeDesc("int")), + }, } for _, tc := range tcs { t.Run(tc.text, func(t *testing.T) { @@ -235,6 +247,35 @@ functions: return: type_name: V is_type_param: true +`, + }, + { + name: "multiline and tab whitespace in types", + yamlIn: `name: user_env +variables: + - name: user_lookup_table + type: >- + map< + string, + list + > + - name: permissions + type: "map" +`, + yamlOut: `name: user_env +variables: + - name: user_lookup_table + type_name: map + params: + - type_name: string + - type_name: list + params: + - type_name: string + - name: permissions + type_name: map + params: + - type_name: string + - type_name: int `, }, } From fc1b523bbeefbbb970dd9d98aec7f59914b4788b Mon Sep 17 00:00:00 2001 From: dplotnikov Date: Thu, 13 Aug 2026 17:47:36 -0700 Subject: [PATCH 16/37] Switch module and import paths to cel.dev/cel-go Following the repository relocation and adoption of the cel.dev domain, update the module path in go.mod files from github.com/google/cel-go to cel.dev/cel-go. Update all internal package imports and Bazel BUILD definitions accordingly. --- README.md | 12 +++---- cel/BUILD.bazel | 2 +- cel/async/BUILD.bazel | 2 +- cel/async/async.go | 10 +++--- cel/async/async_test.go | 10 +++--- cel/cel_example_test.go | 6 ++-- cel/cel_test.go | 26 +++++++------- cel/decls.go | 10 +++--- cel/decls_test.go | 18 +++++----- cel/env.go | 26 +++++++------- cel/env_test.go | 16 ++++----- cel/fieldpaths.go | 4 +-- cel/folding.go | 12 +++---- cel/folding_test.go | 12 +++---- cel/inlining.go | 12 +++---- cel/inlining_test.go | 4 +-- cel/io.go | 12 +++---- cel/io_test.go | 12 +++---- cel/library.go | 24 ++++++------- cel/macro.go | 8 ++--- cel/macro_test.go | 4 +-- cel/optimizer.go | 8 ++--- cel/optimizer_test.go | 8 ++--- cel/options.go | 22 ++++++------ cel/program.go | 14 ++++---- cel/program_async_test.go | 14 ++++---- cel/prompt.go | 8 ++--- cel/prompt_test.go | 4 +-- cel/validator.go | 8 ++--- cel/validator_test.go | 14 ++++---- checker/BUILD.bazel | 2 +- checker/checker.go | 14 ++++---- checker/checker_test.go | 22 ++++++------ checker/cost.go | 10 +++--- checker/cost_test.go | 16 ++++----- checker/decls/BUILD.bazel | 2 +- checker/env.go | 10 +++--- checker/env_test.go | 12 +++---- checker/errors.go | 6 ++-- checker/format.go | 4 +-- checker/format_test.go | 4 +-- checker/mapping.go | 2 +- checker/printer.go | 4 +-- checker/scopes.go | 2 +- checker/types.go | 2 +- codelab/README.md | 4 +-- codelab/codelab.go | 24 ++++++------- codelab/go.mod | 20 +++++------ codelab/go.sum | 36 +++++++------------ codelab/solution/codelab.go | 24 ++++++------- common/BUILD.bazel | 2 +- common/ast/BUILD.bazel | 2 +- common/ast/ast.go | 6 ++-- common/ast/ast_test.go | 10 +++--- common/ast/conversion.go | 4 +-- common/ast/conversion_test.go | 16 ++++----- common/ast/expr.go | 2 +- common/ast/expr_test.go | 6 ++-- common/ast/factory.go | 2 +- common/ast/navigable.go | 4 +-- common/ast/navigable_test.go | 20 +++++------ common/containers/BUILD.bazel | 2 +- common/containers/container.go | 2 +- common/containers/container_test.go | 2 +- common/debug/BUILD.bazel | 2 +- common/debug/debug.go | 6 ++-- common/decls/BUILD.bazel | 2 +- common/decls/decls.go | 12 +++---- common/decls/decls_test.go | 14 ++++---- common/env/BUILD.bazel | 2 +- common/env/env.go | 4 +-- common/env/env_test.go | 10 +++--- common/functions/BUILD.bazel | 2 +- common/functions/functions.go | 2 +- common/operators/BUILD.bazel | 2 +- common/overloads/BUILD.bazel | 2 +- common/runes/BUILD.bazel | 2 +- common/source.go | 2 +- common/stdlib/BUILD.bazel | 2 +- common/stdlib/standard.go | 16 ++++----- common/types/BUILD.bazel | 2 +- common/types/bool.go | 2 +- common/types/bytes.go | 2 +- common/types/compare.go | 2 +- common/types/double.go | 2 +- common/types/double_test.go | 4 +-- common/types/duration.go | 4 +-- common/types/duration_test.go | 4 +-- common/types/err.go | 2 +- common/types/format.go | 4 +-- common/types/int.go | 2 +- common/types/int_test.go | 4 +-- common/types/iterator.go | 4 +-- common/types/json_list_test.go | 2 +- common/types/list.go | 4 +-- common/types/list_test.go | 4 +-- common/types/map.go | 6 ++-- common/types/map_test.go | 8 ++--- common/types/native.go | 4 +-- common/types/native_test.go | 16 ++++----- common/types/null.go | 2 +- common/types/null_test.go | 2 +- common/types/object.go | 4 +-- common/types/object_test.go | 4 +-- common/types/optional.go | 2 +- common/types/optional_test.go | 2 +- common/types/pb/BUILD.bazel | 2 +- common/types/pb/equal_test.go | 2 +- common/types/pb/file_test.go | 6 ++-- common/types/pb/pb_test.go | 4 +-- common/types/pb/type_test.go | 6 ++-- common/types/provider.go | 6 ++-- common/types/provider_test.go | 6 ++-- common/types/ref/BUILD.bazel | 2 +- common/types/string.go | 4 +-- common/types/string_test.go | 4 +-- common/types/struct.go | 2 +- common/types/timestamp.go | 4 +-- common/types/timestamp_test.go | 4 +-- common/types/traits/BUILD.bazel | 2 +- common/types/traits/comparer.go | 2 +- common/types/traits/container.go | 2 +- common/types/traits/field_tester.go | 2 +- common/types/traits/indexer.go | 2 +- common/types/traits/iterator.go | 2 +- common/types/traits/lister.go | 2 +- common/types/traits/mapper.go | 2 +- common/types/traits/matcher.go | 2 +- common/types/traits/math.go | 2 +- common/types/traits/receiver.go | 2 +- common/types/traits/sizer.go | 2 +- common/types/type_test.go | 2 +- common/types/types.go | 6 ++-- common/types/types_test.go | 4 +-- common/types/uint.go | 2 +- common/types/uint_test.go | 4 +-- common/types/unknown.go | 2 +- common/types/unknown_test.go | 2 +- common/types/util.go | 2 +- conformance/conformance_test.go | 12 +++---- conformance/go.mod | 18 ++++++---- conformance/go.sum | 26 +++----------- conformance/policy/policy_conformance_test.go | 12 +++---- examples/README.md | 2 +- examples/example_cel_advanced_test.go | 2 +- examples/example_cel_collections_test.go | 2 +- examples/example_cel_compile_test.go | 4 +-- examples/example_cel_context_eval_test.go | 2 +- examples/example_cel_custom_functions_test.go | 6 ++-- examples/example_cel_custom_macros_test.go | 12 +++---- examples/example_cel_execution_cost_test.go | 6 ++-- .../example_cel_logic_and_conditions_test.go | 2 +- examples/example_cel_native_structs_test.go | 4 +-- examples/example_cel_operators_test.go | 2 +- examples/example_cel_protocol_buffers_test.go | 4 +-- .../example_cel_strings_and_numbers_test.go | 4 +-- examples/example_cel_time_test.go | 2 +- .../example_cel_transforming_data_test.go | 2 +- examples/example_cel_type_conversions_test.go | 2 +- ext/BUILD.bazel | 2 +- ext/bindings.go | 12 +++---- ext/bindings_test.go | 16 ++++----- ext/comprehensions.go | 14 ++++---- ext/comprehensions_test.go | 8 ++--- ext/costs.go | 12 +++---- ext/encoders.go | 10 +++--- ext/encoders_test.go | 4 +-- ext/extension_option_factory.go | 4 +-- ext/extension_option_factory_test.go | 4 +-- ext/formatting.go | 12 +++---- ext/formatting_test.go | 10 +++--- ext/formatting_v2.go | 10 +++--- ext/formatting_v2_test.go | 10 +++--- ext/guards.go | 6 ++-- ext/lists.go | 20 +++++------ ext/lists_test.go | 8 ++--- ext/math.go | 14 ++++---- ext/math_test.go | 6 ++-- ext/native.go | 4 +-- ext/native_test.go | 14 ++++---- ext/network.go | 12 +++---- ext/network_test.go | 6 ++-- ext/protos.go | 4 +-- ext/protos_test.go | 12 +++---- ext/regex.go | 12 +++---- ext/regex_test.go | 4 +-- ext/sets.go | 16 ++++----- ext/sets_test.go | 10 +++--- ext/strings.go | 14 ++++---- ext/strings_test.go | 8 ++--- go.mod | 2 +- interpreter/BUILD.bazel | 2 +- interpreter/activation.go | 2 +- interpreter/activation_test.go | 4 +-- interpreter/async.go | 6 ++-- interpreter/async_test.go | 18 +++++----- interpreter/attribute_patterns.go | 6 ++-- interpreter/attribute_patterns_test.go | 4 +-- interpreter/attributes.go | 8 ++--- interpreter/attributes_test.go | 18 +++++----- interpreter/decorators.go | 8 ++--- interpreter/dispatcher.go | 2 +- interpreter/evalstate.go | 2 +- interpreter/frame.go | 6 ++-- interpreter/frame_test.go | 4 +-- interpreter/functions/BUILD.bazel | 2 +- interpreter/functions/functions.go | 2 +- interpreter/interpretable.go | 12 +++---- interpreter/interpreter.go | 8 ++--- interpreter/interpreter_test.go | 28 +++++++-------- interpreter/optimizations.go | 4 +-- interpreter/planner.go | 10 +++--- interpreter/prune.go | 12 +++---- interpreter/prune_test.go | 20 +++++------ interpreter/runtimecost.go | 10 +++--- interpreter/runtimecost_test.go | 18 +++++----- parser/BUILD.bazel | 2 +- parser/errors.go | 2 +- parser/gen/BUILD.bazel | 2 +- parser/helper.go | 8 ++--- parser/helper_test.go | 4 +-- parser/input.go | 2 +- parser/macro.go | 10 +++--- parser/macro_test.go | 4 +-- parser/parser.go | 12 +++---- parser/parser_test.go | 10 +++--- parser/unparser.go | 8 ++--- parser/unparser_test.go | 6 ++-- policy/BUILD.bazel | 2 +- policy/compiler.go | 14 ++++---- policy/compiler_test.go | 10 +++--- policy/composer.go | 10 +++--- policy/composer_test.go | 8 ++--- policy/config.go | 6 ++-- policy/config_test.go | 6 ++-- policy/go.mod | 10 +++--- policy/helper_test.go | 10 +++--- policy/parser.go | 6 ++-- policy/parser_test.go | 6 ++-- policy/source.go | 2 +- policy/test/cel_test_runner.go | 8 ++--- policy/test/k8s_cel_test_runner.go | 4 +-- repl/BUILD.bazel | 2 +- repl/commands.go | 4 +-- repl/evaluator.go | 16 ++++----- repl/evaluator_test.go | 6 ++-- repl/go.mod | 8 ++--- repl/go.sum | 2 ++ repl/main/BUILD.bazel | 4 +-- repl/main/main.go | 2 +- repl/parser/BUILD.bazel | 2 +- repl/typefmt.go | 8 ++--- repl/typefmt_test.go | 2 +- test/BUILD.bazel | 2 +- test/async.go | 4 +-- test/bench/BUILD.bazel | 2 +- test/bench/bench.go | 8 ++--- test/bench/bench_test.go | 2 +- test/expr.go | 2 +- test/proto2pb/BUILD.bazel | 4 +-- test/proto2pb/test_all_types.proto | 2 +- test/proto2pb/test_extensions.proto | 2 +- test/proto3pb/BUILD.bazel | 4 +-- test/proto3pb/test_all_types.proto | 2 +- test/proto3pb/test_import.proto | 2 +- tools/celtest/BUILD.bazel | 2 +- tools/celtest/test_coverage_reporter.go | 6 ++-- tools/celtest/test_coverage_reporter_test.go | 6 ++-- tools/celtest/test_runner.go | 20 +++++------ tools/celtest/test_runner_test.go | 14 ++++---- tools/compiler/BUILD.bazel | 2 +- tools/compiler/compiler.go | 12 +++---- tools/compiler/compiler_test.go | 8 ++--- tools/go.mod | 12 +++---- tools/go.sum | 19 ---------- 275 files changed, 944 insertions(+), 985 deletions(-) diff --git a/README.md b/README.md index 6b71ff776..d07aa3ca4 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # Common Expression Language -[![Go Report Card](https://goreportcard.com/badge/github.com/google/cel-go)](https://goreportcard.com/report/github.com/google/cel-go) -[![GoDoc](https://godoc.org/github.com/google/cel-go?status.svg)][6] +[![Go Report Card](https://goreportcard.com/badge/cel.dev/cel-go)](https://goreportcard.com/report/cel.dev/cel-go) +[![GoDoc](https://godoc.org/cel.dev/cel-go?status.svg)][6] > [!WARNING] > **On June 16, 2026, this repository will move to > github.com/cel-expr/cel-go!** > > Please update your links and dependencies. See the [pinned -> issue](https://github.com/google/cel-go/issues/1329) for details. +> issue](https://cel.dev/cel-go/issues/1329) for details. The Common Expression Language (CEL) is a non-Turing complete language designed for simplicity, speed, safety, and portability. CEL's C-like [syntax][1] looks @@ -64,7 +64,7 @@ Let's expose `name` and `group` variables to CEL using the `cel.Variable` environment option: ```go -import "github.com/google/cel-go/cel" +import "cel.dev/cel-go/cel" env, err := cel.NewEnv( cel.Variable("name", cel.StringType), @@ -288,6 +288,6 @@ Released under the [Apache License](LICENSE). [1]: https://github.com/google/cel-spec [2]: https://groups.google.com/forum/#!forum/cel-go-discuss [3]: https://github.com/google/cel-cpp -[4]: https://github.com/google/cel-go/issues +[4]: https://cel.dev/cel-go/issues [5]: https://bazel.build -[6]: https://godoc.org/github.com/google/cel-go +[6]: https://godoc.org/cel.dev/cel-go diff --git a/cel/BUILD.bazel b/cel/BUILD.bazel index 62a56036a..c42612e19 100644 --- a/cel/BUILD.bazel +++ b/cel/BUILD.bazel @@ -23,7 +23,7 @@ go_library( "validator.go", ], embedsrcs = ["templates/authoring.tmpl"], - importpath = "github.com/google/cel-go/cel", + importpath = "cel.dev/cel-go/cel", visibility = ["//visibility:public"], deps = [ "//cel/async:go_default_library", diff --git a/cel/async/BUILD.bazel b/cel/async/BUILD.bazel index 85b28bcdb..e77dfaec5 100644 --- a/cel/async/BUILD.bazel +++ b/cel/async/BUILD.bazel @@ -9,7 +9,7 @@ go_library( srcs = [ "async.go", ], - importpath = "github.com/google/cel-go/cel/async", + importpath = "cel.dev/cel-go/cel/async", visibility = ["//visibility:public"], deps = [ "//common/decls:go_default_library", diff --git a/cel/async/async.go b/cel/async/async.go index a011114bd..b8621a784 100644 --- a/cel/async/async.go +++ b/cel/async/async.go @@ -21,11 +21,11 @@ import ( "errors" "time" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/functions" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/interpreter" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/interpreter" ) // Call describes a pending or completed asynchronous function call. diff --git a/cel/async/async_test.go b/cel/async/async_test.go index 71fe50de6..9c5ba9962 100644 --- a/cel/async/async_test.go +++ b/cel/async/async_test.go @@ -22,11 +22,11 @@ import ( "testing" "time" - "github.com/google/cel-go/cel/async" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/functions" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/cel/async" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) type retryableTestErr struct{} diff --git a/cel/cel_example_test.go b/cel/cel_example_test.go index f4c11b598..5c6b7603a 100644 --- a/cel/cel_example_test.go +++ b/cel/cel_example_test.go @@ -19,9 +19,9 @@ import ( "fmt" "log" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) func Example() { diff --git a/cel/cel_test.go b/cel/cel_test.go index 7cb32afb0..7a04e65f3 100644 --- a/cel/cel_test.go +++ b/cel/cel_test.go @@ -32,17 +32,17 @@ import ( "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoreflect" - "github.com/google/cel-go/checker" - celast "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/env" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" - "github.com/google/cel-go/interpreter" - "github.com/google/cel-go/parser" - "github.com/google/cel-go/test" + "cel.dev/cel-go/checker" + celast "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/env" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/interpreter" + "cel.dev/cel-go/parser" + "cel.dev/cel-go/test" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" descpb "google.golang.org/protobuf/types/descriptorpb" @@ -51,8 +51,8 @@ import ( timestamppb "google.golang.org/protobuf/types/known/timestamppb" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" - proto2pb "github.com/google/cel-go/test/proto2pb" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto2pb "cel.dev/cel-go/test/proto2pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) func Test_ExampleWithBuiltins(t *testing.T) { diff --git a/cel/decls.go b/cel/decls.go index 53941146d..1cc3f7b50 100644 --- a/cel/decls.go +++ b/cel/decls.go @@ -17,11 +17,11 @@ package cel import ( "fmt" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/functions" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" celpb "cel.dev/expr" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" diff --git a/cel/decls_test.go b/cel/decls_test.go index 9024d74db..bade15c7b 100644 --- a/cel/decls_test.go +++ b/cel/decls_test.go @@ -21,15 +21,15 @@ import ( "strings" "testing" - chkdecls "github.com/google/cel-go/checker/decls" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/functions" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/stdlib" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + chkdecls "cel.dev/cel-go/checker/decls" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/stdlib" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/env.go b/cel/env.go index 794c62f78..772b0da48 100644 --- a/cel/env.go +++ b/cel/env.go @@ -23,19 +23,19 @@ import ( "strings" "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/env" - "github.com/google/cel-go/common/functions" - "github.com/google/cel-go/common/stdlib" - "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" + "cel.dev/cel-go/checker" + chkdecls "cel.dev/cel-go/checker/decls" + "cel.dev/cel-go/common" + celast "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/env" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/stdlib" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/interpreter" + "cel.dev/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "google.golang.org/protobuf/reflect/protoreflect" diff --git a/cel/env_test.go b/cel/env_test.go index 004ab9f0a..95124c15b 100644 --- a/cel/env_test.go +++ b/cel/env_test.go @@ -23,17 +23,17 @@ import ( "sync" "testing" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/env" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/env" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" "google.golang.org/protobuf/proto" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/fieldpaths.go b/cel/fieldpaths.go index 570fce3a4..b63c2ba2a 100644 --- a/cel/fieldpaths.go +++ b/cel/fieldpaths.go @@ -4,8 +4,8 @@ import ( "slices" "strings" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/types" ) // fieldPath represents a selection path to a field from a variable in a CEL environment. diff --git a/cel/folding.go b/cel/folding.go index e3459034c..e45dc6093 100644 --- a/cel/folding.go +++ b/cel/folding.go @@ -19,12 +19,12 @@ import ( "errors" "fmt" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" ) // ConstantFoldingOption defines a functional option for configuring constant folding. diff --git a/cel/folding_test.go b/cel/folding_test.go index 8a292cb73..b894681e2 100644 --- a/cel/folding_test.go +++ b/cel/folding_test.go @@ -25,13 +25,13 @@ import ( "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/interpreter" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/interpreter" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/inlining.go b/cel/inlining.go index d9a5e89a5..3ade2204d 100644 --- a/cel/inlining.go +++ b/cel/inlining.go @@ -15,12 +15,12 @@ package cel import ( - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/containers" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/traits" ) // InlineVariable holds a variable name to be matched and an AST representing diff --git a/cel/inlining_test.go b/cel/inlining_test.go index d9ad88b7a..ed1b9270d 100644 --- a/cel/inlining_test.go +++ b/cel/inlining_test.go @@ -17,9 +17,9 @@ package cel_test import ( "testing" - "github.com/google/cel-go/cel" + "cel.dev/cel-go/cel" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) func TestInliningOptimizerNoopShadow(t *testing.T) { diff --git a/cel/io.go b/cel/io.go index c991c95c3..31aa304fe 100644 --- a/cel/io.go +++ b/cel/io.go @@ -21,12 +21,12 @@ import ( "google.golang.org/protobuf/proto" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/parser" celpb "cel.dev/expr" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" diff --git a/cel/io_test.go b/cel/io_test.go index f0ef20a81..41da494aa 100644 --- a/cel/io_test.go +++ b/cel/io_test.go @@ -22,13 +22,13 @@ import ( "google.golang.org/protobuf/proto" - "github.com/google/cel-go/checker/decls" - celast "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/checker/decls" + celast "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/library.go b/cel/library.go index 43912eaae..eb0c9562b 100644 --- a/cel/library.go +++ b/cel/library.go @@ -18,18 +18,18 @@ import ( "fmt" "math" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/env" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/stdlib" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" - "github.com/google/cel-go/interpreter" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/env" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/stdlib" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/interpreter" + "cel.dev/cel-go/parser" ) const ( diff --git a/cel/macro.go b/cel/macro.go index 3d3c5be1b..aed4a8da1 100644 --- a/cel/macro.go +++ b/cel/macro.go @@ -17,10 +17,10 @@ package cel import ( "fmt" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/macro_test.go b/cel/macro_test.go index f6400b020..85c7ab694 100644 --- a/cel/macro_test.go +++ b/cel/macro_test.go @@ -17,8 +17,8 @@ package cel import ( "testing" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" ) func TestGlobalVarArgMacro(t *testing.T) { diff --git a/cel/optimizer.go b/cel/optimizer.go index 6e260a93c..5c2846558 100644 --- a/cel/optimizer.go +++ b/cel/optimizer.go @@ -18,10 +18,10 @@ import ( "fmt" "sort" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // StaticOptimizer contains a sequence of ASTOptimizer instances which will be applied in order. diff --git a/cel/optimizer_test.go b/cel/optimizer_test.go index 04212d537..b1776a7ae 100644 --- a/cel/optimizer_test.go +++ b/cel/optimizer_test.go @@ -19,14 +19,14 @@ import ( "strings" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/ext" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/ext" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/options.go b/cel/options.go index 709ce007a..21cdb3615 100644 --- a/cel/options.go +++ b/cel/options.go @@ -24,17 +24,17 @@ import ( "google.golang.org/protobuf/reflect/protoregistry" "google.golang.org/protobuf/types/dynamicpb" - "github.com/google/cel-go/cel/async" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/containers" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/env" - "github.com/google/cel-go/common/functions" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/pb" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/interpreter" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/cel/async" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/env" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/pb" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/interpreter" + "cel.dev/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" descpb "google.golang.org/protobuf/types/descriptorpb" diff --git a/cel/program.go b/cel/program.go index 07849974a..b6df752ee 100644 --- a/cel/program.go +++ b/cel/program.go @@ -20,13 +20,13 @@ import ( "fmt" "time" - "github.com/google/cel-go/cel/async" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/interpreter" + "cel.dev/cel-go/cel/async" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/interpreter" ) // Program is an evaluable view of an Ast. diff --git a/cel/program_async_test.go b/cel/program_async_test.go index e3d1746dd..3a7a3ec4a 100644 --- a/cel/program_async_test.go +++ b/cel/program_async_test.go @@ -24,13 +24,13 @@ import ( "testing" "time" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/cel/async" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/ext" - "github.com/google/cel-go/interpreter" - "github.com/google/cel-go/test" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/cel/async" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/ext" + "cel.dev/cel-go/interpreter" + "cel.dev/cel-go/test" ) func TestConcurrentEval(t *testing.T) { diff --git a/cel/prompt.go b/cel/prompt.go index f59934827..e5e2acde7 100644 --- a/cel/prompt.go +++ b/cel/prompt.go @@ -20,10 +20,10 @@ import ( "strings" "text/template" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" ) //go:embed templates/authoring.tmpl diff --git a/cel/prompt_test.go b/cel/prompt_test.go index 068211cb7..b206e056d 100644 --- a/cel/prompt_test.go +++ b/cel/prompt_test.go @@ -20,8 +20,8 @@ import ( "sync" "testing" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/env" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/env" "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/proto" diff --git a/cel/validator.go b/cel/validator.go index 229defe79..17a999f92 100644 --- a/cel/validator.go +++ b/cel/validator.go @@ -20,10 +20,10 @@ import ( "reflect" "regexp" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/env" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/env" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" ) const ( diff --git a/cel/validator_test.go b/cel/validator_test.go index 99795da0d..fb31c94c0 100644 --- a/cel/validator_test.go +++ b/cel/validator_test.go @@ -18,13 +18,13 @@ import ( "reflect" "testing" - celenv "github.com/google/cel-go/common/env" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" - "github.com/google/cel-go/test" + celenv "cel.dev/cel-go/common/env" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/test" ) func TestValidateDurationLiterals(t *testing.T) { diff --git a/checker/BUILD.bazel b/checker/BUILD.bazel index 678b412a9..18698e679 100644 --- a/checker/BUILD.bazel +++ b/checker/BUILD.bazel @@ -18,7 +18,7 @@ go_library( "scopes.go", "types.go", ], - importpath = "github.com/google/cel-go/checker", + importpath = "cel.dev/cel-go/checker", visibility = ["//visibility:public"], deps = [ "//checker/decls:go_default_library", diff --git a/checker/checker.go b/checker/checker.go index 42d27a428..1cffd3e5f 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -22,13 +22,13 @@ import ( "slices" "strings" - "github.com/google/cel-go/common" - "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/operators" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) type checker struct { diff --git a/checker/checker_test.go b/checker/checker_test.go index b61a226cb..47da296d3 100644 --- a/checker/checker_test.go +++ b/checker/checker_test.go @@ -20,18 +20,18 @@ import ( "testing" "time" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/containers" - "github.com/google/cel-go/common/debug" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/stdlib" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/parser" - "github.com/google/cel-go/test" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/debug" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/stdlib" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/parser" + "cel.dev/cel-go/test" - proto2pb "github.com/google/cel-go/test/proto2pb" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto2pb "cel.dev/cel-go/test/proto2pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) func testCases(t testing.TB) []testInfo { diff --git a/checker/cost.go b/checker/cost.go index 3d7dd7ec4..3233fa11b 100644 --- a/checker/cost.go +++ b/checker/cost.go @@ -17,11 +17,11 @@ package checker import ( "math" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/parser" ) // WARNING: Any changes to cost calculations in this file require a corresponding change in interpreter/runtimecost.go diff --git a/checker/cost_test.go b/checker/cost_test.go index ab83559a1..3a7ef835b 100644 --- a/checker/cost_test.go +++ b/checker/cost_test.go @@ -19,15 +19,15 @@ import ( "strings" "testing" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/containers" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/stdlib" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/stdlib" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/parser" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) func TestCost(t *testing.T) { diff --git a/checker/decls/BUILD.bazel b/checker/decls/BUILD.bazel index a6b0be292..fe5380166 100644 --- a/checker/decls/BUILD.bazel +++ b/checker/decls/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "decls.go", ], - importpath = "github.com/google/cel-go/checker/decls", + importpath = "cel.dev/cel-go/checker/decls", deps = [ "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", "@org_golang_google_protobuf//types/known/emptypb:go_default_library", diff --git a/checker/env.go b/checker/env.go index 9c3de9517..1dd2f0ff7 100644 --- a/checker/env.go +++ b/checker/env.go @@ -18,11 +18,11 @@ import ( "fmt" "strings" - "github.com/google/cel-go/common/containers" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/parser" ) type aggregateLiteralElementType int diff --git a/checker/env_test.go b/checker/env_test.go index c3a6aa353..f8a10f29b 100644 --- a/checker/env_test.go +++ b/checker/env_test.go @@ -18,12 +18,12 @@ import ( "strings" "testing" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/containers" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/stdlib" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/stdlib" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/parser" ) func TestOverlappingMacro(t *testing.T) { diff --git a/checker/errors.go b/checker/errors.go index 3535440ba..dce384a80 100644 --- a/checker/errors.go +++ b/checker/errors.go @@ -15,9 +15,9 @@ package checker import ( - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/types" ) // typeErrors is a specialization of Errors. diff --git a/checker/format.go b/checker/format.go index 95842905e..17b1a4d24 100644 --- a/checker/format.go +++ b/checker/format.go @@ -18,8 +18,8 @@ import ( "fmt" "strings" - chkdecls "github.com/google/cel-go/checker/decls" - "github.com/google/cel-go/common/types" + chkdecls "cel.dev/cel-go/checker/decls" + "cel.dev/cel-go/common/types" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/checker/format_test.go b/checker/format_test.go index c68b01c2f..68afc51fc 100644 --- a/checker/format_test.go +++ b/checker/format_test.go @@ -17,8 +17,8 @@ package checker import ( "testing" - "github.com/google/cel-go/checker/decls" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/checker/decls" + "cel.dev/cel-go/common/types" ) func TestFormatType(t *testing.T) { diff --git a/checker/mapping.go b/checker/mapping.go index 8163a908a..7018d8199 100644 --- a/checker/mapping.go +++ b/checker/mapping.go @@ -15,7 +15,7 @@ package checker import ( - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/common/types" ) type mapping struct { diff --git a/checker/printer.go b/checker/printer.go index 7a3984f02..c90711e4c 100644 --- a/checker/printer.go +++ b/checker/printer.go @@ -17,8 +17,8 @@ package checker import ( "sort" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/debug" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/debug" ) type semanticAdorner struct { diff --git a/checker/scopes.go b/checker/scopes.go index 2fcfdf29e..88b664af6 100644 --- a/checker/scopes.go +++ b/checker/scopes.go @@ -17,7 +17,7 @@ package checker import ( "strings" - "github.com/google/cel-go/common/decls" + "cel.dev/cel-go/common/decls" ) // Scopes represents nested Decl sets where the Scopes value contains a Groups containing all diff --git a/checker/types.go b/checker/types.go index 4c65b2737..b716f4e9b 100644 --- a/checker/types.go +++ b/checker/types.go @@ -15,7 +15,7 @@ package checker import ( - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/common/types" ) // isDyn returns true if the input t is either type DYN or a well-known ANY message. diff --git a/codelab/README.md b/codelab/README.md index bf985c71b..27772532e 100644 --- a/codelab/README.md +++ b/codelab/README.md @@ -3,6 +3,6 @@ Find the codelab instructions [here](https://codelabs.developers.google.com/codelabs/cel-go/#0). It requires some knowledge of GoLang and Protobuf. -If you get stuck, check out the [solutions](https://github.com/google/cel-go/blob/master/codelab/solution/codelab.go). +If you get stuck, check out the [solutions](https://cel.dev/cel-go/blob/master/codelab/solution/codelab.go). -If you find a bug or want to make an improvement, PRs and issues are welcome. Please follow the [contributing guidelines](https://github.com/google/cel-go/blob/master/CONTRIBUTING.md). +If you find a bug or want to make an improvement, PRs and issues are welcome. Please follow the [contributing guidelines](https://cel.dev/cel-go/blob/master/CONTRIBUTING.md). diff --git a/codelab/codelab.go b/codelab/codelab.go index f533e7003..77e7eeb3d 100644 --- a/codelab/codelab.go +++ b/codelab/codelab.go @@ -24,10 +24,10 @@ import ( "strings" "time" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" "github.com/golang/glog" "google.golang.org/protobuf/encoding/protojson" @@ -54,7 +54,7 @@ func main() { // // Compile, eval, profit! func exercise1() { - fmt.Println("=== Exercise 1: Hello World ===\n") + fmt.Println("=== Exercise 1: Hello World ===") fmt.Println() } @@ -64,7 +64,7 @@ func exercise1() { // Given a `request` of type `google.rpc.context.AttributeContext.Request` // determine whether a specific auth claim is set. func exercise2() { - fmt.Println("=== Exercise 2: Variables ===\n") + fmt.Println("=== Exercise 2: Variables ===") fmt.Println() } @@ -79,7 +79,7 @@ func exercise2() { // sets the appropriate principal and occurs at 12:00 hours. Then evaluate the // request a second time at midnight. Observe the difference in output. func exercise3() { - fmt.Println("=== Exercise 3: Logical AND/OR ===\n") + fmt.Println("=== Exercise 3: Logical AND/OR ===") fmt.Println() } @@ -89,7 +89,7 @@ func exercise3() { // Declare a `contains` member function on map types that returns a boolean // indicating whether the map contains the key-value pair. func exercise4() { - fmt.Println("=== Exercise 4: Customization ===\n") + fmt.Println("=== Exercise 4: Customization ===") fmt.Println() } @@ -98,7 +98,7 @@ func exercise4() { // // Given the input `now`, construct a JWT with an expiry of 5 minutes. func exercise5() { - fmt.Println("=== Exercise 5: Building JSON ===\n") + fmt.Println("=== Exercise 5: Building JSON ===") fmt.Println() } @@ -109,7 +109,7 @@ func exercise5() { // `google.rpc.context.AttributeContext.Request` with the `time` and `auth` // fields populated according to the go/api-attributes specification. func exercise6() { - fmt.Println("=== Exercise 6: Building Protos ===\n") + fmt.Println("=== Exercise 6: Building Protos ===") fmt.Println() } @@ -120,7 +120,7 @@ func exercise6() { // with the `group` prefix, and ensure that all group-like keys have list // values containing only strings that end with '@acme.co`. func exercise7() { - fmt.Println("=== Exercise 7: Macros ===\n") + fmt.Println("=== Exercise 7: Macros ===") fmt.Println() } @@ -134,7 +134,7 @@ func exercise7() { // Also, turn on the homogeneous aggregate literals flag to disable // heterogeneous list and map literals. func exercise8() { - fmt.Println("=== Exercise 8: Tuning ===\n") + fmt.Println("=== Exercise 8: Tuning ===") fmt.Println() } diff --git a/codelab/go.mod b/codelab/go.mod index 58d84bb8b..40128a408 100644 --- a/codelab/go.mod +++ b/codelab/go.mod @@ -1,22 +1,20 @@ -module github.com/google/cel-go/codelab +module cel.dev/cel-go/codelab -go 1.22.0 - -toolchain go1.22.5 +go 1.23.0 require ( + cel.dev/cel-go v0.21.0 github.com/golang/glog v1.2.4 - github.com/google/cel-go v0.21.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 - google.golang.org/protobuf v1.34.2 + google.golang.org/protobuf v1.36.10 ) require ( - cel.dev/expr v0.22.1 // indirect - github.com/antlr4-go/antlr/v4 v4.13.0 // indirect - github.com/stoewer/go-strcase v1.2.0 // indirect - golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect + cel.dev/expr v0.25.1 // indirect + github.com/antlr4-go/antlr/v4 v4.13.1 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect ) -replace github.com/google/cel-go => ../. +replace cel.dev/cel-go => ../. diff --git a/codelab/go.sum b/codelab/go.sum index 8dbce6c33..596a97228 100644 --- a/codelab/go.sum +++ b/codelab/go.sum @@ -1,32 +1,22 @@ -cel.dev/expr v0.22.1 h1:xoFEsNh972Yzey8N9TCPx2nDvMN7TMhQEzxLuj/iRrI= -cel.dev/expr v0.22.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= -github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= -github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= +github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc= github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= +golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw= google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/codelab/solution/codelab.go b/codelab/solution/codelab.go index b1bfb95d9..f7ef829d2 100644 --- a/codelab/solution/codelab.go +++ b/codelab/solution/codelab.go @@ -24,10 +24,10 @@ import ( "strings" "time" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" "github.com/golang/glog" "google.golang.org/protobuf/encoding/protojson" @@ -54,7 +54,7 @@ func main() { // // Compile, eval, profit! func exercise1() { - fmt.Println("=== Exercise 1: Hello World ===\n") + fmt.Println("=== Exercise 1: Hello World ===") // Create the standard environment. env, err := cel.NewEnv() if err != nil { @@ -93,7 +93,7 @@ func exercise1() { // Given a `request` of type `google.rpc.context.AttributeContext.Request` // determine whether a specific auth claim is set. func exercise2() { - fmt.Println("=== Exercise 2: Variables ===\n") + fmt.Println("=== Exercise 2: Variables ===") // Construct a standard environment that accepts 'request' as input and uses // the google.rpc.context.AttributeContext.Request type. env, err := cel.NewEnv( @@ -125,7 +125,7 @@ func exercise2() { // sets the appropriate principal and occurs at 12:00 hours. Then evaluate the // request a second time at midnight. Observe the difference in output. func exercise3() { - fmt.Println("=== Exercise 3: Logical AND/OR ===\n") + fmt.Println("=== Exercise 3: Logical AND/OR ===") env, _ := cel.NewEnv( cel.Types(&rpcpb.AttributeContext_Request{}), cel.Variable("request", @@ -154,7 +154,7 @@ func exercise3() { // Declare a `contains` member function on map types that returns a boolean // indicating whether the map contains the key-value pair. func exercise4() { - fmt.Println("=== Exercise 4: Customization ===\n") + fmt.Println("=== Exercise 4: Customization ===") // Determine whether an optional claim is set to the proper value. The custom // map.contains(key, value) function is used as an alternative to: // key in map && map[key] == value @@ -200,7 +200,7 @@ func exercise4() { // // Given the input `now`, construct a JWT with an expiry of 5 minutes. func exercise5() { - fmt.Println("=== Exercise 5: Building JSON ===\n") + fmt.Println("=== Exercise 5: Building JSON ===") // Note the quoted keys in the CEL map literal. For proto messages the // field names are unquoted as they represent well-defined identifiers. env, _ := cel.NewEnv( @@ -238,7 +238,7 @@ func exercise5() { // `google.rpc.context.AttributeContext.Request` with the `time` and `auth` // fields populated according to the go/api-attributes specification. func exercise6() { - fmt.Println("=== Exercise 6: Building Protos ===\n") + fmt.Println("=== Exercise 6: Building Protos ===") // Construct an environment and indicate that the container for all references // within the expression is `google.rpc.context.AttributeContext`. @@ -305,7 +305,7 @@ func exercise6() { // with the `group` prefix, and ensure that all group-like keys have list // values containing only strings that end with '@acme.co`. func exercise7() { - fmt.Println("=== Exercise 7: Macros ===\n") + fmt.Println("=== Exercise 7: Macros ===") env, _ := cel.NewEnv(cel.Variable("jwt", cel.DynType)) ast := compile(env, `jwt.extra_claims.exists(c, c.startsWith('group')) @@ -341,7 +341,7 @@ func exercise7() { // Turn on the optimization, exhaustive eval, and state tracking // `cel.ProgramOption` flags to see the impact on evaluation behavior. func exercise8() { - fmt.Println("=== Exercise 8: Tuning ===\n") + fmt.Println("=== Exercise 8: Tuning ===") // Declare the `x` and 'y' variables as input into the expression. env, _ := cel.NewEnv( cel.Variable("x", cel.IntType), diff --git a/common/BUILD.bazel b/common/BUILD.bazel index 1b1b7914d..408888472 100644 --- a/common/BUILD.bazel +++ b/common/BUILD.bazel @@ -15,7 +15,7 @@ go_library( "location.go", "source.go", ], - importpath = "github.com/google/cel-go/common", + importpath = "cel.dev/cel-go/common", deps = [ "//common/runes:go_default_library", "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", diff --git a/common/ast/BUILD.bazel b/common/ast/BUILD.bazel index 9824f57a9..d62a4cb6e 100644 --- a/common/ast/BUILD.bazel +++ b/common/ast/BUILD.bazel @@ -14,7 +14,7 @@ go_library( "factory.go", "navigable.go", ], - importpath = "github.com/google/cel-go/common/ast", + importpath = "cel.dev/cel-go/common/ast", deps = [ "//common:go_default_library", "//common/types:go_default_library", diff --git a/common/ast/ast.go b/common/ast/ast.go index c8f8f8a02..d65d10b59 100644 --- a/common/ast/ast.go +++ b/common/ast/ast.go @@ -18,9 +18,9 @@ package ast import ( "slices" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // AST contains a protobuf expression and source info along with CEL-native type and reference information. diff --git a/common/ast/ast_test.go b/common/ast/ast_test.go index ee63171a6..20e9181e1 100644 --- a/common/ast/ast_test.go +++ b/common/ast/ast_test.go @@ -20,11 +20,11 @@ import ( "reflect" "testing" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" ) diff --git a/common/ast/conversion.go b/common/ast/conversion.go index 380f8c118..5d032cc1d 100644 --- a/common/ast/conversion.go +++ b/common/ast/conversion.go @@ -19,8 +19,8 @@ import ( "google.golang.org/protobuf/proto" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" celpb "cel.dev/expr" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" diff --git a/common/ast/conversion_test.go b/common/ast/conversion_test.go index ecd50830b..a7e799a4f 100644 --- a/common/ast/conversion_test.go +++ b/common/ast/conversion_test.go @@ -23,14 +23,14 @@ import ( "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" - chkdecls "github.com/google/cel-go/checker/decls" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/parser" + chkdecls "cel.dev/cel-go/checker/decls" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/ast/expr.go b/common/ast/expr.go index 9f55cb3b9..459311f0d 100644 --- a/common/ast/expr.go +++ b/common/ast/expr.go @@ -15,7 +15,7 @@ package ast import ( - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) // ExprKind represents the expression node kind. diff --git a/common/ast/expr_test.go b/common/ast/expr_test.go index d32bf3fee..339d5c5cb 100644 --- a/common/ast/expr_test.go +++ b/common/ast/expr_test.go @@ -19,9 +19,9 @@ import ( "reflect" "testing" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" ) func TestSetKindCase(t *testing.T) { diff --git a/common/ast/factory.go b/common/ast/factory.go index d4dcde4d9..b83e5555a 100644 --- a/common/ast/factory.go +++ b/common/ast/factory.go @@ -14,7 +14,7 @@ package ast -import "github.com/google/cel-go/common/types/ref" +import "cel.dev/cel-go/common/types/ref" // ExprFactory interfaces defines a set of methods necessary for building native expression values. type ExprFactory interface { diff --git a/common/ast/navigable.go b/common/ast/navigable.go index 364edfa3a..3a71f865f 100644 --- a/common/ast/navigable.go +++ b/common/ast/navigable.go @@ -15,8 +15,8 @@ package ast import ( - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // NavigableExpr represents the base navigable expression value with methods to inspect the diff --git a/common/ast/navigable_test.go b/common/ast/navigable_test.go index a9e55eaea..1784e5f71 100644 --- a/common/ast/navigable_test.go +++ b/common/ast/navigable_test.go @@ -18,17 +18,17 @@ import ( "reflect" "testing" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common" - "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/operators" - "github.com/google/cel-go/common/stdlib" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/stdlib" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/parser" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) func TestNavigateAST(t *testing.T) { diff --git a/common/containers/BUILD.bazel b/common/containers/BUILD.bazel index 81197f064..7def2f256 100644 --- a/common/containers/BUILD.bazel +++ b/common/containers/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "container.go", ], - importpath = "github.com/google/cel-go/common/containers", + importpath = "cel.dev/cel-go/common/containers", deps = [ "//common/ast:go_default_library", ], diff --git a/common/containers/container.go b/common/containers/container.go index fcfcdfc3f..2330e0764 100644 --- a/common/containers/container.go +++ b/common/containers/container.go @@ -21,7 +21,7 @@ import ( "strings" "unicode" - "github.com/google/cel-go/common/ast" + "cel.dev/cel-go/common/ast" ) var ( diff --git a/common/containers/container_test.go b/common/containers/container_test.go index 2ccdfd6ee..efab6391f 100644 --- a/common/containers/container_test.go +++ b/common/containers/container_test.go @@ -19,7 +19,7 @@ import ( "reflect" "testing" - "github.com/google/cel-go/common/ast" + "cel.dev/cel-go/common/ast" ) func TestContainers_ResolveCandidateNames(t *testing.T) { diff --git a/common/debug/BUILD.bazel b/common/debug/BUILD.bazel index 724ed3404..07fed4271 100644 --- a/common/debug/BUILD.bazel +++ b/common/debug/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "debug.go", ], - importpath = "github.com/google/cel-go/common/debug", + importpath = "cel.dev/cel-go/common/debug", deps = [ "//common:go_default_library", "//common/ast:go_default_library", diff --git a/common/debug/debug.go b/common/debug/debug.go index fbc847f0c..67593c996 100644 --- a/common/debug/debug.go +++ b/common/debug/debug.go @@ -22,9 +22,9 @@ import ( "strconv" "strings" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // Adorner returns debug metadata that will be tacked on to the string diff --git a/common/decls/BUILD.bazel b/common/decls/BUILD.bazel index bd3f9ae70..85c7d5c41 100644 --- a/common/decls/BUILD.bazel +++ b/common/decls/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "decls.go", ], - importpath = "github.com/google/cel-go/common/decls", + importpath = "cel.dev/cel-go/common/decls", deps = [ "//checker/decls:go_default_library", "//common:go_default_library", diff --git a/common/decls/decls.go b/common/decls/decls.go index 5c50b8e99..d63a202d6 100644 --- a/common/decls/decls.go +++ b/common/decls/decls.go @@ -20,12 +20,12 @@ import ( "fmt" "strings" - chkdecls "github.com/google/cel-go/checker/decls" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/functions" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + chkdecls "cel.dev/cel-go/checker/decls" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/decls/decls_test.go b/common/decls/decls_test.go index ca26eb690..50ce48e41 100644 --- a/common/decls/decls_test.go +++ b/common/decls/decls_test.go @@ -23,13 +23,13 @@ import ( "google.golang.org/protobuf/proto" - chkdecls "github.com/google/cel-go/checker/decls" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + chkdecls "cel.dev/cel-go/checker/decls" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/env/BUILD.bazel b/common/env/BUILD.bazel index 261da924d..1cd6f9ee7 100644 --- a/common/env/BUILD.bazel +++ b/common/env/BUILD.bazel @@ -25,7 +25,7 @@ go_library( "env.go", "io.go", ], - importpath = "github.com/google/cel-go/common/env", + importpath = "cel.dev/cel-go/common/env", deps = [ "//common:go_default_library", "//common/decls:go_default_library", diff --git a/common/env/env.go b/common/env/env.go index 936036ed2..19e17c5da 100644 --- a/common/env/env.go +++ b/common/env/env.go @@ -22,8 +22,8 @@ import ( "strconv" "strings" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/types" ) // NewConfig creates an instance of a YAML serializable CEL environment configuration. diff --git a/common/env/env_test.go b/common/env/env_test.go index 866807ef1..b2bf70412 100644 --- a/common/env/env_test.go +++ b/common/env/env_test.go @@ -25,11 +25,11 @@ import ( "go.yaml.in/yaml/v3" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" ) func TestConfig(t *testing.T) { diff --git a/common/functions/BUILD.bazel b/common/functions/BUILD.bazel index 3cc27d60c..4511d06f7 100644 --- a/common/functions/BUILD.bazel +++ b/common/functions/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "functions.go", ], - importpath = "github.com/google/cel-go/common/functions", + importpath = "cel.dev/cel-go/common/functions", deps = [ "//common/types/ref:go_default_library", ], diff --git a/common/functions/functions.go b/common/functions/functions.go index 0c00781d9..c5e68571e 100644 --- a/common/functions/functions.go +++ b/common/functions/functions.go @@ -18,7 +18,7 @@ package functions import ( "context" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) // Overload defines a named overload of a function, indicating an operand trait diff --git a/common/operators/BUILD.bazel b/common/operators/BUILD.bazel index b5b67f062..21776cecc 100644 --- a/common/operators/BUILD.bazel +++ b/common/operators/BUILD.bazel @@ -10,5 +10,5 @@ go_library( srcs = [ "operators.go", ], - importpath = "github.com/google/cel-go/common/operators", + importpath = "cel.dev/cel-go/common/operators", ) diff --git a/common/overloads/BUILD.bazel b/common/overloads/BUILD.bazel index e46e2f483..1f1eaa094 100644 --- a/common/overloads/BUILD.bazel +++ b/common/overloads/BUILD.bazel @@ -10,5 +10,5 @@ go_library( srcs = [ "overloads.go", ], - importpath = "github.com/google/cel-go/common/overloads", + importpath = "cel.dev/cel-go/common/overloads", ) diff --git a/common/runes/BUILD.bazel b/common/runes/BUILD.bazel index bb30242cf..0674eaed1 100644 --- a/common/runes/BUILD.bazel +++ b/common/runes/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "buffer.go", ], - importpath = "github.com/google/cel-go/common/runes", + importpath = "cel.dev/cel-go/common/runes", ) go_test( diff --git a/common/source.go b/common/source.go index 9187e9b5c..73237edf9 100644 --- a/common/source.go +++ b/common/source.go @@ -15,7 +15,7 @@ package common import ( - "github.com/google/cel-go/common/runes" + "cel.dev/cel-go/common/runes" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/stdlib/BUILD.bazel b/common/stdlib/BUILD.bazel index 124dbea81..dd34ac1e5 100644 --- a/common/stdlib/BUILD.bazel +++ b/common/stdlib/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "standard.go", ], - importpath = "github.com/google/cel-go/common/stdlib", + importpath = "cel.dev/cel-go/common/stdlib", deps = [ "//common:go_default_library", "//common/decls:go_default_library", diff --git a/common/stdlib/standard.go b/common/stdlib/standard.go index 3a151462e..1c2a54585 100644 --- a/common/stdlib/standard.go +++ b/common/stdlib/standard.go @@ -22,14 +22,14 @@ import ( "strings" "time" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/functions" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" ) var ( diff --git a/common/types/BUILD.bazel b/common/types/BUILD.bazel index 4ecc40031..5de867d8d 100644 --- a/common/types/BUILD.bazel +++ b/common/types/BUILD.bazel @@ -36,7 +36,7 @@ go_library( "unknown.go", "util.go", ], - importpath = "github.com/google/cel-go/common/types", + importpath = "cel.dev/cel-go/common/types", deps = [ "//checker/decls:go_default_library", "//common/overloads:go_default_library", diff --git a/common/types/bool.go b/common/types/bool.go index 5f1e4573e..d16964cdb 100644 --- a/common/types/bool.go +++ b/common/types/bool.go @@ -20,7 +20,7 @@ import ( "strconv" "strings" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/bytes.go b/common/types/bytes.go index 2eefb5d7f..206dd3bc5 100644 --- a/common/types/bytes.go +++ b/common/types/bytes.go @@ -22,7 +22,7 @@ import ( "strings" "unicode/utf8" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/compare.go b/common/types/compare.go index e19682618..caad97b94 100644 --- a/common/types/compare.go +++ b/common/types/compare.go @@ -17,7 +17,7 @@ package types import ( "math" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) func compareDoubleInt(d Double, i Int) Int { diff --git a/common/types/double.go b/common/types/double.go index 02abfee2d..729fc1ec4 100644 --- a/common/types/double.go +++ b/common/types/double.go @@ -21,7 +21,7 @@ import ( "strconv" "strings" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/double_test.go b/common/types/double_test.go index 47e875f29..96f871655 100644 --- a/common/types/double_test.go +++ b/common/types/double_test.go @@ -23,8 +23,8 @@ import ( "google.golang.org/protobuf/proto" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/duration.go b/common/types/duration.go index 220714773..cd3efa1ce 100644 --- a/common/types/duration.go +++ b/common/types/duration.go @@ -21,8 +21,8 @@ import ( "strings" "time" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" dpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/duration_test.go b/common/types/duration_test.go index f0e4509e8..76362d366 100644 --- a/common/types/duration_test.go +++ b/common/types/duration_test.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/proto" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" dpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/err.go b/common/types/err.go index 3216ff1c4..22749de6c 100644 --- a/common/types/err.go +++ b/common/types/err.go @@ -19,7 +19,7 @@ import ( "fmt" "reflect" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) // Error interface which allows types types.Err values to be treated as error values. diff --git a/common/types/format.go b/common/types/format.go index 174a2bd04..3b2c06f0a 100644 --- a/common/types/format.go +++ b/common/types/format.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" ) type formattable interface { diff --git a/common/types/int.go b/common/types/int.go index 60d5a7160..82dbb1738 100644 --- a/common/types/int.go +++ b/common/types/int.go @@ -22,7 +22,7 @@ import ( "strings" "time" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/int_test.go b/common/types/int_test.go index 560c3dfb2..0d573eb29 100644 --- a/common/types/int_test.go +++ b/common/types/int_test.go @@ -24,8 +24,8 @@ import ( "google.golang.org/protobuf/proto" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/iterator.go b/common/types/iterator.go index 98e9147b6..77fc90f42 100644 --- a/common/types/iterator.go +++ b/common/types/iterator.go @@ -18,8 +18,8 @@ import ( "fmt" "reflect" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" ) var ( diff --git a/common/types/json_list_test.go b/common/types/json_list_test.go index 0d8cff093..867b7bbe8 100644 --- a/common/types/json_list_test.go +++ b/common/types/json_list_test.go @@ -19,7 +19,7 @@ import ( "reflect" "testing" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/types/traits" "google.golang.org/protobuf/proto" anypb "google.golang.org/protobuf/types/known/anypb" diff --git a/common/types/list.go b/common/types/list.go index 028770ed6..2d3a20a15 100644 --- a/common/types/list.go +++ b/common/types/list.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/list_test.go b/common/types/list_test.go index ca134b716..acfcc5cde 100644 --- a/common/types/list_test.go +++ b/common/types/list_test.go @@ -24,8 +24,8 @@ import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" dpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/map.go b/common/types/map.go index e4d6f7657..bc643b3d0 100644 --- a/common/types/map.go +++ b/common/types/map.go @@ -24,9 +24,9 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "github.com/google/cel-go/common/types/pb" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/types/pb" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/map_test.go b/common/types/map_test.go index 81989120d..9e7107b23 100644 --- a/common/types/map_test.go +++ b/common/types/map_test.go @@ -26,11 +26,11 @@ import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "github.com/google/cel-go/common/types/pb" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/types/pb" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" tpb "google.golang.org/protobuf/types/known/timestamppb" diff --git a/common/types/native.go b/common/types/native.go index 33c897b43..5efed25ce 100644 --- a/common/types/native.go +++ b/common/types/native.go @@ -23,8 +23,8 @@ import ( "google.golang.org/protobuf/reflect/protoreflect" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" structpb "google.golang.org/protobuf/types/known/structpb" ) diff --git a/common/types/native_test.go b/common/types/native_test.go index 161337538..4989be906 100644 --- a/common/types/native_test.go +++ b/common/types/native_test.go @@ -26,17 +26,17 @@ import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/pb" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" - "github.com/google/cel-go/ext" - "github.com/google/cel-go/test" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/pb" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/ext" + "cel.dev/cel-go/test" structpb "google.golang.org/protobuf/types/known/structpb" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) func TestNativeTypes(t *testing.T) { diff --git a/common/types/null.go b/common/types/null.go index 671e1ee5c..990609c69 100644 --- a/common/types/null.go +++ b/common/types/null.go @@ -21,7 +21,7 @@ import ( "google.golang.org/protobuf/proto" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/null_test.go b/common/types/null_test.go index 97ec24c10..df6e31c8c 100644 --- a/common/types/null_test.go +++ b/common/types/null_test.go @@ -22,7 +22,7 @@ import ( "google.golang.org/protobuf/proto" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" dynamicpb "google.golang.org/protobuf/types/dynamicpb" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/object.go b/common/types/object.go index bb2a09e87..579b84e11 100644 --- a/common/types/object.go +++ b/common/types/object.go @@ -24,8 +24,8 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "github.com/google/cel-go/common/types/pb" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/pb" + "cel.dev/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/object_test.go b/common/types/object_test.go index b2e2207ea..816c1de80 100644 --- a/common/types/object_test.go +++ b/common/types/object_test.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" anypb "google.golang.org/protobuf/types/known/anypb" diff --git a/common/types/optional.go b/common/types/optional.go index 0d861823d..1efccd36d 100644 --- a/common/types/optional.go +++ b/common/types/optional.go @@ -20,7 +20,7 @@ import ( "reflect" "strings" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) var ( diff --git a/common/types/optional_test.go b/common/types/optional_test.go index 89f28d7e7..d874e9a7b 100644 --- a/common/types/optional_test.go +++ b/common/types/optional_test.go @@ -19,7 +19,7 @@ import ( "reflect" "testing" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) func TestOptionalOptionalOf(t *testing.T) { diff --git a/common/types/pb/BUILD.bazel b/common/types/pb/BUILD.bazel index e2b9d37b5..66d333c36 100644 --- a/common/types/pb/BUILD.bazel +++ b/common/types/pb/BUILD.bazel @@ -15,7 +15,7 @@ go_library( "pb.go", "type.go", ], - importpath = "github.com/google/cel-go/common/types/pb", + importpath = "cel.dev/cel-go/common/types/pb", deps = [ "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", "@org_golang_google_protobuf//encoding/protowire:go_default_library", diff --git a/common/types/pb/equal_test.go b/common/types/pb/equal_test.go index 83254e4f6..341dfec0a 100644 --- a/common/types/pb/equal_test.go +++ b/common/types/pb/equal_test.go @@ -20,7 +20,7 @@ import ( "google.golang.org/protobuf/proto" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" anypb "google.golang.org/protobuf/types/known/anypb" ) diff --git a/common/types/pb/file_test.go b/common/types/pb/file_test.go index d0c4da607..fa0db187e 100644 --- a/common/types/pb/file_test.go +++ b/common/types/pb/file_test.go @@ -21,10 +21,10 @@ import ( "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoreflect" - "github.com/google/cel-go/checker/decls" + "cel.dev/cel-go/checker/decls" - proto2pb "github.com/google/cel-go/test/proto2pb" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto2pb "cel.dev/cel-go/test/proto2pb" + proto3pb "cel.dev/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" descpb "google.golang.org/protobuf/types/descriptorpb" ) diff --git a/common/types/pb/pb_test.go b/common/types/pb/pb_test.go index aef1c02b4..e709ad9f7 100644 --- a/common/types/pb/pb_test.go +++ b/common/types/pb/pb_test.go @@ -21,8 +21,8 @@ import ( "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoreflect" - proto2pb "github.com/google/cel-go/test/proto2pb" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto2pb "cel.dev/cel-go/test/proto2pb" + proto3pb "cel.dev/cel-go/test/proto3pb" descpb "google.golang.org/protobuf/types/descriptorpb" dynamicpb "google.golang.org/protobuf/types/dynamicpb" durationpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/pb/type_test.go b/common/types/pb/type_test.go index fed1a3d0d..e859a1a66 100644 --- a/common/types/pb/type_test.go +++ b/common/types/pb/type_test.go @@ -19,11 +19,11 @@ import ( "testing" "time" - "github.com/google/cel-go/checker/decls" + "cel.dev/cel-go/checker/decls" "google.golang.org/protobuf/proto" - proto2pb "github.com/google/cel-go/test/proto2pb" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto2pb "cel.dev/cel-go/test/proto2pb" + proto3pb "cel.dev/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" dynamicpb "google.golang.org/protobuf/types/dynamicpb" anypb "google.golang.org/protobuf/types/known/anypb" diff --git a/common/types/provider.go b/common/types/provider.go index 2321828d8..84c8c9e97 100644 --- a/common/types/provider.go +++ b/common/types/provider.go @@ -24,9 +24,9 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "github.com/google/cel-go/common/types/pb" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/types/pb" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" anypb "google.golang.org/protobuf/types/known/anypb" diff --git a/common/types/provider_test.go b/common/types/provider_test.go index 65f695b11..9033829e4 100644 --- a/common/types/provider_test.go +++ b/common/types/provider_test.go @@ -24,11 +24,11 @@ import ( "testing" "time" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" "google.golang.org/protobuf/proto" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" anypb "google.golang.org/protobuf/types/known/anypb" dpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/ref/BUILD.bazel b/common/types/ref/BUILD.bazel index 79330c332..9df5779d8 100644 --- a/common/types/ref/BUILD.bazel +++ b/common/types/ref/BUILD.bazel @@ -11,7 +11,7 @@ go_library( "provider.go", "reference.go", ], - importpath = "github.com/google/cel-go/common/types/ref", + importpath = "cel.dev/cel-go/common/types/ref", deps = [ "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", "@org_golang_google_protobuf//proto:go_default_library", diff --git a/common/types/string.go b/common/types/string.go index 1335903a7..238b85487 100644 --- a/common/types/string.go +++ b/common/types/string.go @@ -22,8 +22,8 @@ import ( "strings" "time" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/string_test.go b/common/types/string_test.go index daf8fdc54..dc0cf40b2 100644 --- a/common/types/string_test.go +++ b/common/types/string_test.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/proto" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/struct.go b/common/types/struct.go index 61740a70e..0b50895a5 100644 --- a/common/types/struct.go +++ b/common/types/struct.go @@ -17,7 +17,7 @@ package types import ( "reflect" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) // StructTypeDescriptor describes a CEL struct type, providing field metadata and value instantiation. diff --git a/common/types/timestamp.go b/common/types/timestamp.go index aee47f053..a57c6591e 100644 --- a/common/types/timestamp.go +++ b/common/types/timestamp.go @@ -23,8 +23,8 @@ import ( "time" "unicode" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/timestamp_test.go b/common/types/timestamp_test.go index c13b21fef..d88773385 100644 --- a/common/types/timestamp_test.go +++ b/common/types/timestamp_test.go @@ -21,8 +21,8 @@ import ( "testing" "time" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types/ref" "google.golang.org/protobuf/proto" diff --git a/common/types/traits/BUILD.bazel b/common/types/traits/BUILD.bazel index b19eb8301..e2f7d6fdb 100644 --- a/common/types/traits/BUILD.bazel +++ b/common/types/traits/BUILD.bazel @@ -22,7 +22,7 @@ go_library( "traits.go", "zeroer.go", ], - importpath = "github.com/google/cel-go/common/types/traits", + importpath = "cel.dev/cel-go/common/types/traits", deps = [ "//common/types/ref:go_default_library", ], diff --git a/common/types/traits/comparer.go b/common/types/traits/comparer.go index b531d9ae2..83b139535 100644 --- a/common/types/traits/comparer.go +++ b/common/types/traits/comparer.go @@ -15,7 +15,7 @@ package traits import ( - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) // Comparer interface for ordering comparisons between values in order to diff --git a/common/types/traits/container.go b/common/types/traits/container.go index cf5c621ae..357ccd31d 100644 --- a/common/types/traits/container.go +++ b/common/types/traits/container.go @@ -14,7 +14,7 @@ package traits -import "github.com/google/cel-go/common/types/ref" +import "cel.dev/cel-go/common/types/ref" // Container interface which permits containment tests such as 'a in b'. type Container interface { diff --git a/common/types/traits/field_tester.go b/common/types/traits/field_tester.go index 816a95652..d80351b40 100644 --- a/common/types/traits/field_tester.go +++ b/common/types/traits/field_tester.go @@ -15,7 +15,7 @@ package traits import ( - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) // FieldTester indicates if a defined field on an object type is set to a diff --git a/common/types/traits/indexer.go b/common/types/traits/indexer.go index 662c6836c..2d1f72ae2 100644 --- a/common/types/traits/indexer.go +++ b/common/types/traits/indexer.go @@ -15,7 +15,7 @@ package traits import ( - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) // Indexer permits random access of elements by index 'a[b()]'. diff --git a/common/types/traits/iterator.go b/common/types/traits/iterator.go index 91c10f08f..f0a0b4cb9 100644 --- a/common/types/traits/iterator.go +++ b/common/types/traits/iterator.go @@ -15,7 +15,7 @@ package traits import ( - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) // Iterable aggregate types permit traversal over their elements. diff --git a/common/types/traits/lister.go b/common/types/traits/lister.go index e54781a60..67f32391b 100644 --- a/common/types/traits/lister.go +++ b/common/types/traits/lister.go @@ -14,7 +14,7 @@ package traits -import "github.com/google/cel-go/common/types/ref" +import "cel.dev/cel-go/common/types/ref" // Lister interface which aggregates the traits of a list. type Lister interface { diff --git a/common/types/traits/mapper.go b/common/types/traits/mapper.go index d13333f3f..7a4acedc5 100644 --- a/common/types/traits/mapper.go +++ b/common/types/traits/mapper.go @@ -14,7 +14,7 @@ package traits -import "github.com/google/cel-go/common/types/ref" +import "cel.dev/cel-go/common/types/ref" // Mapper interface which aggregates the traits of a maps. type Mapper interface { diff --git a/common/types/traits/matcher.go b/common/types/traits/matcher.go index 085dc94ff..4ea283e90 100644 --- a/common/types/traits/matcher.go +++ b/common/types/traits/matcher.go @@ -14,7 +14,7 @@ package traits -import "github.com/google/cel-go/common/types/ref" +import "cel.dev/cel-go/common/types/ref" // Matcher interface for supporting 'matches()' overloads. type Matcher interface { diff --git a/common/types/traits/math.go b/common/types/traits/math.go index 86d5b9137..cb6354f7e 100644 --- a/common/types/traits/math.go +++ b/common/types/traits/math.go @@ -14,7 +14,7 @@ package traits -import "github.com/google/cel-go/common/types/ref" +import "cel.dev/cel-go/common/types/ref" // Adder interface to support '+' operator overloads. type Adder interface { diff --git a/common/types/traits/receiver.go b/common/types/traits/receiver.go index 8f41db45e..4760192c6 100644 --- a/common/types/traits/receiver.go +++ b/common/types/traits/receiver.go @@ -14,7 +14,7 @@ package traits -import "github.com/google/cel-go/common/types/ref" +import "cel.dev/cel-go/common/types/ref" // Receiver interface for routing instance method calls within a value. type Receiver interface { diff --git a/common/types/traits/sizer.go b/common/types/traits/sizer.go index b80d25137..8e2e3cf92 100644 --- a/common/types/traits/sizer.go +++ b/common/types/traits/sizer.go @@ -15,7 +15,7 @@ package traits import ( - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) // Sizer interface for supporting 'size()' overloads. diff --git a/common/types/type_test.go b/common/types/type_test.go index a63d17057..fbebeeeb3 100644 --- a/common/types/type_test.go +++ b/common/types/type_test.go @@ -17,7 +17,7 @@ package types import ( "testing" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) func TestType_ConvertToType(t *testing.T) { diff --git a/common/types/types.go b/common/types/types.go index 78c77a9b5..fc35bddb4 100644 --- a/common/types/types.go +++ b/common/types/types.go @@ -21,9 +21,9 @@ import ( "google.golang.org/protobuf/proto" - chkdecls "github.com/google/cel-go/checker/decls" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + chkdecls "cel.dev/cel-go/checker/decls" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" celpb "cel.dev/expr" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" diff --git a/common/types/types_test.go b/common/types/types_test.go index c91d7148a..9ea7b8f6c 100644 --- a/common/types/types_test.go +++ b/common/types/types_test.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/proto" - chkdecls "github.com/google/cel-go/checker/decls" - "github.com/google/cel-go/common/types/traits" + chkdecls "cel.dev/cel-go/checker/decls" + "cel.dev/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/types/uint.go b/common/types/uint.go index 91d5369da..6c2bef729 100644 --- a/common/types/uint.go +++ b/common/types/uint.go @@ -21,7 +21,7 @@ import ( "strconv" "strings" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/uint_test.go b/common/types/uint_test.go index 2484fbf36..e31fda8a3 100644 --- a/common/types/uint_test.go +++ b/common/types/uint_test.go @@ -23,8 +23,8 @@ import ( "google.golang.org/protobuf/proto" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/unknown.go b/common/types/unknown.go index f43aff18e..96150fc3f 100644 --- a/common/types/unknown.go +++ b/common/types/unknown.go @@ -23,7 +23,7 @@ import ( "strings" "unicode" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) var ( diff --git a/common/types/unknown_test.go b/common/types/unknown_test.go index 593708950..6957e5846 100644 --- a/common/types/unknown_test.go +++ b/common/types/unknown_test.go @@ -21,7 +21,7 @@ import ( "strings" "testing" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) func TestIsUnknown(t *testing.T) { diff --git a/common/types/util.go b/common/types/util.go index 71662eee3..cb37389f9 100644 --- a/common/types/util.go +++ b/common/types/util.go @@ -15,7 +15,7 @@ package types import ( - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) // IsUnknownOrError returns whether the input element ref.Val is an ErrType or UnknownType. diff --git a/conformance/conformance_test.go b/conformance/conformance_test.go index 417310287..800294019 100644 --- a/conformance/conformance_test.go +++ b/conformance/conformance_test.go @@ -11,12 +11,12 @@ import ( "github.com/bazelbuild/rules_go/go/runfiles" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/ext" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/ext" "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/encoding/prototext" diff --git a/conformance/go.mod b/conformance/go.mod index f5e6a2cc6..e543dea61 100644 --- a/conformance/go.mod +++ b/conformance/go.mod @@ -1,22 +1,28 @@ -module github.com/google/cel-go/conformance +module cel.dev/cel-go/conformance go 1.23.0 require ( + cel.dev/cel-go v0.26.1 + cel.dev/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1 + cel.dev/cel-go/tools v0.0.0-20251023215754-a36d461be521 cel.dev/expr v0.25.1 github.com/bazelbuild/rules_go v0.49.0 - github.com/google/cel-go v0.26.1 github.com/google/go-cmp v0.7.0 google.golang.org/protobuf v1.36.10 ) require ( github.com/antlr4-go/antlr/v4 v4.13.1 // indirect - github.com/stoewer/go-strcase v1.3.1 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect golang.org/x/text v0.22.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250311190419-81fb87f6b8bf // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf // indirect ) -replace github.com/google/cel-go => ./.. +replace cel.dev/cel-go => ./.. + +replace cel.dev/cel-go/policy => ../policy + +replace cel.dev/cel-go/tools => ../tools diff --git a/conformance/go.sum b/conformance/go.sum index 3c87f38d3..a73997939 100644 --- a/conformance/go.sum +++ b/conformance/go.sum @@ -4,35 +4,19 @@ github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYW github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/bazelbuild/rules_go v0.49.0 h1:5vCbuvy8Q11g41lseGJDc5vxhDjJtfxr6nM/IC4VmqM= github.com/bazelbuild/rules_go v0.49.0/go.mod h1:Dhcz716Kqg1RHNWos+N6MlXNkjNP2EwZQ0LukRKJfMs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= -github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw= -google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/api v0.0.0-20250311190419-81fb87f6b8bf h1:BdIVRm+fyDUn8lrZLPSlBCfM/YKDwUBYgDoLv9+DYo0= +google.golang.org/genproto/googleapis/api v0.0.0-20250311190419-81fb87f6b8bf/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf h1:dHDlF3CWxQkefK9IJx+O8ldY0gLygvrlYRBNbPqDWuY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/conformance/policy/policy_conformance_test.go b/conformance/policy/policy_conformance_test.go index d718f1778..21966df04 100644 --- a/conformance/policy/policy_conformance_test.go +++ b/conformance/policy/policy_conformance_test.go @@ -24,12 +24,12 @@ import ( "testing" "github.com/bazelbuild/rules_go/go/runfiles" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/policy" - "github.com/google/cel-go/tools/celtest" - "github.com/google/cel-go/tools/compiler" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/policy" + "cel.dev/cel-go/tools/celtest" + "cel.dev/cel-go/tools/compiler" _ "cel.dev/expr/conformance/proto3" ) diff --git a/examples/README.md b/examples/README.md index 4e739f660..ecb6b33c3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -15,7 +15,7 @@ import ( "fmt" "log" - "github.com/google/cel-go/cel" + "cel.dev/cel-go/cel" ) func main() { diff --git a/examples/example_cel_advanced_test.go b/examples/example_cel_advanced_test.go index a04a9ded5..f0ee5e736 100644 --- a/examples/example_cel_advanced_test.go +++ b/examples/example_cel_advanced_test.go @@ -17,7 +17,7 @@ package examples import ( "fmt" - "github.com/google/cel-go/cel" + "cel.dev/cel-go/cel" ) // Example_cel_CommonErrors showcases handling common runtime errors (division by zero, index out of bounds, missing key) diff --git a/examples/example_cel_collections_test.go b/examples/example_cel_collections_test.go index 93c2110b6..c2e6cfa88 100644 --- a/examples/example_cel_collections_test.go +++ b/examples/example_cel_collections_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "github.com/google/cel-go/cel" + "cel.dev/cel-go/cel" ) // Example_cel_Collections showcases membership, indexing, search (exists), all, and filter diff --git a/examples/example_cel_compile_test.go b/examples/example_cel_compile_test.go index 7a7f3a42c..f5217c420 100644 --- a/examples/example_cel_compile_test.go +++ b/examples/example_cel_compile_test.go @@ -18,8 +18,8 @@ import ( "fmt" "log" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/ext" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/ext" ) // Example_cel_Compile showcases compiling a CEL expression with variable declarations diff --git a/examples/example_cel_context_eval_test.go b/examples/example_cel_context_eval_test.go index 18f51d712..3d5c0fb4b 100644 --- a/examples/example_cel_context_eval_test.go +++ b/examples/example_cel_context_eval_test.go @@ -19,7 +19,7 @@ import ( "fmt" "log" - "github.com/google/cel-go/cel" + "cel.dev/cel-go/cel" ) // Example_cel_ContextEval showcases evaluation cancellation and timeout using ContextEval diff --git a/examples/example_cel_custom_functions_test.go b/examples/example_cel_custom_functions_test.go index 0664ff0d3..11fcf5394 100644 --- a/examples/example_cel_custom_functions_test.go +++ b/examples/example_cel_custom_functions_test.go @@ -18,9 +18,9 @@ import ( "fmt" "log" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // Example_cel_Overload showcases defining custom global functions with cel.Overload diff --git a/examples/example_cel_custom_macros_test.go b/examples/example_cel_custom_macros_test.go index b970415f4..387c9a849 100644 --- a/examples/example_cel_custom_macros_test.go +++ b/examples/example_cel_custom_macros_test.go @@ -18,12 +18,12 @@ import ( "fmt" "log" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/parser" ) // Example_cel_CustomMacros showcases defining custom AST transformation macros diff --git a/examples/example_cel_execution_cost_test.go b/examples/example_cel_execution_cost_test.go index 69e6411b5..123c8b747 100644 --- a/examples/example_cel_execution_cost_test.go +++ b/examples/example_cel_execution_cost_test.go @@ -19,9 +19,9 @@ import ( "log" "strings" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/types/ref" ) type exampleCostEstimator struct { diff --git a/examples/example_cel_logic_and_conditions_test.go b/examples/example_cel_logic_and_conditions_test.go index ed3d9792c..2456e06c1 100644 --- a/examples/example_cel_logic_and_conditions_test.go +++ b/examples/example_cel_logic_and_conditions_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "github.com/google/cel-go/cel" + "cel.dev/cel-go/cel" ) // Example_cel_LogicAndConditions showcases logical operators, conditional (ternary) operator, and evaluation diff --git a/examples/example_cel_native_structs_test.go b/examples/example_cel_native_structs_test.go index 076a254db..b2b164eac 100644 --- a/examples/example_cel_native_structs_test.go +++ b/examples/example_cel_native_structs_test.go @@ -19,8 +19,8 @@ import ( "log" "reflect" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/ext" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/ext" ) type User struct { diff --git a/examples/example_cel_operators_test.go b/examples/example_cel_operators_test.go index 895079af3..2d66a2c5d 100644 --- a/examples/example_cel_operators_test.go +++ b/examples/example_cel_operators_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "github.com/google/cel-go/cel" + "cel.dev/cel-go/cel" ) // Example_cel_Arithmetic showcases negation, basic operations, modulo, and precedence diff --git a/examples/example_cel_protocol_buffers_test.go b/examples/example_cel_protocol_buffers_test.go index 26a46bc6c..30b32d7fa 100644 --- a/examples/example_cel_protocol_buffers_test.go +++ b/examples/example_cel_protocol_buffers_test.go @@ -18,8 +18,8 @@ import ( "fmt" "log" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/test/proto3pb" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/test/proto3pb" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/wrapperspb" ) diff --git a/examples/example_cel_strings_and_numbers_test.go b/examples/example_cel_strings_and_numbers_test.go index 8be7cc820..b61a244b0 100644 --- a/examples/example_cel_strings_and_numbers_test.go +++ b/examples/example_cel_strings_and_numbers_test.go @@ -18,8 +18,8 @@ import ( "fmt" "log" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/ext" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/ext" ) // Example_cel_StringsAndNumbers showcases string functions, concatenation, and numeric comparisons diff --git a/examples/example_cel_time_test.go b/examples/example_cel_time_test.go index 448e6a977..cfe34b96b 100644 --- a/examples/example_cel_time_test.go +++ b/examples/example_cel_time_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "github.com/google/cel-go/cel" + "cel.dev/cel-go/cel" ) // Example_cel_TimestampsAndDurations showcases timestamps, durations, arithmetic, and field access diff --git a/examples/example_cel_transforming_data_test.go b/examples/example_cel_transforming_data_test.go index 594582cf8..d27a4b4dc 100644 --- a/examples/example_cel_transforming_data_test.go +++ b/examples/example_cel_transforming_data_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "github.com/google/cel-go/cel" + "cel.dev/cel-go/cel" ) // Example_cel_TransformingData showcases building maps, transforming lists with map(), diff --git a/examples/example_cel_type_conversions_test.go b/examples/example_cel_type_conversions_test.go index 1681e6c0a..3a816dec1 100644 --- a/examples/example_cel_type_conversions_test.go +++ b/examples/example_cel_type_conversions_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "github.com/google/cel-go/cel" + "cel.dev/cel-go/cel" ) // Example_cel_TypeConversions showcases type casting functions (int, uint, double, string, bytes, dyn) diff --git a/ext/BUILD.bazel b/ext/BUILD.bazel index f362fd97b..4e63a832f 100644 --- a/ext/BUILD.bazel +++ b/ext/BUILD.bazel @@ -24,7 +24,7 @@ go_library( "sets.go", "strings.go", ], - importpath = "github.com/google/cel-go/ext", + importpath = "cel.dev/cel-go/ext", visibility = ["//visibility:public"], deps = [ "//cel:go_default_library", diff --git a/ext/bindings.go b/ext/bindings.go index 89766d60a..1bf97c59c 100644 --- a/ext/bindings.go +++ b/ext/bindings.go @@ -22,12 +22,12 @@ import ( "strings" "sync" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" - "github.com/google/cel-go/interpreter" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/interpreter" ) // Bindings returns a cel.EnvOption to configure support for local variable diff --git a/ext/bindings_test.go b/ext/bindings_test.go index 4999cc416..ced229716 100644 --- a/ext/bindings_test.go +++ b/ext/bindings_test.go @@ -20,14 +20,14 @@ import ( "sync" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "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/test" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/interpreter" + "cel.dev/cel-go/test" ) var bindingTests = []struct { diff --git a/ext/comprehensions.go b/ext/comprehensions.go index adb22912b..d01524bb1 100644 --- a/ext/comprehensions.go +++ b/ext/comprehensions.go @@ -18,13 +18,13 @@ import ( "fmt" "math" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/parser" ) const ( diff --git a/ext/comprehensions_test.go b/ext/comprehensions_test.go index 986547d97..075c2af38 100644 --- a/ext/comprehensions_test.go +++ b/ext/comprehensions_test.go @@ -19,10 +19,10 @@ import ( "strings" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/interpreter" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/interpreter" ) func TestTwoVarComprehensions(t *testing.T) { diff --git a/ext/costs.go b/ext/costs.go index d2cf7c757..5a2c6c085 100644 --- a/ext/costs.go +++ b/ext/costs.go @@ -17,12 +17,12 @@ package ext import ( "math" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" ) var ( diff --git a/ext/encoders.go b/ext/encoders.go index 97fc932a5..eca542666 100644 --- a/ext/encoders.go +++ b/ext/encoders.go @@ -20,11 +20,11 @@ import ( "fmt" "math" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/interpreter" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/interpreter" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/structpb" ) diff --git a/ext/encoders_test.go b/ext/encoders_test.go index dde2200da..1d526a15d 100644 --- a/ext/encoders_test.go +++ b/ext/encoders_test.go @@ -20,8 +20,8 @@ import ( "strings" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" ) func TestEncoders(t *testing.T) { diff --git a/ext/extension_option_factory.go b/ext/extension_option_factory.go index e68cf5bc7..6aeb157bc 100644 --- a/ext/extension_option_factory.go +++ b/ext/extension_option_factory.go @@ -17,8 +17,8 @@ package ext import ( "fmt" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/env" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/env" ) // ExtensionOptionFactory converts an ExtensionConfig value to a CEL environment option. diff --git a/ext/extension_option_factory_test.go b/ext/extension_option_factory_test.go index 603aedf53..260bf795b 100644 --- a/ext/extension_option_factory_test.go +++ b/ext/extension_option_factory_test.go @@ -18,8 +18,8 @@ import ( "fmt" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/env" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/env" ) func TestExtensionOptionFactoryInvalidExtension(t *testing.T) { diff --git a/ext/formatting.go b/ext/formatting.go index 35fb17048..f8633dbc0 100644 --- a/ext/formatting.go +++ b/ext/formatting.go @@ -26,12 +26,12 @@ import ( "golang.org/x/text/language" "golang.org/x/text/message" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" ) type clauseImpl func(ref.Val, string) (string, error) diff --git a/ext/formatting_test.go b/ext/formatting_test.go index e77e3a936..c72316588 100644 --- a/ext/formatting_test.go +++ b/ext/formatting_test.go @@ -24,12 +24,12 @@ import ( "google.golang.org/protobuf/proto" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) func TestStringFormat(t *testing.T) { diff --git a/ext/formatting_v2.go b/ext/formatting_v2.go index f923cc7e1..969a779e1 100644 --- a/ext/formatting_v2.go +++ b/ext/formatting_v2.go @@ -24,11 +24,11 @@ import ( "time" "unicode" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" ) type clauseImplV2 func(ref.Val) (string, error) diff --git a/ext/formatting_v2_test.go b/ext/formatting_v2_test.go index a183dba38..71c96576a 100644 --- a/ext/formatting_v2_test.go +++ b/ext/formatting_v2_test.go @@ -24,12 +24,12 @@ import ( "google.golang.org/protobuf/proto" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) func TestStringsWithExtensionV2(t *testing.T) { diff --git a/ext/guards.go b/ext/guards.go index 1461c0416..83e64e89a 100644 --- a/ext/guards.go +++ b/ext/guards.go @@ -15,9 +15,9 @@ package ext import ( - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // function invocation guards for common call signatures within extension functions. diff --git a/ext/lists.go b/ext/lists.go index 2196d218c..873428837 100644 --- a/ext/lists.go +++ b/ext/lists.go @@ -19,16 +19,16 @@ import ( "math" "sort" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "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/common/types/traits" - "github.com/google/cel-go/interpreter" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/interpreter" + "cel.dev/cel-go/parser" ) var comparableTypes = []*cel.Type{ diff --git a/ext/lists_test.go b/ext/lists_test.go index bf01801cb..8b68cf4bd 100644 --- a/ext/lists_test.go +++ b/ext/lists_test.go @@ -19,11 +19,11 @@ import ( "strings" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/types" - proto2pb "github.com/google/cel-go/test/proto2pb" + proto2pb "cel.dev/cel-go/test/proto2pb" ) func TestLists(t *testing.T) { diff --git a/ext/math.go b/ext/math.go index e67b205de..a706dc199 100644 --- a/ext/math.go +++ b/ext/math.go @@ -19,13 +19,13 @@ import ( "math" "strings" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" - "github.com/google/cel-go/interpreter" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/interpreter" ) // Math returns a cel.EnvOption to configure namespaced math helper macros and diff --git a/ext/math_test.go b/ext/math_test.go index 8b47cae64..815d113fc 100644 --- a/ext/math_test.go +++ b/ext/math_test.go @@ -19,9 +19,9 @@ import ( "strings" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/types" ) func TestMath(t *testing.T) { diff --git a/ext/native.go b/ext/native.go index 2598b38d6..213a2443e 100644 --- a/ext/native.go +++ b/ext/native.go @@ -15,8 +15,8 @@ package ext import ( - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/types" ) // NativeTypesOption is a functional interface for configuring handling of native types. diff --git a/ext/native_test.go b/ext/native_test.go index c6af35c9a..a5c870044 100644 --- a/ext/native_test.go +++ b/ext/native_test.go @@ -26,16 +26,16 @@ import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/pb" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" - "github.com/google/cel-go/test" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/pb" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/test" structpb "google.golang.org/protobuf/types/known/structpb" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) func TestNativeTypes(t *testing.T) { diff --git a/ext/network.go b/ext/network.go index bca065707..d542dda9c 100644 --- a/ext/network.go +++ b/ext/network.go @@ -20,12 +20,12 @@ import ( "net/netip" "reflect" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/interpreter" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/interpreter" ) const ( diff --git a/ext/network_test.go b/ext/network_test.go index 592c07494..993c6ef08 100644 --- a/ext/network_test.go +++ b/ext/network_test.go @@ -19,9 +19,9 @@ import ( "reflect" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/types" ) func TestNetwork_Success(t *testing.T) { diff --git a/ext/protos.go b/ext/protos.go index b09db25b0..0dc2f6a0d 100644 --- a/ext/protos.go +++ b/ext/protos.go @@ -17,8 +17,8 @@ package ext import ( "math" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/ast" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/ast" ) // Protos returns a cel.EnvOption to configure extended macros and functions for diff --git a/ext/protos_test.go b/ext/protos_test.go index 739dddf3d..6c2fce992 100644 --- a/ext/protos_test.go +++ b/ext/protos_test.go @@ -20,13 +20,13 @@ import ( "google.golang.org/protobuf/proto" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" - "github.com/google/cel-go/test" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/test" - proto2pb "github.com/google/cel-go/test/proto2pb" + proto2pb "cel.dev/cel-go/test/proto2pb" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" ) diff --git a/ext/regex.go b/ext/regex.go index bd222f170..a8fedc72f 100644 --- a/ext/regex.go +++ b/ext/regex.go @@ -22,12 +22,12 @@ import ( "strconv" "strings" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/interpreter" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/interpreter" ) const ( diff --git a/ext/regex_test.go b/ext/regex_test.go index 9a61b7946..6c7b401e0 100644 --- a/ext/regex_test.go +++ b/ext/regex_test.go @@ -19,8 +19,8 @@ import ( "strings" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" ) func TestRegex(t *testing.T) { diff --git a/ext/sets.go b/ext/sets.go index 63c019ad9..f087ae67f 100644 --- a/ext/sets.go +++ b/ext/sets.go @@ -15,14 +15,14 @@ package ext import ( - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" - "github.com/google/cel-go/interpreter" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/interpreter" ) // Sets returns a cel.EnvOption to configure namespaced set relationship diff --git a/ext/sets_test.go b/ext/sets_test.go index 4ea409b67..161c2ae9a 100644 --- a/ext/sets_test.go +++ b/ext/sets_test.go @@ -20,12 +20,12 @@ import ( "strings" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) func TestSets(t *testing.T) { diff --git a/ext/strings.go b/ext/strings.go index 1f7732f2f..a77e527d2 100644 --- a/ext/strings.go +++ b/ext/strings.go @@ -27,13 +27,13 @@ import ( "golang.org/x/text/language" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" - "github.com/google/cel-go/interpreter" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/interpreter" ) const ( diff --git a/ext/strings_test.go b/ext/strings_test.go index 276a3efd5..8e9e01b11 100644 --- a/ext/strings_test.go +++ b/ext/strings_test.go @@ -21,10 +21,10 @@ import ( "time" "unicode/utf8" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // TODO: move these tests to a conformance test. diff --git a/go.mod b/go.mod index ad223de71..188cf7bca 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/google/cel-go +module cel.dev/cel-go go 1.23.0 diff --git a/interpreter/BUILD.bazel b/interpreter/BUILD.bazel index 40ac2ba69..3ec8f4c02 100644 --- a/interpreter/BUILD.bazel +++ b/interpreter/BUILD.bazel @@ -23,7 +23,7 @@ go_library( "prune.go", "runtimecost.go", ], - importpath = "github.com/google/cel-go/interpreter", + importpath = "cel.dev/cel-go/interpreter", deps = [ "//common:go_default_library", "//common/ast:go_default_library", diff --git a/interpreter/activation.go b/interpreter/activation.go index bc9296ed4..15cbd3002 100644 --- a/interpreter/activation.go +++ b/interpreter/activation.go @@ -18,7 +18,7 @@ import ( "errors" "fmt" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) // Activation used to resolve identifiers by name and references by id. diff --git a/interpreter/activation_test.go b/interpreter/activation_test.go index b0fed2102..21cf93636 100644 --- a/interpreter/activation_test.go +++ b/interpreter/activation_test.go @@ -18,8 +18,8 @@ import ( "testing" "time" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) func TestActivation(t *testing.T) { diff --git a/interpreter/async.go b/interpreter/async.go index 4e391196b..a678705a7 100644 --- a/interpreter/async.go +++ b/interpreter/async.go @@ -23,9 +23,9 @@ import ( "sync" "sync/atomic" - "github.com/google/cel-go/common/functions" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // Async extension function support. diff --git a/interpreter/async_test.go b/interpreter/async_test.go index 242a93310..228eddb14 100644 --- a/interpreter/async_test.go +++ b/interpreter/async_test.go @@ -24,15 +24,15 @@ import ( "testing" "time" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/containers" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/functions" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/parser" ) // asyncReturning returns an AsyncOp that immediately produces the given value, while counting the diff --git a/interpreter/attribute_patterns.go b/interpreter/attribute_patterns.go index bbaca5226..8e33ddd23 100644 --- a/interpreter/attribute_patterns.go +++ b/interpreter/attribute_patterns.go @@ -18,9 +18,9 @@ import ( "fmt" "strings" - "github.com/google/cel-go/common/containers" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // AttributePattern represents a top-level variable with an optional set of qualifier patterns. diff --git a/interpreter/attribute_patterns_test.go b/interpreter/attribute_patterns_test.go index 9fbbc7d9d..99be5604c 100644 --- a/interpreter/attribute_patterns_test.go +++ b/interpreter/attribute_patterns_test.go @@ -18,8 +18,8 @@ import ( "fmt" "testing" - "github.com/google/cel-go/common/containers" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/types" ) // attr describes a simplified format for specifying common Attribute and Qualifier values for diff --git a/interpreter/attributes.go b/interpreter/attributes.go index 26d8eb0f3..ce344eb62 100644 --- a/interpreter/attributes.go +++ b/interpreter/attributes.go @@ -18,10 +18,10 @@ import ( "fmt" "strings" - "github.com/google/cel-go/common/containers" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" ) // AttributeFactory provides methods creating Attribute and Qualifier values. diff --git a/interpreter/attributes_test.go b/interpreter/attributes_test.go index 1dea98d9a..35582f676 100644 --- a/interpreter/attributes_test.go +++ b/interpreter/attributes_test.go @@ -20,19 +20,19 @@ import ( "reflect" "testing" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/containers" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/stdlib" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/stdlib" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" anypb "google.golang.org/protobuf/types/known/anypb" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) func TestAttributesAbsoluteAttr(t *testing.T) { diff --git a/interpreter/decorators.go b/interpreter/decorators.go index 6c48e5c1b..7402d18fa 100644 --- a/interpreter/decorators.go +++ b/interpreter/decorators.go @@ -17,10 +17,10 @@ package interpreter import ( "fmt" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" ) // InterpretableDecorator is a functional interface for decorating or replacing diff --git a/interpreter/dispatcher.go b/interpreter/dispatcher.go index 8f0bdb7b8..40efe3009 100644 --- a/interpreter/dispatcher.go +++ b/interpreter/dispatcher.go @@ -17,7 +17,7 @@ package interpreter import ( "fmt" - "github.com/google/cel-go/common/functions" + "cel.dev/cel-go/common/functions" ) // Dispatcher resolves function calls to their appropriate overload. diff --git a/interpreter/evalstate.go b/interpreter/evalstate.go index 4bdd1fdc7..c1ee6ea1c 100644 --- a/interpreter/evalstate.go +++ b/interpreter/evalstate.go @@ -15,7 +15,7 @@ package interpreter import ( - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/ref" ) // EvalState tracks the values associated with expression ids during execution. diff --git a/interpreter/frame.go b/interpreter/frame.go index 20ab313c8..2fd93052d 100644 --- a/interpreter/frame.go +++ b/interpreter/frame.go @@ -21,9 +21,9 @@ import ( "sync" "sync/atomic" - "github.com/google/cel-go/common/functions" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // evalContext contains the stateful information needed for a single evaluation. diff --git a/interpreter/frame_test.go b/interpreter/frame_test.go index fb9d629cc..a1bb7bbf6 100644 --- a/interpreter/frame_test.go +++ b/interpreter/frame_test.go @@ -18,8 +18,8 @@ import ( "context" "testing" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) func TestFrameCheckInterrupt(t *testing.T) { diff --git a/interpreter/functions/BUILD.bazel b/interpreter/functions/BUILD.bazel index 4a80c3ea0..e438b126b 100644 --- a/interpreter/functions/BUILD.bazel +++ b/interpreter/functions/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "functions.go", ], - importpath = "github.com/google/cel-go/interpreter/functions", + importpath = "cel.dev/cel-go/interpreter/functions", deps = [ "//common/functions:go_default_library", ], diff --git a/interpreter/functions/functions.go b/interpreter/functions/functions.go index 21ffb6924..02123f1ab 100644 --- a/interpreter/functions/functions.go +++ b/interpreter/functions/functions.go @@ -16,7 +16,7 @@ // interpreter and as declared within the checker#StandardDeclarations. package functions -import fn "github.com/google/cel-go/common/functions" +import fn "cel.dev/cel-go/common/functions" // Overload defines a named overload of a function, indicating an operand trait // which must be present on the first argument to the overload as well as one diff --git a/interpreter/interpretable.go b/interpreter/interpretable.go index 906c4f805..d17e51d10 100644 --- a/interpreter/interpretable.go +++ b/interpreter/interpretable.go @@ -18,12 +18,12 @@ import ( "fmt" "sync" - "github.com/google/cel-go/common/functions" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" ) // Interpretable evaluates an Activation and produces a value. diff --git a/interpreter/interpreter.go b/interpreter/interpreter.go index 29df9d41e..f2ece5088 100644 --- a/interpreter/interpreter.go +++ b/interpreter/interpreter.go @@ -20,10 +20,10 @@ package interpreter import ( "errors" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/containers" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // PlannerOption configures the program plan options during interpretable setup. diff --git a/interpreter/interpreter_test.go b/interpreter/interpreter_test.go index 2c3880251..76e9e1b41 100644 --- a/interpreter/interpreter_test.go +++ b/interpreter/interpreter_test.go @@ -25,26 +25,26 @@ import ( "testing" "time" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common" - "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/functions" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/stdlib" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/stdlib" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" structpb "google.golang.org/protobuf/types/known/structpb" tpb "google.golang.org/protobuf/types/known/timestamppb" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" - proto2pb "github.com/google/cel-go/test/proto2pb" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto2pb "cel.dev/cel-go/test/proto2pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) type testCase struct { diff --git a/interpreter/optimizations.go b/interpreter/optimizations.go index 2fc87e693..c6478fa72 100644 --- a/interpreter/optimizations.go +++ b/interpreter/optimizations.go @@ -17,8 +17,8 @@ package interpreter import ( "regexp" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // MatchesRegexOptimization optimizes the 'matches' standard library function by compiling the regex pattern and diff --git a/interpreter/planner.go b/interpreter/planner.go index 396a9803f..5b39c2af2 100644 --- a/interpreter/planner.go +++ b/interpreter/planner.go @@ -18,11 +18,11 @@ import ( "fmt" "strings" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/containers" - "github.com/google/cel-go/common/functions" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/types" ) // newPlanner creates an interpretablePlanner which references a Dispatcher, TypeProvider, diff --git a/interpreter/prune.go b/interpreter/prune.go index 1662c1c1b..9f55f1a81 100644 --- a/interpreter/prune.go +++ b/interpreter/prune.go @@ -15,12 +15,12 @@ package interpreter import ( - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" ) type astPruner struct { diff --git a/interpreter/prune_test.go b/interpreter/prune_test.go index 3d12abc5a..631d8195d 100644 --- a/interpreter/prune_test.go +++ b/interpreter/prune_test.go @@ -17,17 +17,17 @@ package interpreter import ( "testing" - "github.com/google/cel-go/common" - "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/common/types/traits" - "github.com/google/cel-go/parser" - "github.com/google/cel-go/test" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" + "cel.dev/cel-go/parser" + "cel.dev/cel-go/test" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) type testInfo struct { diff --git a/interpreter/runtimecost.go b/interpreter/runtimecost.go index 81e4ef63c..723eaf686 100644 --- a/interpreter/runtimecost.go +++ b/interpreter/runtimecost.go @@ -18,11 +18,11 @@ import ( "errors" "math" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/common/types/traits" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/common/types/traits" ) // WARNING: Any changes to cost calculations in this file require a corresponding change in checker/cost.go diff --git a/interpreter/runtimecost_test.go b/interpreter/runtimecost_test.go index 597d73ef9..b160318b7 100644 --- a/interpreter/runtimecost_test.go +++ b/interpreter/runtimecost_test.go @@ -23,16 +23,16 @@ import ( "testing" "time" - "github.com/google/cel-go/checker" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/containers" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/overloads" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/checker" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/overloads" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/parser" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) func TestTrackCostAdvanced(t *testing.T) { diff --git a/parser/BUILD.bazel b/parser/BUILD.bazel index 97bc9bd43..3c46add7b 100644 --- a/parser/BUILD.bazel +++ b/parser/BUILD.bazel @@ -16,7 +16,7 @@ go_library( "unescape.go", "unparser.go", ], - importpath = "github.com/google/cel-go/parser", + importpath = "cel.dev/cel-go/parser", visibility = ["//visibility:public"], deps = [ "//common:go_default_library", diff --git a/parser/errors.go b/parser/errors.go index c3cec01a8..968dbe82b 100644 --- a/parser/errors.go +++ b/parser/errors.go @@ -15,7 +15,7 @@ package parser import ( - "github.com/google/cel-go/common" + "cel.dev/cel-go/common" ) // parseErrors is a specialization of Errors. diff --git a/parser/gen/BUILD.bazel b/parser/gen/BUILD.bazel index 3efed87b7..6c0187d58 100644 --- a/parser/gen/BUILD.bazel +++ b/parser/gen/BUILD.bazel @@ -19,7 +19,7 @@ go_library( "CEL.tokens", "CELLexer.tokens", ], - importpath = "github.com/google/cel-go/parser/gen", + importpath = "cel.dev/cel-go/parser/gen", deps = [ "@com_github_antlr4_go_antlr_v4//:go_default_library", ], diff --git a/parser/helper.go b/parser/helper.go index 84bef80d5..b043ef54b 100644 --- a/parser/helper.go +++ b/parser/helper.go @@ -19,10 +19,10 @@ import ( antlr "github.com/antlr4-go/antlr/v4" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) type parserHelper struct { diff --git a/parser/helper_test.go b/parser/helper_test.go index d6602bc7b..22b2216fb 100644 --- a/parser/helper_test.go +++ b/parser/helper_test.go @@ -17,8 +17,8 @@ package parser import ( "testing" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" "google.golang.org/protobuf/proto" ) diff --git a/parser/input.go b/parser/input.go index 44792455d..9ccccd0fc 100644 --- a/parser/input.go +++ b/parser/input.go @@ -17,7 +17,7 @@ package parser import ( antlr "github.com/antlr4-go/antlr/v4" - "github.com/google/cel-go/common/runes" + "cel.dev/cel-go/common/runes" ) type charStream struct { diff --git a/parser/macro.go b/parser/macro.go index 1ef43c4b5..b9f53d7ba 100644 --- a/parser/macro.go +++ b/parser/macro.go @@ -17,11 +17,11 @@ package parser import ( "fmt" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // MacroOpt defines a functional option for configuring macro behavior. diff --git a/parser/macro_test.go b/parser/macro_test.go index 1d056f644..988138d1e 100644 --- a/parser/macro_test.go +++ b/parser/macro_test.go @@ -17,8 +17,8 @@ package parser import ( "testing" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" ) func TestReceiverVarArgMacro(t *testing.T) { diff --git a/parser/parser.go b/parser/parser.go index 338233543..2df20a704 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -25,12 +25,12 @@ import ( antlr "github.com/antlr4-go/antlr/v4" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/runes" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/parser/gen" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/runes" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/parser/gen" ) // Parser encapsulates the context necessary to perform parsing for different expressions. diff --git a/parser/parser_test.go b/parser/parser_test.go index 88527d813..fd08f1666 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -21,11 +21,11 @@ import ( "strings" "testing" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/debug" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/test" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/debug" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/test" ) var testCases = []testInfo{ diff --git a/parser/unparser.go b/parser/unparser.go index d503a450e..8b5fdbdcc 100644 --- a/parser/unparser.go +++ b/parser/unparser.go @@ -21,10 +21,10 @@ import ( "strconv" "strings" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // Unparse takes an input expression and source position information and generates a human-readable diff --git a/parser/unparser_test.go b/parser/unparser_test.go index 7862e5ca0..6d0accfe9 100644 --- a/parser/unparser_test.go +++ b/parser/unparser_test.go @@ -19,9 +19,9 @@ import ( "strings" "testing" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" "google.golang.org/protobuf/proto" diff --git a/policy/BUILD.bazel b/policy/BUILD.bazel index d443c6d7c..de3775aad 100644 --- a/policy/BUILD.bazel +++ b/policy/BUILD.bazel @@ -37,7 +37,7 @@ go_library( "test_tag_handler_k8s.go", "yaml.go", ], - importpath = "github.com/google/cel-go/policy", + importpath = "cel.dev/cel-go/policy", deps = [ "//cel:go_default_library", "//common:go_default_library", diff --git a/policy/compiler.go b/policy/compiler.go index 2ca84e0a2..0faea4498 100644 --- a/policy/compiler.go +++ b/policy/compiler.go @@ -19,13 +19,13 @@ package policy import ( "fmt" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common" - "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" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/containers" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // CompiledRule represents the variables and match blocks associated with a rule block. diff --git a/policy/compiler_test.go b/policy/compiler_test.go index 6f9bc4f3b..44adbdfc0 100644 --- a/policy/compiler_test.go +++ b/policy/compiler_test.go @@ -23,11 +23,11 @@ import ( "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/ext" - "github.com/google/cel-go/interpreter" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/ext" + "cel.dev/cel-go/interpreter" "github.com/google/go-cmp/cmp" ) diff --git a/policy/composer.go b/policy/composer.go index ef392184f..79d411ace 100644 --- a/policy/composer.go +++ b/policy/composer.go @@ -20,11 +20,11 @@ import ( "slices" "strings" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/types" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/types" ) // ComposerOption is a functional option used to configure a RuleComposer diff --git a/policy/composer_test.go b/policy/composer_test.go index 5cd601c2c..237dd98e0 100644 --- a/policy/composer_test.go +++ b/policy/composer_test.go @@ -4,10 +4,10 @@ import ( "strings" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/debug" - "github.com/google/cel-go/ext" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/debug" + "cel.dev/cel-go/ext" ) func TestCompose_SourceInfo(t *testing.T) { diff --git a/policy/config.go b/policy/config.go index 02243922b..33ccd998b 100644 --- a/policy/config.go +++ b/policy/config.go @@ -15,9 +15,9 @@ package policy import ( - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/env" - "github.com/google/cel-go/ext" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/env" + "cel.dev/cel-go/ext" ) // FromConfig configures a CEL policy environment from a config file. diff --git a/policy/config_test.go b/policy/config_test.go index 77fcce274..a8fb741d7 100644 --- a/policy/config_test.go +++ b/policy/config_test.go @@ -17,12 +17,12 @@ package policy import ( "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/env" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/env" "go.yaml.in/yaml/v3" - proto3pb "github.com/google/cel-go/test/proto3pb" + proto3pb "cel.dev/cel-go/test/proto3pb" ) func TestConfig(t *testing.T) { diff --git a/policy/go.mod b/policy/go.mod index 6fe07141a..ce093cae2 100644 --- a/policy/go.mod +++ b/policy/go.mod @@ -1,10 +1,10 @@ -module github.com/google/cel-go/policy +module cel.dev/cel-go/policy go 1.23.0 require ( - github.com/google/cel-go v0.26.1 - github.com/google/cel-go/tools v0.0.0-20251023215754-a36d461be521 + cel.dev/cel-go v0.26.1 + cel.dev/cel-go/tools v0.0.0-20251023215754-a36d461be521 github.com/google/go-cmp v0.7.0 go.yaml.in/yaml/v3 v3.0.4 google.golang.org/protobuf v1.36.10 @@ -19,6 +19,6 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf // indirect ) -replace github.com/google/cel-go => ../. +replace cel.dev/cel-go => ../. -replace github.com/google/cel-go/tools => ../tools/. +replace cel.dev/cel-go/tools => ../tools/. diff --git a/policy/helper_test.go b/policy/helper_test.go index fbb62b55a..225f25a70 100644 --- a/policy/helper_test.go +++ b/policy/helper_test.go @@ -19,11 +19,11 @@ import ( "os" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/env" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/test" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/env" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/test" "go.yaml.in/yaml/v3" diff --git a/policy/parser.go b/policy/parser.go index a42c7a3b9..11e7eb7c1 100644 --- a/policy/parser.go +++ b/policy/parser.go @@ -20,9 +20,9 @@ import ( "go.yaml.in/yaml/v3" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/ast" ) type semanticType int diff --git a/policy/parser_test.go b/policy/parser_test.go index f407a8600..47bf0b348 100644 --- a/policy/parser_test.go +++ b/policy/parser_test.go @@ -18,9 +18,9 @@ import ( "fmt" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/ext" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/ext" "github.com/google/go-cmp/cmp" "go.yaml.in/yaml/v3" ) diff --git a/policy/source.go b/policy/source.go index 41abd19bd..d07f91f1f 100644 --- a/policy/source.go +++ b/policy/source.go @@ -15,7 +15,7 @@ package policy import ( - "github.com/google/cel-go/common" + "cel.dev/cel-go/common" ) // ByteSource converts a byte sequence and location description to a model.Source. diff --git a/policy/test/cel_test_runner.go b/policy/test/cel_test_runner.go index 95269e13c..be5850b4a 100644 --- a/policy/test/cel_test_runner.go +++ b/policy/test/cel_test_runner.go @@ -18,10 +18,10 @@ import ( "os" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/tools/celtest" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/tools/celtest" ) // TestCEL triggers the celtest test runner with a list of custom options which are used to set up diff --git a/policy/test/k8s_cel_test_runner.go b/policy/test/k8s_cel_test_runner.go index 26e6ad114..6380ecafb 100644 --- a/policy/test/k8s_cel_test_runner.go +++ b/policy/test/k8s_cel_test_runner.go @@ -18,8 +18,8 @@ import ( "os" "testing" - "github.com/google/cel-go/policy" - "github.com/google/cel-go/tools/celtest" + "cel.dev/cel-go/policy" + "cel.dev/cel-go/tools/celtest" ) // TestK8sCEL triggers compilation and test execution of a k8s policy which diff --git a/repl/BUILD.bazel b/repl/BUILD.bazel index 2f6e627ea..2550ab1c1 100644 --- a/repl/BUILD.bazel +++ b/repl/BUILD.bazel @@ -26,7 +26,7 @@ go_library( "evaluator.go", "typefmt.go", ], - importpath = "github.com/google/cel-go/repl", + importpath = "cel.dev/cel-go/repl", deps = [ "//cel:go_default_library", "//checker:go_default_library", diff --git a/repl/commands.go b/repl/commands.go index bad89eb7c..6e0ac7bd7 100644 --- a/repl/commands.go +++ b/repl/commands.go @@ -21,8 +21,8 @@ import ( antlr "github.com/antlr4-go/antlr/v4" - "github.com/google/cel-go/common/env" - "github.com/google/cel-go/repl/parser" + "cel.dev/cel-go/common/env" + "cel.dev/cel-go/repl/parser" ) var ( diff --git a/repl/evaluator.go b/repl/evaluator.go index ecfd72264..4c4b70bc7 100644 --- a/repl/evaluator.go +++ b/repl/evaluator.go @@ -23,14 +23,14 @@ import ( "sort" "strings" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/decls" - "github.com/google/cel-go/common/env" - envlib "github.com/google/cel-go/common/env" - "github.com/google/cel-go/common/functions" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/ext" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/env" + envlib "cel.dev/cel-go/common/env" + "cel.dev/cel-go/common/functions" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/ext" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" diff --git a/repl/evaluator_test.go b/repl/evaluator_test.go index d18f015e2..8bb383630 100644 --- a/repl/evaluator_test.go +++ b/repl/evaluator_test.go @@ -18,11 +18,11 @@ import ( "strings" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/env" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/env" "github.com/google/go-cmp/cmp" - proto2pb "github.com/google/cel-go/test/proto2pb" + proto2pb "cel.dev/cel-go/test/proto2pb" ) var testTextDescriptorFile string = "testdata/attribute_context_fds.textproto" diff --git a/repl/go.mod b/repl/go.mod index 400aa810d..7118b13c8 100644 --- a/repl/go.mod +++ b/repl/go.mod @@ -1,12 +1,12 @@ -module github.com/google/cel-go/repl +module cel.dev/cel-go/repl go 1.23.0 require ( + cel.dev/cel-go v0.26.1 cel.dev/expr v0.25.1 github.com/antlr4-go/antlr/v4 v4.13.1 github.com/chzyer/readline v1.5.1 - github.com/google/cel-go v0.26.1 github.com/google/go-cmp v0.7.0 go.yaml.in/yaml/v3 v3.0.4 google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 @@ -20,6 +20,4 @@ require ( golang.org/x/text v0.22.0 // indirect ) -replace github.com/google/cel-go => ../. - -replace cel.dev/expr => ../../cel-spec +replace cel.dev/cel-go => ../. diff --git a/repl/go.sum b/repl/go.sum index 944635123..7cf762b5d 100644 --- a/repl/go.sum +++ b/repl/go.sum @@ -1,3 +1,5 @@ +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= diff --git a/repl/main/BUILD.bazel b/repl/main/BUILD.bazel index 8b935c48c..0fa269833 100644 --- a/repl/main/BUILD.bazel +++ b/repl/main/BUILD.bazel @@ -21,14 +21,14 @@ package( go_binary( name = "main", embed = [":go_default_library"], - importpath = "github.com/google/cel-go/repl/main", + importpath = "cel.dev/cel-go/repl/main", visibility = ["//visibility:public"], ) go_library( name = "go_default_library", srcs = ["main.go"], - importpath = "github.com/google/cel-go/repl/main", + importpath = "cel.dev/cel-go/repl/main", visibility = ["//visibility:private"], deps = [ "//repl:go_default_library", diff --git a/repl/main/main.go b/repl/main/main.go index bfc00242f..4d6f5cd00 100644 --- a/repl/main/main.go +++ b/repl/main/main.go @@ -45,7 +45,7 @@ import ( "os" "path/filepath" - "github.com/google/cel-go/repl" + "cel.dev/cel-go/repl" "github.com/chzyer/readline" ) diff --git a/repl/parser/BUILD.bazel b/repl/parser/BUILD.bazel index 4d6f12cb2..821ac1391 100644 --- a/repl/parser/BUILD.bazel +++ b/repl/parser/BUILD.bazel @@ -23,7 +23,7 @@ go_library( name = "go_default_library", srcs = glob(["*.go"], exclude=["*_test.go"]), data = glob(["*.tokens"]), - importpath = "github.com/google/cel-go/repl/parser", + importpath = "cel.dev/cel-go/repl/parser", deps = [ "@com_github_antlr4_go_antlr_v4//:go_default_library", ], diff --git a/repl/typefmt.go b/repl/typefmt.go index a877f57d0..3f559e286 100644 --- a/repl/typefmt.go +++ b/repl/typefmt.go @@ -20,10 +20,10 @@ import ( antlr "github.com/antlr4-go/antlr/v4" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/env" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/repl/parser" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/env" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/repl/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/repl/typefmt_test.go b/repl/typefmt_test.go index a9c9df9ec..662247876 100644 --- a/repl/typefmt_test.go +++ b/repl/typefmt_test.go @@ -17,7 +17,7 @@ package repl import ( "testing" - "github.com/google/cel-go/cel" + "cel.dev/cel-go/cel" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/test/BUILD.bazel b/test/BUILD.bazel index 597010414..3a6884590 100644 --- a/test/BUILD.bazel +++ b/test/BUILD.bazel @@ -23,7 +23,7 @@ go_library( "expr.go", "suite.go", ], - importpath = "github.com/google/cel-go/test", + importpath = "cel.dev/cel-go/test", deps = [ "//common/operators:go_default_library", "//common/types:go_default_library", diff --git a/test/async.go b/test/async.go index c550e940c..a4ab6ebbb 100644 --- a/test/async.go +++ b/test/async.go @@ -18,8 +18,8 @@ import ( "context" "time" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" ) // FakeRPC returns a blocking async function which simulates an RPC that succeeds after a short diff --git a/test/bench/BUILD.bazel b/test/bench/BUILD.bazel index 5f856dc74..e4ccee5e9 100644 --- a/test/bench/BUILD.bazel +++ b/test/bench/BUILD.bazel @@ -9,7 +9,7 @@ go_library( srcs = [ "bench.go", ], - importpath = "github.com/google/cel-go/test/bench", + importpath = "cel.dev/cel-go/test/bench", deps = [ "//cel:go_default_library", "//ext:go_default_library", diff --git a/test/bench/bench.go b/test/bench/bench.go index 6725faaf7..95d23cec3 100644 --- a/test/bench/bench.go +++ b/test/bench/bench.go @@ -19,10 +19,10 @@ import ( "fmt" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/ext" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/ext" ) // Case represents a human-readable expression and an expected output given an input diff --git a/test/bench/bench_test.go b/test/bench/bench_test.go index 4cfc1e85c..d0798b11b 100644 --- a/test/bench/bench_test.go +++ b/test/bench/bench_test.go @@ -17,7 +17,7 @@ package bench import ( "testing" - "github.com/google/cel-go/cel" + "cel.dev/cel-go/cel" ) func BenchmarkReferenceCases(b *testing.B) { diff --git a/test/expr.go b/test/expr.go index 39adcee60..1390a6498 100644 --- a/test/expr.go +++ b/test/expr.go @@ -15,7 +15,7 @@ package test import ( - "github.com/google/cel-go/common/operators" + "cel.dev/cel-go/common/operators" "google.golang.org/protobuf/proto" diff --git a/test/proto2pb/BUILD.bazel b/test/proto2pb/BUILD.bazel index c7233c8f1..82f27495c 100644 --- a/test/proto2pb/BUILD.bazel +++ b/test/proto2pb/BUILD.bazel @@ -23,7 +23,7 @@ go_library( "test_all_types.pb.go", "test_extensions.pb.go", ], - importpath = "github.com/google/cel-go/test/proto2pb", + importpath = "cel.dev/cel-go/test/proto2pb", deps = [ "@org_golang_google_protobuf//proto:go_default_library", "@org_golang_google_protobuf//reflect/protoreflect:go_default_library", @@ -59,7 +59,7 @@ proto_library( go_proto_library( name = "test_all_types_go_proto", - importpath = "github.com/google/cel-go/test/proto2pb", + importpath = "cel.dev/cel-go/test/proto2pb", protos = [ ":test_all_types_proto", ":test_extensions_proto", diff --git a/test/proto2pb/test_all_types.proto b/test/proto2pb/test_all_types.proto index a77cc6d8b..aaf1677ed 100644 --- a/test/proto2pb/test_all_types.proto +++ b/test/proto2pb/test_all_types.proto @@ -3,7 +3,7 @@ syntax = "proto2"; package google.expr.proto2.test; -option go_package = "github.com/google/cel-go/test/proto2pb"; +option go_package = "cel.dev/cel-go/test/proto2pb"; import "google/protobuf/any.proto"; import "google/protobuf/duration.proto"; diff --git a/test/proto2pb/test_extensions.proto b/test/proto2pb/test_extensions.proto index f98ca34a4..c41db5a08 100644 --- a/test/proto2pb/test_extensions.proto +++ b/test/proto2pb/test_extensions.proto @@ -2,7 +2,7 @@ syntax = "proto2"; package google.expr.proto2.test; -option go_package = "github.com/google/cel-go/test/proto2pb"; +option go_package = "cel.dev/cel-go/test/proto2pb"; import "google/protobuf/wrappers.proto"; import "test/proto2pb/test_all_types.proto"; diff --git a/test/proto3pb/BUILD.bazel b/test/proto3pb/BUILD.bazel index 7b3449686..b30cc56c6 100644 --- a/test/proto3pb/BUILD.bazel +++ b/test/proto3pb/BUILD.bazel @@ -24,7 +24,7 @@ go_library( "test_all_types.pb.go", "test_import.pb.go", ], - importpath = "github.com/google/cel-go/test/proto3pb", + importpath = "cel.dev/cel-go/test/proto3pb", deps = [ "@org_golang_google_protobuf//proto:go_default_library", "@org_golang_google_protobuf//types/known/anypb:go_default_library", @@ -57,7 +57,7 @@ proto_library( go_proto_library( name = "test_all_types_go_proto", - importpath = "github.com/google/cel-go/test/proto3pb", + importpath = "cel.dev/cel-go/test/proto3pb", protos = [ ":test_all_types_proto", ":test_import_proto", diff --git a/test/proto3pb/test_all_types.proto b/test/proto3pb/test_all_types.proto index f6e8f2c1e..dce24bac0 100644 --- a/test/proto3pb/test_all_types.proto +++ b/test/proto3pb/test_all_types.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package google.expr.proto3.test; -option go_package = "github.com/google/cel-go/test/proto3pb"; +option go_package = "cel.dev/cel-go/test/proto3pb"; import "google/protobuf/any.proto"; import "google/protobuf/duration.proto"; diff --git a/test/proto3pb/test_import.proto b/test/proto3pb/test_import.proto index f508fc5d0..afb62608d 100644 --- a/test/proto3pb/test_import.proto +++ b/test/proto3pb/test_import.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package google.expr.proto3.test; -option go_package = "github.com/google/cel-go/test/proto3pb"; +option go_package = "cel.dev/cel-go/test/proto3pb"; enum ImportedGlobalEnum { IMPORT_FOO = 0; diff --git a/tools/celtest/BUILD.bazel b/tools/celtest/BUILD.bazel index 8d5fd5a99..bb1ad7826 100644 --- a/tools/celtest/BUILD.bazel +++ b/tools/celtest/BUILD.bazel @@ -26,7 +26,7 @@ go_library( "test_coverage_reporter.go", "test_runner.go", ], - importpath = "github.com/google/cel-go/tools/celtest", + importpath = "cel.dev/cel-go/tools/celtest", deps = [ "//cel:go_default_library", "//common/ast:go_default_library", diff --git a/tools/celtest/test_coverage_reporter.go b/tools/celtest/test_coverage_reporter.go index 1147e84c6..e1eab67b5 100644 --- a/tools/celtest/test_coverage_reporter.go +++ b/tools/celtest/test_coverage_reporter.go @@ -20,9 +20,9 @@ import ( "strings" "testing" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/parser" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/parser" ) // reportCoverage reports the coverage information for the provided programs. diff --git a/tools/celtest/test_coverage_reporter_test.go b/tools/celtest/test_coverage_reporter_test.go index ae7b0c701..ac4a1de47 100644 --- a/tools/celtest/test_coverage_reporter_test.go +++ b/tools/celtest/test_coverage_reporter_test.go @@ -18,9 +18,9 @@ package celtest import ( "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/tools/compiler" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/tools/compiler" ) func TestCoverageStats(t *testing.T) { diff --git a/tools/celtest/test_runner.go b/tools/celtest/test_runner.go index 5f1e997d6..5e04bde59 100644 --- a/tools/celtest/test_runner.go +++ b/tools/celtest/test_runner.go @@ -25,16 +25,16 @@ import ( "strings" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/debug" - "github.com/google/cel-go/common/env" - "github.com/google/cel-go/common/operators" - "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/test" - "github.com/google/cel-go/tools/compiler" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/ast" + "cel.dev/cel-go/common/debug" + "cel.dev/cel-go/common/env" + "cel.dev/cel-go/common/operators" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/interpreter" + "cel.dev/cel-go/test" + "cel.dev/cel-go/tools/compiler" "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/encoding/prototext" diff --git a/tools/celtest/test_runner_test.go b/tools/celtest/test_runner_test.go index deb6699a7..0ccfd391f 100644 --- a/tools/celtest/test_runner_test.go +++ b/tools/celtest/test_runner_test.go @@ -18,13 +18,13 @@ package celtest import ( "testing" - "github.com/google/cel-go/cel" - "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/policy" - "github.com/google/cel-go/test" - "github.com/google/cel-go/tools/compiler" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/decls" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/common/types/ref" + "cel.dev/cel-go/policy" + "cel.dev/cel-go/test" + "cel.dev/cel-go/tools/compiler" "go.yaml.in/yaml/v3" diff --git a/tools/compiler/BUILD.bazel b/tools/compiler/BUILD.bazel index de7d0cc6e..58ee40197 100644 --- a/tools/compiler/BUILD.bazel +++ b/tools/compiler/BUILD.bazel @@ -24,7 +24,7 @@ go_library( srcs = [ "compiler.go", ], - importpath = "github.com/google/cel-go/tools/compiler", + importpath = "cel.dev/cel-go/tools/compiler", deps = [ "//cel:go_default_library", "//common:go_default_library", diff --git a/tools/compiler/compiler.go b/tools/compiler/compiler.go index 4d4f69709..e5830f364 100644 --- a/tools/compiler/compiler.go +++ b/tools/compiler/compiler.go @@ -24,12 +24,12 @@ import ( "go.yaml.in/yaml/v3" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/env" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/ext" - "github.com/google/cel-go/policy" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common" + "cel.dev/cel-go/common/env" + "cel.dev/cel-go/common/types" + "cel.dev/cel-go/ext" + "cel.dev/cel-go/policy" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" diff --git a/tools/compiler/compiler_test.go b/tools/compiler/compiler_test.go index d528eed56..4dbca315d 100644 --- a/tools/compiler/compiler_test.go +++ b/tools/compiler/compiler_test.go @@ -18,10 +18,10 @@ import ( "reflect" "testing" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/env" - "github.com/google/cel-go/ext" - "github.com/google/cel-go/policy" + "cel.dev/cel-go/cel" + "cel.dev/cel-go/common/env" + "cel.dev/cel-go/ext" + "cel.dev/cel-go/policy" celpb "cel.dev/expr" configpb "cel.dev/expr/conformance" diff --git a/tools/go.mod b/tools/go.mod index dc978e3aa..3fb749df2 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -1,11 +1,11 @@ -module github.com/google/cel-go/tools +module cel.dev/cel-go/tools go 1.23.0 require ( + cel.dev/cel-go v0.26.1 + cel.dev/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1 cel.dev/expr v0.25.1 - github.com/google/cel-go v0.22.0 - github.com/google/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1 github.com/google/go-cmp v0.7.0 go.yaml.in/yaml/v3 v3.0.4 google.golang.org/genproto/googleapis/api v0.0.0-20250311190419-81fb87f6b8bf @@ -14,11 +14,11 @@ require ( require ( github.com/antlr4-go/antlr/v4 v4.13.1 // indirect - github.com/stoewer/go-strcase v1.3.1 // indirect golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect golang.org/x/text v0.22.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/google/cel-go => ../. +replace cel.dev/cel-go => ../. + +replace cel.dev/cel-go/policy => ../policy diff --git a/tools/go.sum b/tools/go.sum index 2d091fe5a..02f0ae46e 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -2,24 +2,8 @@ cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1 h1:jT/04RYwo++S9tvHggXWuAqvnc2Pi0BTHYsZYVOoMOs= -github.com/google/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1/go.mod h1:dgvqy3CzFx17CBMkL0s1hd0r1+rEQOo85tDpr0g6Dp4= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= -github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= @@ -34,6 +18,3 @@ google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aO google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From fc6a53ca6468b21b36f13856c8657a7c22c601a4 Mon Sep 17 00:00:00 2001 From: dplotnikov Date: Thu, 13 Aug 2026 17:59:12 -0700 Subject: [PATCH 17/37] Update README.md badges and links for cel.dev/cel-go --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d07aa3ca4..1a4a99281 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # Common Expression Language [![Go Report Card](https://goreportcard.com/badge/cel.dev/cel-go)](https://goreportcard.com/report/cel.dev/cel-go) -[![GoDoc](https://godoc.org/cel.dev/cel-go?status.svg)][6] +[![GoDoc](https://pkg.go.dev/badge/cel.dev/cel-go.svg)][6] > [!WARNING] > **On June 16, 2026, this repository will move to > github.com/cel-expr/cel-go!** > > Please update your links and dependencies. See the [pinned -> issue](https://cel.dev/cel-go/issues/1329) for details. +> issue](https://github.com/cel-expr/cel-go/issues/1329) for details. The Common Expression Language (CEL) is a non-Turing complete language designed for simplicity, speed, safety, and portability. CEL's C-like [syntax][1] looks @@ -285,9 +285,9 @@ bazel test ... Released under the [Apache License](LICENSE). -[1]: https://github.com/google/cel-spec +[1]: https://github.com/cel-expr/cel-spec [2]: https://groups.google.com/forum/#!forum/cel-go-discuss -[3]: https://github.com/google/cel-cpp -[4]: https://cel.dev/cel-go/issues +[3]: https://github.com/cel-expr/cel-cpp +[4]: https://github.com/cel-expr/cel-go/issues [5]: https://bazel.build -[6]: https://godoc.org/cel.dev/cel-go +[6]: https://pkg.go.dev/cel.dev/cel-go From a3027a4d358fcba46cc8420411eafeeac6f0a212 Mon Sep 17 00:00:00 2001 From: Jonathan Tatum Date: Fri, 14 Aug 2026 11:09:13 -0700 Subject: [PATCH 18/37] Enable list_ext conformance tests. (#1412) --- MODULE.bazel | 8 ++++++-- conformance/BUILD.bazel | 1 + conformance/conformance_test.go | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index ba8c23ae9..714224fed 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -37,16 +37,20 @@ bazel_dep( bazel_dep(name = "rules_shell", version = "0.6.1") bazel_dep(name = "rules_license", version = "1.0.0") - # local_path_override( # module_name = "cel-spec", # path = "../cel-spec", # ) bazel_dep( name = "cel-spec", - version = "0.25.1", + version = "0.25.2", repo_name = "dev_cel_expr", ) +git_override( + module_name = "cel-spec", + commit = "ba58ae5007845f3a1279b488cdeb79645ce958bb", + remote = "https://github.com/cel-expr/cel-spec", +) go_sdk = use_extension("@io_bazel_rules_go//go:extensions.bzl", "go_sdk") go_sdk.download(version = "1.23.0") diff --git a/conformance/BUILD.bazel b/conformance/BUILD.bazel index 455f6e695..b30f1c1d3 100644 --- a/conformance/BUILD.bazel +++ b/conformance/BUILD.bazel @@ -17,6 +17,7 @@ _ALL_TESTS = [ "@dev_cel_expr//tests/simple:testdata/fp_math.textproto", "@dev_cel_expr//tests/simple:testdata/integer_math.textproto", "@dev_cel_expr//tests/simple:testdata/lists.textproto", + "@dev_cel_expr//tests/simple:testdata/lists_ext.textproto", "@dev_cel_expr//tests/simple:testdata/logic.textproto", "@dev_cel_expr//tests/simple:testdata/macros.textproto", "@dev_cel_expr//tests/simple:testdata/macros2.textproto", diff --git a/conformance/conformance_test.go b/conformance/conformance_test.go index 417310287..6b96e5866 100644 --- a/conformance/conformance_test.go +++ b/conformance/conformance_test.go @@ -88,6 +88,7 @@ func init() { cel.Types(&test2pb.TestAllTypes{}, &test2pb.Proto2ExtensionScopedMessage{}, &test3pb.TestAllTypes{}), ext.Bindings(), ext.Encoders(), + ext.Lists(), ext.Math(), ext.Protos(), ext.Strings(), From b6027c466d6a21efd89811256b4cea0070b02ac3 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Fri, 14 Aug 2026 16:23:38 -0700 Subject: [PATCH 19/37] Parsing helper for working with different types and formats of timestamp (#1414) * Parsing helper for working with different types and formats of timestamp * Add overflow checks and negative tests --- common/types/timestamp.go | 86 ++++++++++++++++ common/types/timestamp_test.go | 176 +++++++++++++++++++++++++++++++++ 2 files changed, 262 insertions(+) diff --git a/common/types/timestamp.go b/common/types/timestamp.go index aee47f053..49b200101 100644 --- a/common/types/timestamp.go +++ b/common/types/timestamp.go @@ -15,6 +15,8 @@ package types import ( + "encoding/json" + "errors" "fmt" "reflect" "regexp" @@ -258,6 +260,90 @@ func (t Timestamp) format(sb *strings.Builder) { fmt.Fprintf(sb, `timestamp("%s")`, t.Time.UTC().Format(time.RFC3339Nano)) } +// ParseTimestamp attempts to parse a timestamp from various supported types and representations: +// - time.Time, Timestamp, *timestamppb.Timestamp +// - RFC 3339 and RFC 3339Nano formatted strings (e.g. "2023-01-01T00:00:00Z") +// - Unix epoch integers (int, int32, int64) +// - Unix epoch floating-point seconds (float32, float64) +// - json.Number +// - String representations of integers or floating-point epoch seconds +// +// If the parsed timestamp falls outside the supported range [minUnixTime, maxUnixTime], an error is returned. +func ParseTimestamp(val any) (time.Time, error) { + if val == nil { + return time.Time{}, errors.New("invalid timestamp: nil value") + } + switch v := val.(type) { + case time.Time: + return validateTimestampRange(v.UTC()) + case Timestamp: + return validateTimestampRange(v.Time.UTC()) + case *tpb.Timestamp: + if v == nil { + return time.Time{}, nil + } + return validateTimestampRange(v.AsTime().UTC()) + case int: + return validateTimestampRange(time.Unix(int64(v), 0).UTC()) + case int32: + return validateTimestampRange(time.Unix(int64(v), 0).UTC()) + case int64: + return validateTimestampRange(time.Unix(v, 0).UTC()) + case float32: + return unixTimeFromFloat(float64(v)) + case float64: + return unixTimeFromFloat(v) + case json.Number: + if i, err := v.Int64(); err == nil { + return validateTimestampRange(time.Unix(i, 0).UTC()) + } + if f, err := v.Float64(); err == nil { + return unixTimeFromFloat(f) + } + return ParseTimestamp(v.String()) + case string: + s := strings.TrimSpace(v) + if s == "" { + return time.Time{}, errors.New("invalid RFC 3339 timestamp: ''") + } + if isStrictRFC3339(s) { + t, err := time.Parse(time.RFC3339, s) + if err != nil { + return time.Time{}, fmt.Errorf("invalid RFC 3339 timestamp %q", s) + } + return validateTimestampRange(t.UTC()) + } + if i, err := strconv.ParseInt(s, 10, 64); err == nil { + return validateTimestampRange(time.Unix(i, 0).UTC()) + } + if f, err := strconv.ParseFloat(s, 64); err == nil { + return unixTimeFromFloat(f) + } + return time.Time{}, fmt.Errorf("unsupported timestamp format: %q", s) + default: + return time.Time{}, fmt.Errorf("unsupported timestamp type: %T", val) + } +} + +func unixTimeFromFloat(f float64) (time.Time, error) { + sec, err := doubleToInt64Checked(f) + if err != nil { + return time.Time{}, err + } + nsec := int64((f - float64(sec)) * 1e9) + return validateTimestampRange(time.Unix(sec, nsec).UTC()) +} + +func validateTimestampRange(t time.Time) (time.Time, error) { + if t.IsZero() { + return t, nil + } + if t.Unix() < minUnixTime || t.Unix() > maxUnixTime { + return time.Time{}, fmt.Errorf("timestamp overflow: %v", t) + } + return t, nil +} + var ( timestampValueType = reflect.TypeOf(&tpb.Timestamp{}) diff --git a/common/types/timestamp_test.go b/common/types/timestamp_test.go index c13b21fef..47afeec3e 100644 --- a/common/types/timestamp_test.go +++ b/common/types/timestamp_test.go @@ -15,6 +15,7 @@ package types import ( + "encoding/json" "errors" "math" "reflect" @@ -551,3 +552,178 @@ func TestIsStrictRFC3339MatchesPattern(t *testing.T) { } } } + +func TestParseTimestamp(t *testing.T) { + now := time.Now().UTC() + epoch := int64(1700000000) + epochTime := time.Unix(epoch, 0).UTC() + epochFloatTime := time.Unix(epoch, 500000000).UTC() + var nilPbTs *tpb.Timestamp + + tests := []struct { + name string + val any + want time.Time + wantErr bool + }{ + { + name: "nil", + val: nil, + wantErr: true, + }, + { + name: "empty string", + val: "", + wantErr: true, + }, + { + name: "time.Time", + val: now, + want: now, + }, + { + name: "Timestamp struct", + val: Timestamp{Time: now}, + want: now, + }, + { + name: "*tpb.Timestamp", + val: tpb.New(now), + want: now, + }, + { + name: "nil *tpb.Timestamp", + val: nilPbTs, + want: time.Time{}, + }, + { + name: "int", + val: int(epoch), + want: epochTime, + }, + { + name: "int32", + val: int32(epoch), + want: epochTime, + }, + { + name: "int64", + val: int64(epoch), + want: epochTime, + }, + { + name: "float64", + val: float64(1700000000.5), + want: epochFloatTime, + }, + { + name: "float64 negative", + val: float64(-1700000000.5), + want: time.Unix(-1700000000, -500000000).UTC(), + }, + { + name: "float64 MaxFloat64 overflow", + val: math.MaxFloat64, + wantErr: true, + }, + { + name: "float64 NaN overflow", + val: math.NaN(), + wantErr: true, + }, + { + name: "float64 Inf overflow", + val: math.Inf(1), + wantErr: true, + }, + { + name: "float64 -Inf overflow", + val: math.Inf(-1), + wantErr: true, + }, + { + name: "float32", + val: float32(1700000000.5), + want: epochTime, + }, + { + name: "float32 negative", + val: float32(-1700000000.5), + want: time.Unix(-1700000000, 0).UTC(), + }, + { + name: "json.Number int", + val: json.Number("1700000000"), + want: epochTime, + }, + { + name: "json.Number float", + val: json.Number("1700000000.5"), + want: epochFloatTime, + }, + { + name: "json.Number invalid", + val: json.Number("invalid"), + wantErr: true, + }, + { + name: "string RFC3339", + val: "2026-08-10T12:00:00Z", + want: time.Date(2026, 8, 10, 12, 0, 0, 0, time.UTC), + }, + { + name: "string RFC3339Nano", + val: "2026-08-10T12:00:00.500Z", + want: time.Date(2026, 8, 10, 12, 0, 0, 500000000, time.UTC), + }, + { + name: "string RFC3339 invalid", + val: "2026-99-99T99:99:99Z", + wantErr: true, + }, + { + name: "string epoch int", + val: "1700000000", + want: epochTime, + }, + { + name: "string epoch float", + val: "1700000000.5", + want: epochFloatTime, + }, + { + name: "string invalid", + val: "not-a-timestamp", + wantErr: true, + }, + { + name: "unsupported map type", + val: map[string]any{}, + wantErr: true, + }, + { + name: "overflow", + val: int64(999999999999999), + wantErr: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ts, err := ParseTimestamp(tc.val) + if tc.wantErr { + if err == nil { + t.Errorf("ParseTimestamp(%v) succeeded, wanted error", tc.val) + } + return + } + if err != nil { + t.Errorf("ParseTimestamp(%v) unexpected error: %v", tc.val, err) + return + } + if !ts.Equal(tc.want) { + t.Errorf("ParseTimestamp(%v) = %v, wanted %v", tc.val, ts, tc.want) + } + }) + } +} From f74ac421377cd73f45ac10437a308b22e8e174a7 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Fri, 14 Aug 2026 16:50:42 -0700 Subject: [PATCH 20/37] Add Go-native JSON type support into NativeToValue (#1402) --- common/types/provider.go | 13 +++++++++++++ common/types/provider_test.go | 36 +++++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/common/types/provider.go b/common/types/provider.go index 2321828d8..24607351f 100644 --- a/common/types/provider.go +++ b/common/types/provider.go @@ -15,6 +15,7 @@ package types import ( + "encoding/json" "fmt" "maps" "reflect" @@ -663,6 +664,18 @@ func (p *Registry) NativeToValue(value any) ref.Val { if v != nil { return Uint(*v) } + case json.Number: + if i, err := v.Int64(); err == nil { + return Int(i) + } + if f, err := v.Float64(); err == nil { + return Double(f) + } + case json.RawMessage: + var rawVal any + if err := json.Unmarshal(v, &rawVal); err == nil { + return p.NativeToValue(rawVal) + } case []byte: return Bytes(v) // specializations for common lists types. diff --git a/common/types/provider_test.go b/common/types/provider_test.go index 65f695b11..593d33255 100644 --- a/common/types/provider_test.go +++ b/common/types/provider_test.go @@ -16,6 +16,7 @@ package types import ( "bytes" + "encoding/json" "fmt" "reflect" "sort" @@ -938,9 +939,10 @@ func TestNativeToValue_Any(t *testing.T) { } tests := []struct { - name string - in any - want ref.Val + name string + in any + want ref.Val + wantErr bool }{ { name: "NullValue", @@ -986,9 +988,10 @@ func TestNativeToValue_Json(t *testing.T) { parsedExpr := &exprpb.ParsedExpr{} tests := []struct { - name string - in any - want ref.Val + name string + in any + want ref.Val + wantErr bool }{ // Json primitive conversion test. {name: "bool value", in: structpb.NewBoolValue(false), want: False}, @@ -1040,10 +1043,31 @@ func TestNativeToValue_Json(t *testing.T) { in: parsedExpr, want: reg.NativeToValue(parsedExpr), }, + + // Go json.Number conversion. + {name: "json.Number int", in: json.Number("42"), want: Int(42)}, + {name: "json.Number float", in: json.Number("42.5"), want: Double(42.5)}, + {name: "json.Number invalid", in: json.Number("invalid-num"), wantErr: true}, + + // Go json.RawMessage conversion. + {name: "json.RawMessage map", in: json.RawMessage(`{"key":"value"}`), want: NewStringInterfaceMap(reg, map[string]any{"key": "value"})}, + {name: "json.RawMessage string", in: json.RawMessage(`"hello"`), want: String("hello")}, + {name: "json.RawMessage int", in: json.RawMessage(`123`), want: Double(123)}, + {name: "json.RawMessage array", in: json.RawMessage(`["world", 42]`), want: NewDynamicList(reg, []any{"world", float64(42)})}, + {name: "[]json.RawMessage slice", in: []json.RawMessage{json.RawMessage(`"hello"`), json.RawMessage(`123`)}, want: NewDynamicList(reg, []json.RawMessage{json.RawMessage(`"hello"`), json.RawMessage(`123`)})}, + {name: "json.RawMessage invalid", in: json.RawMessage(`invalid-json`), wantErr: true}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { + if tc.wantErr { + reg := newTestRegistry(t, ProtoTypeDefs(&exprpb.ParsedExpr{})) + val := reg.NativeToValue(tc.in) + if !IsError(val) { + t.Errorf("NativeToValue(%v) = %v, want error", tc.in, val) + } + return + } expectNativeToValue(t, tc.in, tc.want) }) } From 931e003859104219ead50a27d3c290e008746709 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Fri, 14 Aug 2026 18:11:11 -0700 Subject: [PATCH 21/37] JWT data types with Parse and claim helpers (#1415) * Add Go-native JSON type support into NativeToValue * JWT object rep and helpers for inspecting claims * JWT data types, parse, and claim helpers --- common/types/provider_test.go | 2 - ext/security/go.mod | 18 + ext/security/go.sum | 20 + ext/security/jwt/BUILD.bazel | 35 ++ ext/security/jwt/export_test.go | 20 + ext/security/jwt/jwt.go | 447 +++++++++++++ ext/security/jwt/jwt_test.go | 1050 +++++++++++++++++++++++++++++++ 7 files changed, 1590 insertions(+), 2 deletions(-) create mode 100644 ext/security/go.mod create mode 100644 ext/security/go.sum create mode 100644 ext/security/jwt/BUILD.bazel create mode 100644 ext/security/jwt/export_test.go create mode 100644 ext/security/jwt/jwt.go create mode 100644 ext/security/jwt/jwt_test.go diff --git a/common/types/provider_test.go b/common/types/provider_test.go index 593d33255..ee3a245b3 100644 --- a/common/types/provider_test.go +++ b/common/types/provider_test.go @@ -273,8 +273,6 @@ func TestRegistryConcurrentCopy(t *testing.T) { wg.Wait() } - - func TestRegistryRegisterType(t *testing.T) { tests := []struct { name string diff --git a/ext/security/go.mod b/ext/security/go.mod new file mode 100644 index 000000000..8689ca467 --- /dev/null +++ b/ext/security/go.mod @@ -0,0 +1,18 @@ +module github.com/google/cel-go/ext/security + +go 1.23.0 + +require github.com/google/cel-go v0.31.0 + +require ( + cel.dev/expr v0.25.1 // indirect + github.com/antlr4-go/antlr/v4 v4.13.1 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect + golang.org/x/text v0.22.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect + google.golang.org/protobuf v1.36.10 // indirect +) + +replace github.com/google/cel-go => ../../ diff --git a/ext/security/go.sum b/ext/security/go.sum new file mode 100644 index 000000000..94447aa14 --- /dev/null +++ b/ext/security/go.sum @@ -0,0 +1,20 @@ +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= +github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= +golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw= +google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/ext/security/jwt/BUILD.bazel b/ext/security/jwt/BUILD.bazel new file mode 100644 index 000000000..f7360403d --- /dev/null +++ b/ext/security/jwt/BUILD.bazel @@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +package( + default_visibility = ["//visibility:public"], + licenses = ["notice"], # Apache 2.0 +) + +go_library( + name = "go_default_library", + srcs = [ + "jwt.go", + ], + importpath = "github.com/google/cel-go/ext/security/jwt", + deps = [ + "//cel:go_default_library", + "//common/types:go_default_library", + "//common/types/ref:go_default_library", + ], +) + +go_test( + name = "go_default_test", + size = "small", + srcs = [ + "export_test.go", + "jwt_test.go", + ], + embed = [ + ":go_default_library", + ], + deps = [ + "//cel:go_default_library", + "//common/types:go_default_library", + ], +) diff --git a/ext/security/jwt/export_test.go b/ext/security/jwt/export_test.go new file mode 100644 index 000000000..0c1663aff --- /dev/null +++ b/ext/security/jwt/export_test.go @@ -0,0 +1,20 @@ +// Copyright 2026 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 jwt + +// NewJWTLib constructs an internal jwtLib instance for testing. +func NewJWTLib() *jwtLib { + return &jwtLib{} +} diff --git a/ext/security/jwt/jwt.go b/ext/security/jwt/jwt.go new file mode 100644 index 000000000..b8608eda7 --- /dev/null +++ b/ext/security/jwt/jwt.go @@ -0,0 +1,447 @@ +// Copyright 2026 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 jwt implements CEL extension functions for JSON Web Token (JWT) parsing, claims inspection, and validation. +package jwt + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "reflect" + "slices" + "strings" + "time" + + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" +) + +const ( + // jwtTokenType is the CEL type name for jwt.Token. + jwtTokenType = "jwt.Token" + maxTokenSize = 10 * 1024 * 1024 // 10MB maximum allowed token size +) + +func defaultNowFunc() time.Time { + return time.Now().UTC() +} + +// Library returns a cel.EnvOption to configure extended functions for JWT data handling and claims inspection. +func Library(options ...Option) cel.EnvOption { + l := &jwtLib{ + version: ^uint32(0), + now: defaultNowFunc, + } + for _, o := range options { + l = o(l) + } + return cel.Lib(l) +} + +// Option declares a functional operator for configuring JWT extension library behavior. +type Option func(*jwtLib) *jwtLib + +// Version sets the library version for JWT extensions. +func Version(version uint32) Option { + return func(l *jwtLib) *jwtLib { + l.version = version + return l + } +} + +// ValidateTimes enables automatic time validation (iat, nbf, exp) during token parsing with an optional clock leeway. +func ValidateTimes(leeway ...time.Duration) Option { + return func(l *jwtLib) *jwtLib { + l.validateTimes = true + if len(leeway) > 0 { + l.clockLeeway = leeway[0] + } + return l + } +} + +// Clock sets a custom clock function for time validation (defaults to time.Now). +func Clock(nowFunc func() time.Time) Option { + return func(l *jwtLib) *jwtLib { + l.now = nowFunc + return l + } +} + +// ClockLeeway sets the tolerance window when checking token time claims (iat, nbf, exp). +func ClockLeeway(leeway time.Duration) Option { + return func(l *jwtLib) *jwtLib { + l.clockLeeway = leeway + return l + } +} + +type jwtLib struct { + version uint32 + validateTimes bool + clockLeeway time.Duration + now func() time.Time +} + +// LibraryName returns the CEL library identifier string. +func (*jwtLib) LibraryName() string { + return "cel.lib.ext.security.jwt" +} + +// CompileOptions returns environment options for declaring CEL functions and types. +func (l *jwtLib) CompileOptions() []cel.EnvOption { + celTokenType := cel.ObjectType(jwtTokenType) + tokenType, err := types.NewNativeType(reflect.TypeFor[Token](), types.ParseStructTag("cel")) + if err != nil { + panic(fmt.Errorf("failed to create token type: %w", err)) + } + var adapt func() types.Adapter = func() types.Adapter { + return types.DefaultTypeAdapter + } + return []cel.EnvOption{ + cel.OptionalTypes(), + cel.Types(tokenType), + func(e *cel.Env) (*cel.Env, error) { + adapt = func() types.Adapter { return e.CELTypeAdapter() } + return e, nil + }, + cel.Function("jwt.parse", + cel.FunctionDocs( + "Parses a JWT token string into a structured Token representation.", + "Automatically strips leading 'Bearer ' prefixes if present.", + ), + cel.Overload("jwt_parse_string", + []*cel.Type{cel.StringType}, + cel.OptionalType(celTokenType), + cel.OverloadExamples( + "jwt.parse(tokenStr)", + "jwt.parse('Bearer eyJhbGciOi...')", + ), + cel.UnaryBinding(func(arg ref.Val) ref.Val { + tokenStr := arg.(types.String) + tok, err := ParseToken(string(tokenStr)) + if err != nil { + return types.NewErr("parse token failed: %w", err) + } + if l.validateTimes && !l.isTokenTimeValid(tok) { + return types.OptionalNone + } + return types.OptionalOf(adapt().NativeToValue(tok)) + }), + ), + ), + cel.Function("claim", + cel.FunctionDocs( + "Queries a custom claim value by key name from the JWT token payload, returning an optional dynamic value.", + ), + cel.MemberOverload("jwt_token_claim_string", + []*cel.Type{celTokenType, cel.StringType}, + cel.OptionalType(cel.DynType), + cel.OverloadExamples( + "token.claim('tenant_id')", + "token.claim('roles').orValue([])", + ), + cel.BinaryBinding(func(targetVal, claimNameVal ref.Val) ref.Val { + target := targetVal.Value().(*Token) + claimName := claimNameVal.(types.String) + return target.Claim(adapt(), string(claimName)) + }), + ), + cel.MemberOverload("jwt_token_opt_claim_string", + []*cel.Type{cel.OptionalType(celTokenType), cel.StringType}, + cel.OptionalType(cel.DynType), + cel.OverloadExamples( + "jwt.parse(tokenStr).claim('tenant_id')", + "jwt.parse(tokenStr).claim('tier').orValue('standard')", + ), + cel.BinaryBinding(func(targetVal, claimNameVal ref.Val) ref.Val { + optTarget := targetVal.(*types.Optional) + if !optTarget.HasValue() { + return types.OptionalNone + } + target, ok := optTarget.GetValue().Value().(*Token) + if !ok { + return types.ValOrErr(optTarget.GetValue(), "expected jwt.Token") + } + claimName := claimNameVal.(types.String) + return target.Claim(adapt(), string(claimName)) + }), + ), + ), + cel.Function("presentedBy", + cel.FunctionDocs( + "Determines whether the token was presented by the expected authorized party (`azp`) or audience (`aud`) for the given issuer (`iss`).", + "When `azp` is present in the token, it takes precedence over `aud`.", + ), + cel.MemberOverload("jwt_token_presented_by_string_string", + []*cel.Type{celTokenType, cel.StringType, cel.StringType}, + cel.BoolType, + cel.OverloadExamples( + "token.presentedBy('https://accounts.google.com', 'my-client-app')", + ), + cel.FunctionBinding(func(args ...ref.Val) ref.Val { + target := args[0].Value().(*Token) + issuer := args[1].(types.String) + presenter := args[2].(types.String) + return types.Bool(target.PresentedBy(string(issuer), string(presenter))) + }), + ), + cel.MemberOverload("jwt_token_opt_presented_by_string_string", + []*cel.Type{cel.OptionalType(celTokenType), cel.StringType, cel.StringType}, + cel.BoolType, + cel.OverloadExamples( + "jwt.parse(tokenStr).presentedBy('https://accounts.google.com', 'my-client-app')", + ), + cel.FunctionBinding(func(args ...ref.Val) ref.Val { + optTarget := args[0].(*types.Optional) + if !optTarget.HasValue() { + return types.False + } + target, ok := optTarget.GetValue().Value().(*Token) + if !ok { + return types.ValOrErr(optTarget.GetValue(), "expected jwt.Token") + } + issuer := args[1].(types.String) + presenter := args[2].(types.String) + return types.Bool(target.PresentedBy(string(issuer), string(presenter))) + }), + ), + ), + } +} + +// ProgramOptions returns program options for JWT extensions. +func (l *jwtLib) ProgramOptions() []cel.ProgramOption { + return nil +} + +func (l *jwtLib) isTokenTimeValid(tok *Token) bool { + return !l.validateTimes || tok.IsValidAt(l.now(), l.clockLeeway) +} + +// Token represents a parsed JWT token using Go native struct types. +// A Token instance and its associated Payload map MUST be treated as immutable once parsed or created. +type Token struct { + // Standard claims + Issuer string `json:"iss" cel:"issuer"` + Subject string `json:"sub" cel:"subject"` + Audience []string `json:"aud" cel:"aud"` + AuthorizedParty string `json:"azp,omitempty" cel:"azp"` + ExpiresAt time.Time `json:"exp" cel:"exp"` + NotBefore time.Time `json:"nbf" cel:"nbf"` + IssuedAt time.Time `json:"iat" cel:"iat"` + ID string `json:"jti,omitempty" cel:"id"` + + // Header derived fields + Algorithm string `json:"alg" cel:"alg"` + KeyID string `json:"kid" cel:"keyId"` + + // Raw JSON payload associated with the token including custom claims. + // Must be treated as read-only once initialized. + Payload map[string]any `json:"-" cel:"-"` +} + +// IsValidAt checks whether the token time claims (iat, nbf, exp) are valid at the given reference time with clock leeway tolerance. +func (t *Token) IsValidAt(refTime time.Time, leeway time.Duration) bool { + now := refTime.UTC() + lateNow := now.Add(leeway) + earlyNow := now.Add(-leeway) + + // Issued-at time is present and in the future. + if !t.IssuedAt.IsZero() && t.IssuedAt.Compare(lateNow) > 0 { + return false + } + // Not-before time is present and in the future. + if !t.NotBefore.IsZero() && t.NotBefore.Compare(lateNow) > 0 { + return false + } + // Expires-at time is present and expiry happened in the past. + if !t.ExpiresAt.IsZero() && t.ExpiresAt.Compare(earlyNow) <= 0 { + return false + } + // Inverted validity window: nbf <= exp + if !t.NotBefore.IsZero() && !t.ExpiresAt.IsZero() && t.NotBefore.Compare(t.ExpiresAt) > 0 { + return false + } + // Inverted validity window: iat <= exp + if !t.IssuedAt.IsZero() && !t.ExpiresAt.IsZero() && t.IssuedAt.Compare(t.ExpiresAt) > 0 { + return false + } + + return true +} + +// PresentedBy determines whether the token from the given issuer was presented by the expected authorized party (`azp`) or audience (`aud`). +// If the token contains an `azp` claim, it is checked against the `presenter`. Otherwise, the `aud` claim is checked. +func (t *Token) PresentedBy(issuer, presenter string) bool { + if t.Issuer != issuer { + return false + } + if t.AuthorizedParty != "" { + return t.AuthorizedParty == presenter + } + return slices.Contains(t.Audience, presenter) +} + +// Claim queries a claim value by key name using the provided types.Adapter, returning an optional dyn value. +func (t *Token) Claim(adapter types.Adapter, claimName string) ref.Val { + val, ok := t.Payload[claimName] + if !ok || val == nil { + return types.OptionalNone + } + refVal := adapter.NativeToValue(val) + if types.IsError(refVal) { + return refVal + } + return types.OptionalOf(refVal) +} + +// NewToken generates a `jwt.Token` instance from the JSON-decoded header and payload of a JWT. +// +// Signature validation of the token must be performed before passing the token to CEL. +// It is recommended that `IsValidAt` and `PresentedBy` are checked after creation of the token +// to ensure the token matches core content assumptions. +func NewToken(header, payload map[string]any) (*Token, error) { + alg, ok := header["alg"].(string) + if !ok || alg == "" { + return nil, fmt.Errorf("missing required header: 'alg'") + } + iss, ok := payload["iss"].(string) + if !ok || iss == "" { + return nil, fmt.Errorf("missing required claim: 'iss'") + } + sub, ok := payload["sub"].(string) + if !ok || sub == "" { + return nil, fmt.Errorf("missing required claim: 'sub'") + } + + var audience []string + switch a := payload["aud"].(type) { + case string: + if a != "" { + audience = []string{a} + } + case []any: + for _, item := range a { + s, ok := item.(string) + if !ok || s == "" { + return nil, fmt.Errorf("invalid claim 'aud': expected non-empty string in audience list, got %T", item) + } + audience = append(audience, s) + } + default: + if payload["aud"] != nil { + return nil, fmt.Errorf("invalid claim 'aud': expected string or array of strings, got %T", payload["aud"]) + } + } + if len(audience) == 0 { + return nil, fmt.Errorf("missing required claim: 'aud'") + } + + exp, err := types.ParseTimestamp(payload["exp"]) + if err != nil || exp.IsZero() { + return nil, fmt.Errorf("missing required claim: 'exp'") + } + iat, err := types.ParseTimestamp(payload["iat"]) + if err != nil || iat.IsZero() { + return nil, fmt.Errorf("missing required claim: 'iat'") + } + var nbf time.Time + if rawNbf, ok := payload["nbf"]; ok && rawNbf != nil { + parsedNbf, err := types.ParseTimestamp(rawNbf) + if err != nil { + return nil, fmt.Errorf("invalid claim 'nbf': %w", err) + } + nbf = parsedNbf + } + + return &Token{ + Algorithm: alg, + KeyID: optString(header, "kid"), + Issuer: iss, + Subject: sub, + Audience: audience, + AuthorizedParty: optString(payload, "azp"), + ExpiresAt: exp, + IssuedAt: iat, + NotBefore: nbf, + ID: optString(payload, "jti"), + Payload: payload, + }, nil +} + +// ParseToken parses a JWT token string into a structured Token. +// Verification of the token must be performed before passing the token to CEL. +func ParseToken(tokenStr string) (*Token, error) { + tokenStr = trimBearerPrefix(tokenStr) + if len(tokenStr) > maxTokenSize { + return nil, fmt.Errorf("token size exceeds maximum allowed limit of %d bytes", maxTokenSize) + } + + parts := strings.SplitN(tokenStr, ".", 4) + if len(parts) < 2 || len(parts) > 3 { + return nil, fmt.Errorf("invalid token format: expected 2 or 3 parts, got %d", len(parts)) + } + + headerBytes, err := decodeBase64Segment(parts[0]) + if err != nil { + return nil, fmt.Errorf("failed to decode header: %w", err) + } + + var header map[string]any + if err := json.Unmarshal(headerBytes, &header); err != nil { + return nil, fmt.Errorf("failed to parse header JSON: %w", err) + } + + payloadBytes, err := decodeBase64Segment(parts[1]) + if err != nil { + return nil, fmt.Errorf("failed to decode payload: %w", err) + } + + var payload map[string]any + if err := json.Unmarshal(payloadBytes, &payload); err != nil { + return nil, fmt.Errorf("failed to parse payload JSON: %w", err) + } + return NewToken(header, payload) +} + +func optString(m map[string]any, key string) string { + if v, ok := m[key].(string); ok { + return v + } + return "" +} + +func trimBearerPrefix(tokenStr string) string { + tokenStr = strings.TrimSpace(tokenStr) + if strings.HasPrefix(strings.ToLower(tokenStr), "bearer ") { + return strings.TrimSpace(tokenStr[7:]) + } + return tokenStr +} + +func decodeBase64Segment(seg string) ([]byte, error) { + if b, err := base64.RawURLEncoding.DecodeString(seg); err == nil { + return b, nil + } + if b, err := base64.URLEncoding.DecodeString(seg); err == nil { + return b, nil + } + if b, err := base64.RawStdEncoding.DecodeString(seg); err == nil { + return b, nil + } + return base64.StdEncoding.DecodeString(seg) +} diff --git a/ext/security/jwt/jwt_test.go b/ext/security/jwt/jwt_test.go new file mode 100644 index 000000000..1b9dffe63 --- /dev/null +++ b/ext/security/jwt/jwt_test.go @@ -0,0 +1,1050 @@ +// Copyright 2026 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 jwt_test + +import ( + "encoding/base64" + "encoding/json" + "reflect" + "strings" + "testing" + "time" + + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/ext/security/jwt" +) + +func createTestJWT(t *testing.T, header, payload map[string]any) string { + hBytes, err := json.Marshal(header) + if err != nil { + t.Fatalf("json.Marshal header failed: %v", err) + } + pBytes, err := json.Marshal(payload) + if err != nil { + t.Fatalf("json.Marshal payload failed: %v", err) + } + + hB64 := base64.RawURLEncoding.EncodeToString(hBytes) + pB64 := base64.RawURLEncoding.EncodeToString(pBytes) + sigB64 := base64.RawURLEncoding.EncodeToString([]byte("signature-placeholder")) + + return hB64 + "." + pB64 + "." + sigB64 +} + +func evalExpr(t *testing.T, env *cel.Env, expr string, vars map[string]any) any { + ast, issues := env.Compile(expr) + if issues != nil && issues.Err() != nil { + t.Fatalf("Compile(%q) failed: %v", expr, issues.Err()) + } + prg, err := env.Program(ast) + if err != nil { + t.Fatalf("Program(%q) failed: %v", expr, err) + } + val, _, err := prg.Eval(vars) + if err != nil { + t.Fatalf("Eval(%q) failed: %v", expr, err) + } + return val.Value() +} + +func TestJWTParseAndPresentedBy(t *testing.T) { + header := map[string]any{ + "alg": "RS256", + "typ": "JWT", + "kid": "key-123", + } + payload := map[string]any{ + "iss": "https://auth.example.com", + "sub": "user_12345", + "aud": []string{"https://api.example.com", "https://admin.example.com"}, + "exp": time.Now().Add(1 * time.Hour).Unix(), + "nbf": time.Now().Add(-1 * time.Minute).Unix(), + "iat": time.Now().Add(-1 * time.Minute).Unix(), + "jti": "token-unique-id-999", + "roles": []string{"admin", "editor"}, + "tenant": "tenant_abc", + } + + tokenStr := createTestJWT(t, header, payload) + + env, err := cel.NewEnv( + jwt.Library(), + cel.Variable("tokenStr", cel.StringType), + cel.Variable("bearerToken", cel.StringType), + cel.Variable("upperBearerToken", cel.StringType), + ) + if err != nil { + t.Fatalf("cel.NewEnv failed: %v", err) + } + + vars := map[string]any{ + "tokenStr": tokenStr, + "bearerToken": "Bearer " + tokenStr, + "upperBearerToken": "BEARER " + tokenStr, + } + + tests := []struct { + name string + expr string + want any + }{ + { + name: "parse_has_value", + expr: `jwt.parse(tokenStr).hasValue()`, + want: true, + }, + { + name: "parse_bearer_prefix_has_value", + expr: `jwt.parse(bearerToken).hasValue()`, + want: true, + }, + { + name: "parse_uppercase_bearer_prefix_has_value", + expr: `jwt.parse(upperBearerToken).hasValue()`, + want: true, + }, + { + name: "token_algorithm", + expr: `jwt.parse(tokenStr).value().alg == 'RS256'`, + want: true, + }, + { + name: "token_issuer", + expr: `jwt.parse(tokenStr).value().issuer == 'https://auth.example.com'`, + want: true, + }, + { + name: "token_subject", + expr: `jwt.parse(tokenStr).value().subject == 'user_12345'`, + want: true, + }, + { + name: "token_key_id", + expr: `jwt.parse(tokenStr).value().keyId == 'key-123'`, + want: true, + }, + { + name: "token_id", + expr: `jwt.parse(tokenStr).value().id == 'token-unique-id-999'`, + want: true, + }, + { + name: "token_audience", + expr: `'https://api.example.com' in jwt.parse(tokenStr).value().aud`, + want: true, + }, + { + name: "token_presented_by_direct", + expr: `jwt.parse(tokenStr).value().presentedBy('https://auth.example.com', 'https://api.example.com')`, + want: true, + }, + { + name: "token_presented_by_on_optional", + expr: `jwt.parse(tokenStr).presentedBy('https://auth.example.com', 'https://api.example.com')`, + want: true, + }, + { + name: "token_presented_by_mismatch_iss", + expr: `jwt.parse(tokenStr).presentedBy('https://evil.com', 'https://api.example.com')`, + want: false, + }, + { + name: "token_presented_by_mismatch_aud", + expr: `jwt.parse(tokenStr).presentedBy('https://auth.example.com', 'https://wrong-aud.com')`, + want: false, + }, + { + name: "claim_tenant", + expr: `jwt.parse(tokenStr).value().claim('tenant').orValue('')`, + want: "tenant_abc", + }, + { + name: "claim_on_optional", + expr: `jwt.parse(tokenStr).claim('tenant').orValue('')`, + want: "tenant_abc", + }, + { + name: "claim_missing", + expr: `jwt.parse(tokenStr).claim('nonexistent').hasValue()`, + want: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := evalExpr(t, env, tc.expr, vars) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Eval(%q) = %v (%T), want %v (%T)", tc.expr, got, got, tc.want, tc.want) + } + }) + } +} + +func TestParseUnverifiedTokenAndFieldVariations(t *testing.T) { + header := map[string]any{ + "alg": "ES256", + "kid": "k-42", + } + payload := map[string]any{ + "iss": "https://accounts.google.com", + "sub": "10987654321", + "aud": "my-client-id", + "exp": 1700000000, + "nbf": "1699990000", + "iat": 1699990000.5, + } + + tokStr := createTestJWT(t, header, payload) + + tok, err := jwt.ParseToken(tokStr) + if err != nil { + t.Fatalf("ParseToken failed: %v", err) + } + + tests := []struct { + name string + got any + want any + }{ + {"alg", tok.Algorithm, "ES256"}, + {"issuer", tok.Issuer, "https://accounts.google.com"}, + {"subject", tok.Subject, "10987654321"}, + {"key_id", tok.KeyID, "k-42"}, + {"audience", tok.Audience, []string{"my-client-id"}}, + {"exp", tok.ExpiresAt.Unix(), int64(1700000000)}, + {"nbf", tok.NotBefore.Unix(), int64(1699990000)}, + {"iat", tok.IssuedAt.Unix(), int64(1699990000)}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if !reflect.DeepEqual(tc.got, tc.want) { + t.Errorf("%s = %v, want %v", tc.name, tc.got, tc.want) + } + }) + } +} + +func TestClaimsCustomTypes(t *testing.T) { + tok := &jwt.Token{ + Payload: map[string]any{ + "intNum": json.Number("42"), + "floatNum": json.Number("3.14"), + "strNum": json.Number("NaN"), + "rawJSON": json.RawMessage(`{"nested":"value"}`), + "rawMsgBad": json.RawMessage(`bad-json`), + "badNumFloat": json.Number("not-a-number"), + "simpleStr": "hello", + }, + } + + adapter := types.DefaultTypeAdapter + + tests := []struct { + name string + claimName string + validate func(t *testing.T, val ref.Val) + }{ + { + name: "json_number_int", + claimName: "intNum", + validate: func(t *testing.T, val ref.Val) { + if val.Value() != int64(42) { + t.Errorf("expected 42, got %v", val.Value()) + } + }, + }, + { + name: "json_number_float", + claimName: "floatNum", + validate: func(t *testing.T, val ref.Val) { + if val.Value() != float64(3.14) { + t.Errorf("expected 3.14, got %v", val.Value()) + } + }, + }, + { + name: "json_number_nan_string", + claimName: "strNum", + validate: func(t *testing.T, val ref.Val) { + if val == types.OptionalNone { + t.Errorf("expected non-empty optional for strNum") + } + }, + }, + { + name: "raw_json_message", + claimName: "rawJSON", + validate: func(t *testing.T, val ref.Val) { + if val == types.OptionalNone { + t.Errorf("expected rawJSON to be parsed") + } + }, + }, + { + name: "raw_msg_bad_conversion_error", + claimName: "rawMsgBad", + validate: func(t *testing.T, val ref.Val) { + if !types.IsError(val) { + t.Errorf("expected error ref.Val for invalid json.RawMessage, got %v (%T)", val, val) + } + }, + }, + { + name: "bad_num_conversion_error", + claimName: "badNumFloat", + validate: func(t *testing.T, val ref.Val) { + if !types.IsError(val) { + t.Errorf("expected error ref.Val for invalid json.Number, got %v (%T)", val, val) + } + }, + }, + { + name: "simple_string", + claimName: "simpleStr", + validate: func(t *testing.T, val ref.Val) { + if val.Value() != "hello" { + t.Errorf("expected 'hello', got %v", val.Value()) + } + }, + }, + { + name: "nonexistent_claim", + claimName: "nonexistent", + validate: func(t *testing.T, val ref.Val) { + if val != types.OptionalNone { + t.Errorf("expected None for nonexistent claim, got %v", val) + } + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + val := tok.Claim(adapter, tc.claimName) + tc.validate(t, val) + }) + } +} + +func TestJWTTimestampTypes(t *testing.T) { + header := map[string]any{"alg": "RS256", "typ": "JWT"} + + tests := []struct { + name string + payload map[string]any + wantExp int64 + wantIat int64 + wantNbf int64 + }{ + { + name: "float64_and_string", + payload: map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-client", + "exp": float64(1700000000.5), + "iat": int64(1699990000), + "nbf": "1699990000", + }, + wantExp: 1700000000, + wantIat: 1699990000, + wantNbf: 1699990000, + }, + { + name: "uint64_int32_float32", + payload: map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-client", + "exp": uint64(1700000000), + "iat": int32(1699990000), + "nbf": float32(1699990000), + }, + wantExp: 1700000000, + wantIat: 1699990000, + wantNbf: 1699990000, + }, + { + name: "int_uint_uint32", + payload: map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-client", + "exp": int(1700000000), + "iat": uint(1699990000), + "nbf": uint32(1699990000), + }, + wantExp: 1700000000, + wantIat: 1699990000, + wantNbf: 1699990000, + }, + { + name: "string_float", + payload: map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-client", + "exp": "1700000000.5", + "iat": 1699900000, + }, + wantExp: 1700000000, + wantIat: 1699900000, + wantNbf: 0, + }, + { + name: "json_number_float", + payload: map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-client", + "exp": json.Number("1700000000.75"), + "iat": 1699900000, + }, + wantExp: 1700000000, + wantIat: 1699900000, + wantNbf: 0, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + tokStr := createTestJWT(t, header, tc.payload) + tok, err := jwt.ParseToken(tokStr) + if err != nil { + t.Fatalf("ParseToken failed: %v", err) + } + if tok.ExpiresAt.Unix() != tc.wantExp { + t.Errorf("exp = %v, want %v", tok.ExpiresAt.Unix(), tc.wantExp) + } + if tok.IssuedAt.Unix() != tc.wantIat { + t.Errorf("iat = %v, want %v", tok.IssuedAt.Unix(), tc.wantIat) + } + if tc.wantNbf != 0 && tok.NotBefore.Unix() != tc.wantNbf { + t.Errorf("nbf = %v, want %v", tok.NotBefore.Unix(), tc.wantNbf) + } + }) + } +} + +func TestJWTValidateTimesOption(t *testing.T) { + fixedNow := time.Unix(1700000000, 0).UTC() + header := map[string]any{"alg": "RS256", "typ": "JWT"} + + tokValid := createTestJWT(t, header, map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-aud", + "iat": fixedNow.Add(-1 * time.Hour).Unix(), + "nbf": fixedNow.Add(-1 * time.Hour).Unix(), + "exp": fixedNow.Add(1 * time.Hour).Unix(), + }) + + tokExpired := createTestJWT(t, header, map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-aud", + "iat": fixedNow.Add(-2 * time.Hour).Unix(), + "exp": fixedNow.Add(-10 * time.Minute).Unix(), + }) + + tokFutureNbf := createTestJWT(t, header, map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-aud", + "iat": fixedNow.Add(-1 * time.Hour).Unix(), + "nbf": fixedNow.Add(10 * time.Minute).Unix(), + "exp": fixedNow.Add(1 * time.Hour).Unix(), + }) + + tokFutureIat := createTestJWT(t, header, map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-aud", + "iat": fixedNow.Add(10 * time.Minute).Unix(), + "exp": fixedNow.Add(1 * time.Hour).Unix(), + }) + + tokInvertedWindow := createTestJWT(t, header, map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-aud", + "iat": fixedNow.Add(-30 * time.Minute).Unix(), + "nbf": fixedNow.Add(-10 * time.Minute).Unix(), + "exp": fixedNow.Add(-20 * time.Minute).Unix(), + }) + + realNow := time.Now() + tokValidRealTime := createTestJWT(t, header, map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-aud", + "iat": realNow.Add(-1 * time.Hour).Unix(), + "nbf": realNow.Add(-1 * time.Hour).Unix(), + "exp": realNow.Add(1 * time.Hour).Unix(), + }) + + tokExpiredRealTime := createTestJWT(t, header, map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-aud", + "iat": realNow.Add(-2 * time.Hour).Unix(), + "exp": realNow.Add(-1 * time.Hour).Unix(), + }) + + tests := []struct { + name string + options []jwt.Option + tokenStr string + wantPass bool + }{ + { + name: "default_no_validation_allows_expired", + options: nil, + tokenStr: tokExpired, + wantPass: true, + }, + { + name: "validated_valid_token_passes", + options: []jwt.Option{jwt.ValidateTimes(), jwt.Clock(func() time.Time { return fixedNow })}, + tokenStr: tokValid, + wantPass: true, + }, + { + name: "validated_expired_token_rejected", + options: []jwt.Option{jwt.ValidateTimes(), jwt.Clock(func() time.Time { return fixedNow })}, + tokenStr: tokExpired, + wantPass: false, + }, + { + name: "validated_future_nbf_rejected", + options: []jwt.Option{jwt.ValidateTimes(), jwt.Clock(func() time.Time { return fixedNow })}, + tokenStr: tokFutureNbf, + wantPass: false, + }, + { + name: "validated_future_iat_rejected", + options: []jwt.Option{jwt.ValidateTimes(), jwt.Clock(func() time.Time { return fixedNow })}, + tokenStr: tokFutureIat, + wantPass: false, + }, + { + name: "leeway_allows_token_expired_within_window", + options: []jwt.Option{jwt.ValidateTimes(30 * time.Minute), jwt.Clock(func() time.Time { return fixedNow })}, + tokenStr: tokExpired, + wantPass: true, + }, + { + name: "leeway_rejects_inverted_nbf_after_exp", + options: []jwt.Option{jwt.ValidateTimes(30 * time.Minute), jwt.Clock(func() time.Time { return fixedNow })}, + tokenStr: tokInvertedWindow, + wantPass: false, + }, + { + name: "validated_default_clock_valid_token", + options: []jwt.Option{jwt.ValidateTimes()}, + tokenStr: tokValidRealTime, + wantPass: true, + }, + { + name: "validated_default_clock_expired_token", + options: []jwt.Option{jwt.ValidateTimes()}, + tokenStr: tokExpiredRealTime, + wantPass: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + env, err := cel.NewEnv( + jwt.Library(tc.options...), + cel.Variable("tok", cel.StringType), + ) + if err != nil { + t.Fatalf("cel.NewEnv failed: %v", err) + } + got := evalExpr(t, env, `jwt.parse(tok).hasValue()`, map[string]any{"tok": tc.tokenStr}) + if got != tc.wantPass { + t.Errorf("jwt.parse(tok).hasValue() = %v, want %v", got, tc.wantPass) + } + }) + } +} + +func TestJWTOptionalReceiverChaining(t *testing.T) { + header := map[string]any{"alg": "RS256", "typ": "JWT"} + goodTokStr := createTestJWT(t, header, map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-client", + "tag": "prod", + "exp": 1700000000, + "iat": 1699900000, + }) + + envExpired, err := cel.NewEnv( + jwt.Library(jwt.ValidateTimes(), jwt.Clock(func() time.Time { return time.Unix(2000000000, 0) })), + cel.Variable("tok", cel.StringType), + ) + if err != nil { + t.Fatalf("cel.NewEnv failed: %v", err) + } + + tests := []struct { + name string + env *cel.Env + expr string + want any + }{ + { + name: "presented_by_on_optional_none", + env: envExpired, + expr: `jwt.parse(tok).presentedBy('https://auth.example.com', 'my-client')`, + want: false, + }, + { + name: "claim_on_optional_none", + env: envExpired, + expr: `jwt.parse(tok).claim('tag').orValue('default')`, + want: "default", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := evalExpr(t, tc.env, tc.expr, map[string]any{"tok": goodTokStr}) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Eval(%q) = %v, want %v", tc.expr, got, tc.want) + } + }) + } +} + +func TestJWTDirectTokenVariables(t *testing.T) { + header := map[string]any{"alg": "RS256", "typ": "JWT"} + goodTokStr := createTestJWT(t, header, map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-client", + "tag": "prod", + "exp": 1700000000, + "iat": 1699900000, + }) + + tok, err := jwt.ParseToken(goodTokStr) + if err != nil { + t.Fatalf("ParseToken failed: %v", err) + } + + env, err := cel.NewEnv( + jwt.Library(), + cel.Variable("t", cel.ObjectType("jwt.Token")), + ) + if err != nil { + t.Fatalf("cel.NewEnv failed: %v", err) + } + + vars := map[string]any{"t": tok} + + tests := []struct { + name string + expr string + want any + }{ + { + name: "presented_by_direct_match", + expr: `t.presentedBy('https://auth.example.com', 'my-client')`, + want: true, + }, + { + name: "presented_by_direct_mismatch", + expr: `t.presentedBy('https://auth.example.com', 'wrong-client')`, + want: false, + }, + { + name: "claim_direct_present", + expr: `t.claim('tag').orValue('')`, + want: "prod", + }, + { + name: "claim_direct_missing", + expr: `t.claim('nonexistent').orValue('default')`, + want: "default", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := evalExpr(t, env, tc.expr, vars) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Eval(%q) = %v, want %v", tc.expr, got, tc.want) + } + }) + } +} + +func TestJWTPresentedByWithAuthorizedPartyAZP(t *testing.T) { + header := map[string]any{"alg": "RS256", "typ": "JWT"} + + tokPayload := map[string]any{ + "iss": "https://accounts.google.com", + "sub": "user-456", + "aud": "https://api.example.com", + "azp": "frontend-client-app-id", + "exp": 1700000000, + "iat": 1699900000, + } + tokStr := createTestJWT(t, header, tokPayload) + + tokNoAZP := createTestJWT(t, header, map[string]any{ + "iss": "https://accounts.google.com", + "sub": "user-456", + "aud": "https://api.example.com", + "exp": 1700000000, + "iat": 1699900000, + }) + + env, err := cel.NewEnv( + jwt.Library(), + cel.Variable("tokStr", cel.StringType), + ) + if err != nil { + t.Fatalf("cel.NewEnv failed: %v", err) + } + + tests := []struct { + name string + expr string + tokenStr string + want any + }{ + { + name: "azp_field_access", + expr: `jwt.parse(tokStr).value().azp`, + tokenStr: tokStr, + want: "frontend-client-app-id", + }, + { + name: "presented_by_matches_azp", + expr: `jwt.parse(tokStr).presentedBy('https://accounts.google.com', 'frontend-client-app-id')`, + tokenStr: tokStr, + want: true, + }, + { + name: "presented_by_rejects_aud_when_azp_exists", + expr: `jwt.parse(tokStr).presentedBy('https://accounts.google.com', 'https://api.example.com')`, + tokenStr: tokStr, + want: false, + }, + { + name: "presented_by_falls_back_to_aud_when_azp_omitted", + expr: `jwt.parse(tokStr).presentedBy('https://accounts.google.com', 'https://api.example.com')`, + tokenStr: tokNoAZP, + want: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := evalExpr(t, env, tc.expr, map[string]any{"tokStr": tc.tokenStr}) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Eval(%q) = %v, want %v", tc.expr, got, tc.want) + } + }) + } +} + +func TestJWTParsingErrorsAndEncodings(t *testing.T) { + header := map[string]any{"alg": "RS256", "typ": "JWT"} + validPayload := map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-client", + "exp": 1700000000, + "iat": 1699900000, + } + + goodHeaderB64 := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"RS256"}`)) + validPayloadBytes, _ := json.Marshal(validPayload) + validPayloadB64 := base64.RawURLEncoding.EncodeToString(validPayloadBytes) + badJSONB64 := base64.RawURLEncoding.EncodeToString([]byte(`not-json`)) + + tests := []struct { + name string + tokenStr string + errMsg string + }{ + { + name: "one_segment", + tokenStr: "one", + errMsg: "invalid token format", + }, + { + name: "four_segments", + tokenStr: "one.two.three.four", + errMsg: "invalid token format", + }, + { + name: "empty_token", + tokenStr: "", + errMsg: "invalid token format", + }, + { + name: "bad_header_base64", + tokenStr: "!bad!.payload.sig", + errMsg: "failed to decode header", + }, + { + name: "non_json_header", + tokenStr: badJSONB64 + "." + validPayloadB64 + ".sig", + errMsg: "failed to parse header JSON", + }, + { + name: "missing_header_alg", + tokenStr: base64.RawURLEncoding.EncodeToString([]byte(`{"typ":"JWT"}`)) + "." + validPayloadB64 + ".sig", + errMsg: "missing required header: 'alg'", + }, + { + name: "bad_payload_base64", + tokenStr: goodHeaderB64 + ".!bad!.sig", + errMsg: "failed to decode payload", + }, + { + name: "non_json_payload", + tokenStr: goodHeaderB64 + "." + badJSONB64 + ".sig", + errMsg: "failed to parse payload JSON", + }, + { + name: "exceeds_max_token_size", + tokenStr: strings.Repeat("a", 11*1024*1024), + errMsg: "token size exceeds maximum allowed limit", + }, + { + name: "malformed_nbf_claim", + tokenStr: createTestJWT(t, header, map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": "my-client", + "exp": 1700000000, + "iat": 1699900000, + "nbf": "invalid-timestamp", + }), + errMsg: "invalid claim 'nbf'", + }, + { + name: "non_string_element_in_aud_list", + tokenStr: createTestJWT(t, header, map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": []any{"client-1", 12345}, + "exp": 1700000000, + "iat": 1699900000, + }), + errMsg: "invalid claim 'aud'", + }, + { + name: "invalid_aud_type", + tokenStr: createTestJWT(t, header, map[string]any{ + "iss": "https://auth.example.com", + "sub": "user-123", + "aud": 12345, + "exp": 1700000000, + "iat": 1699900000, + }), + errMsg: "invalid claim 'aud'", + }, + { + name: "excessive_dots", + tokenStr: strings.Repeat(".", 100), + errMsg: "invalid token format", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + _, err := jwt.ParseToken(tc.tokenStr) + if err == nil { + t.Fatalf("expected error containing %q, got nil", tc.errMsg) + } + if !strings.Contains(err.Error(), tc.errMsg) { + t.Errorf("error = %q, want error containing %q", err.Error(), tc.errMsg) + } + }) + } + + requiredClaims := []string{"iss", "sub", "aud", "exp", "iat"} + for _, claim := range requiredClaims { + t.Run("missing_claim_"+claim, func(t *testing.T) { + p := make(map[string]any) + for k, v := range validPayload { + if k != claim { + p[k] = v + } + } + tokStr := createTestJWT(t, header, p) + if _, err := jwt.ParseToken(tokStr); err == nil { + t.Errorf("expected error when missing required claim %q, got nil", claim) + } + }) + } +} + +func TestTokenIsValidAt(t *testing.T) { + now := time.Date(2026, 8, 10, 12, 0, 0, 0, time.UTC) + leeway := 5 * time.Minute + + tests := []struct { + name string + token jwt.Token + refTime time.Time + leeway time.Duration + wantValid bool + }{ + { + name: "valid token within active window", + token: jwt.Token{ + IssuedAt: now.Add(-1 * time.Hour), + NotBefore: now.Add(-30 * time.Minute), + ExpiresAt: now.Add(1 * time.Hour), + }, + refTime: now, + leeway: 0, + wantValid: true, + }, + { + name: "issued-at exactly now", + token: jwt.Token{ + IssuedAt: now, + ExpiresAt: now.Add(1 * time.Hour), + }, + refTime: now, + leeway: 0, + wantValid: true, + }, + { + name: "issued-at in future within leeway", + token: jwt.Token{ + IssuedAt: now.Add(3 * time.Minute), + ExpiresAt: now.Add(1 * time.Hour), + }, + refTime: now, + leeway: leeway, + wantValid: true, + }, + { + name: "issued-at in future beyond leeway", + token: jwt.Token{ + IssuedAt: now.Add(10 * time.Minute), + ExpiresAt: now.Add(1 * time.Hour), + }, + refTime: now, + leeway: leeway, + wantValid: false, + }, + { + name: "not-before in future within leeway", + token: jwt.Token{ + IssuedAt: now.Add(-10 * time.Minute), + NotBefore: now.Add(3 * time.Minute), + ExpiresAt: now.Add(1 * time.Hour), + }, + refTime: now, + leeway: leeway, + wantValid: true, + }, + { + name: "not-before in future beyond leeway", + token: jwt.Token{ + IssuedAt: now.Add(-10 * time.Minute), + NotBefore: now.Add(10 * time.Minute), + ExpiresAt: now.Add(1 * time.Hour), + }, + refTime: now, + leeway: leeway, + wantValid: false, + }, + { + name: "expired token in past within leeway", + token: jwt.Token{ + IssuedAt: now.Add(-1 * time.Hour), + ExpiresAt: now.Add(-3 * time.Minute), + }, + refTime: now, + leeway: leeway, + wantValid: true, + }, + { + name: "expired token in past beyond leeway", + token: jwt.Token{ + IssuedAt: now.Add(-1 * time.Hour), + ExpiresAt: now.Add(-10 * time.Minute), + }, + refTime: now, + leeway: leeway, + wantValid: false, + }, + { + name: "expired token exactly at negative leeway boundary", + token: jwt.Token{ + IssuedAt: now.Add(-1 * time.Hour), + ExpiresAt: now.Add(-5 * time.Minute), + }, + refTime: now, + leeway: leeway, + wantValid: false, + }, + { + name: "inverted nbf > exp", + token: jwt.Token{ + IssuedAt: now.Add(-1 * time.Hour), + NotBefore: now.Add(30 * time.Minute), + ExpiresAt: now.Add(15 * time.Minute), + }, + refTime: now, + leeway: 0, + wantValid: false, + }, + { + name: "inverted iat > exp", + token: jwt.Token{ + IssuedAt: now.Add(30 * time.Minute), + ExpiresAt: now.Add(15 * time.Minute), + }, + refTime: now, + leeway: 1 * time.Hour, + wantValid: false, + }, + { + name: "empty claims (all zero time)", + token: jwt.Token{ + Issuer: "https://auth.example.com", + }, + refTime: now, + leeway: 0, + wantValid: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := tc.token.IsValidAt(tc.refTime, tc.leeway) + if got != tc.wantValid { + t.Errorf("token.IsValidAt(%v, %v) = %v, want %v", tc.refTime, tc.leeway, got, tc.wantValid) + } + }) + } +} From ef240479443ae9bbf89edd93430b1be2173350a7 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Fri, 14 Aug 2026 18:57:44 -0700 Subject: [PATCH 22/37] Support aggregate size computations over list, maps, and structs (#1404) * Support aggregate size computations over list, maps, and structs * Depth and traversal limits for aggregate size calculations * Enhancements to use Foldable and reduce duplication in native size computations --- common/types/BUILD.bazel | 3 + common/types/aggregate_sizer.go | 43 ++ common/types/list.go | 33 +- common/types/list_test.go | 64 +++ common/types/map.go | 31 +- common/types/map_test.go | 95 ++++ common/types/native.go | 17 + common/types/native_test.go | 82 ++++ common/types/object.go | 14 +- common/types/object_test.go | 23 + common/types/optional.go | 8 + common/types/optional_test.go | 19 + common/types/overflow.go | 21 + common/types/size_calc.go | 323 +++++++++++++ common/types/size_calc_test.go | 794 ++++++++++++++++++++++++++++++++ common/types/util_test.go | 37 +- 16 files changed, 1592 insertions(+), 15 deletions(-) create mode 100644 common/types/aggregate_sizer.go create mode 100644 common/types/size_calc.go create mode 100644 common/types/size_calc_test.go diff --git a/common/types/BUILD.bazel b/common/types/BUILD.bazel index 4ecc40031..9a2290f3e 100644 --- a/common/types/BUILD.bazel +++ b/common/types/BUILD.bazel @@ -8,6 +8,7 @@ package( go_library( name = "go_default_library", srcs = [ + "aggregate_sizer.go", "any_value.go", "bool.go", "bytes.go", @@ -28,6 +29,7 @@ go_library( "overflow.go", "provider.go", "regex.go", + "size_calc.go", "string.go", "struct.go", "timestamp.go", @@ -76,6 +78,7 @@ go_test( "optional_test.go", "provider_test.go", "regex_test.go", + "size_calc_test.go", "string_test.go", "timestamp_test.go", "types_test.go", diff --git a/common/types/aggregate_sizer.go b/common/types/aggregate_sizer.go new file mode 100644 index 000000000..d56b8bc4d --- /dev/null +++ b/common/types/aggregate_sizer.go @@ -0,0 +1,43 @@ +// Copyright 2026 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 types + +// AggregateSizer calculates the recursive element size of values. +type AggregateSizer interface { + // AggregateSize returns the size of the input value, if known. + // Otherwise, a unit size of 1 is returned. + AggregateSize(val any) uint32 +} + +// AggregateSizeVisitor interface for ref.Val implementations capable of returning +// their total recursive element count. +type AggregateSizeVisitor interface { + // AggregateSize returns the total count of nested atomic elements (capped at math.MaxUint32). + AggregateSize(sizer AggregateSizer) uint32 +} + +// Helper for computing aggregate sizes of traits.Foldable types. +type foldableAggregateSizer struct { + sizer AggregateSizer + total uint32 +} + +// FoldEntry implements the traits.FoldEntry interface method and counts the aggregate size +// keys and values. +func (f *foldableAggregateSizer) FoldEntry(k, v any) bool { + f.total = safeAddUint32(f.total, f.sizer.AggregateSize(k)) + f.total = safeAddUint32(f.total, f.sizer.AggregateSize(v)) + return true +} diff --git a/common/types/list.go b/common/types/list.go index 028770ed6..483f71261 100644 --- a/common/types/list.go +++ b/common/types/list.go @@ -109,15 +109,10 @@ func NewMutableList(adapter Adapter) traits.MutableLister { // The `Adapter` enables native type to CEL type conversions. type baseList struct { Adapter - value any - - // size indicates the number of elements within the list. - // Since objects are immutable the size of a list is static. - size int - - // get returns a value at the specified integer index. - // The index is guaranteed to be checked against the list index range. - get func(int) any + value any + size int + aggSize uint32 + get func(int) any } // Add implements the traits.Adder interface method. @@ -269,6 +264,19 @@ func (l *baseList) Size() ref.Val { return Int(l.size) } +// AggregateSize implements the AggregateSizeVisitor interface method. +func (l *baseList) AggregateSize(sizer AggregateSizer) uint32 { + if l.aggSize != 0 { + return l.aggSize + } + total := uint32(1) + for i := range l.size { + total = safeAddUint32(total, sizer.AggregateSize(l.get(i))) + } + l.aggSize = total + return total +} + // Type implements the ref.Val interface method. func (l *baseList) Type() ref.Type { return ListType @@ -322,11 +330,13 @@ func (l *mutableList) Add(other ref.Val) ref.Val { case *mutableList: l.mutableValues = append(l.mutableValues, otherList.mutableValues...) l.size += len(otherList.mutableValues) + l.aggSize = 0 case traits.Lister: for i := IntZero; i < otherList.Size().(Int); i++ { l.size++ l.mutableValues = append(l.mutableValues, otherList.Get(i)) } + l.aggSize = 0 default: return MaybeNoSuchOverloadErr(otherList) } @@ -480,6 +490,11 @@ func (l *concatList) Size() ref.Val { return l.cachedSize } +// AggregateSize implements the AggregateSizeVisitor interface method. +func (l *concatList) AggregateSize(sizer AggregateSizer) uint32 { + return safeAddUint32(sizer.AggregateSize(l.prevList), sizer.AggregateSize(l.nextList)) +} + // String converts the concatenated list to a human-readable string. func (l *concatList) String() string { var sb strings.Builder diff --git a/common/types/list_test.go b/common/types/list_test.go index ca134b716..a962a3f1b 100644 --- a/common/types/list_test.go +++ b/common/types/list_test.go @@ -930,3 +930,67 @@ func TestConcatListSizeCached(t *testing.T) { } } } + +func TestListCalculateSize(t *testing.T) { + adapter := DefaultTypeAdapter + + // List literal: [1, [3, 4], [[7, 8], [9, 10]]] + l1 := NewRefValList(adapter, []ref.Val{Int(3), Int(4)}) + l2_1 := NewRefValList(adapter, []ref.Val{Int(7), Int(8)}) + l2_2 := NewRefValList(adapter, []ref.Val{Int(9), Int(10)}) + l2 := NewRefValList(adapter, []ref.Val{l2_1, l2_2}) + nested := NewRefValList(adapter, []ref.Val{Int(1), l1, l2}) + + tests := []struct { + name string + val ref.Val + want uint32 + }{ + { + name: "empty_list", + val: NewRefValList(adapter, []ref.Val{}), + want: 1, + }, + { + name: "flat_list", + val: l1, + want: 3, + }, + { + name: "nested_list", + val: nested, + want: 12, + }, + { + name: "concat_list", + val: l1.Add(l2_1), + want: 6, + }, + { + name: "string_list", + val: NewStringList(adapter, []string{"hello", "world"}), + want: 11, + }, + { + name: "dynamic_list", + val: NewDynamicList(adapter, []any{int64(1), []int64{3, 4}}), + want: 5, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + sizer, ok := tc.val.(AggregateSizeVisitor) + if !ok { + t.Fatalf("expected AggregateSizeVisitor implementation for %T", tc.val) + } + if got := sizer.AggregateSize(NewSizeCalculator()); got != tc.want { + t.Errorf("got aggregate size %d, want %d", got, tc.want) + } + // Caching check (memoized aggSize) + if got := sizer.AggregateSize(NewSizeCalculator()); got != tc.want { + t.Errorf("memoized AggregateSize() got %d, want %d", got, tc.want) + } + }) + } +} diff --git a/common/types/map.go b/common/types/map.go index e4d6f7657..dc502323f 100644 --- a/common/types/map.go +++ b/common/types/map.go @@ -142,8 +142,8 @@ type baseMap struct { // value is the native Go value upon which the map type operators. value any - // size is the number of entries in the map. - size int + size int + aggSize uint32 } // Contains implements the traits.Container interface method. @@ -303,6 +303,17 @@ func (m *baseMap) Size() ref.Val { return Int(m.size) } +// AggregateSize implements the AggregateSizeVisitor interface method. +func (m *baseMap) AggregateSize(sizer AggregateSizer) uint32 { + if m.aggSize != 0 { + return m.aggSize + } + f := foldableAggregateSizer{sizer: sizer, total: 1} + m.Fold(&f) + m.aggSize = f.total + return f.total +} + // String converts the map into a human-readable string. func (m *baseMap) String() string { var sb strings.Builder @@ -380,6 +391,8 @@ func (m *mutableMap) Insert(k, v ref.Val) ref.Val { return NewErr("insert failed: key %v already exists", k) } m.mutableValues[k] = v + m.size++ + m.aggSize = 0 return m } @@ -909,6 +922,20 @@ func (m *protoMap) Size() ref.Val { return Int(m.value.Len()) } +// AggregateSize implements the AggregateSizeVisitor interface method. +func (m *protoMap) AggregateSize(sizer AggregateSizer) uint32 { + if m.value == nil { + return 0 + } + total := uint32(1) + m.value.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { + total = safeAddUint32(total, sizer.AggregateSize(k)) + total = safeAddUint32(total, sizer.AggregateSize(v)) + return true + }) + return total +} + // Type implements the ref.Val interface method. func (m *protoMap) Type() ref.Type { return MapType diff --git a/common/types/map_test.go b/common/types/map_test.go index 81989120d..f085b7e67 100644 --- a/common/types/map_test.go +++ b/common/types/map_test.go @@ -1252,3 +1252,98 @@ func (m proxyLegacyMap) Iterator() traits.Iterator { func (m proxyLegacyMap) Size() ref.Val { return m.proxy.Size() } + +func TestMapCalculateSize(t *testing.T) { + adapter := DefaultTypeAdapter + + // Setup helper data + l := NewRefValList(adapter, []ref.Val{Int(2), Int(3)}) + refValMap := NewRefValMap(adapter, map[ref.Val]ref.Val{ + String("a"): Int(1), + String("b"): l, + }) + + ifaceMap := NewStringInterfaceMap(adapter, map[string]any{ + "a": int64(1), + "b": []any{int64(2), int64(3)}, + }) + + mutMap := NewMutableMap(adapter, map[ref.Val]ref.Val{ + String("a"): Int(1), + String("b"): l, + }) + // Initial evaluation before insert to test aggSize reset + _ = mutMap.(AggregateSizeVisitor).AggregateSize(NewSizeCalculator()) + mutMap.Insert(String("c"), Int(4)) + + reg, err := NewRegistry(&proto3pb.TestAllTypes{}) + if err != nil { + t.Fatalf("NewRegistry() failed: %v", err) + } + msg := &proto3pb.TestAllTypes{ + MapStringString: map[string]string{ + "a": "b", + "c": "d", + }, + } + pbMsg := reg.NativeToValue(msg).(traits.Indexer) + pm := pbMsg.Get(String("map_string_string")).(traits.Mapper) + + tests := []struct { + name string + val ref.Val + want uint32 + }{ + { + name: "empty_ref_val_map", + val: NewRefValMap(adapter, map[ref.Val]ref.Val{}), + want: 1, + }, + { + name: "ref_val_map_nested", + val: refValMap, + want: 7, + }, + { + name: "string_interface_map", + val: ifaceMap, + want: 7, + }, + { + name: "string_string_map", + val: NewStringStringMap(adapter, map[string]string{"k1": "v1", "k2": "v2"}), + want: 9, + }, + { + name: "mutable_map_after_insert", + val: mutMap, + want: 9, + }, + { + name: "proto_map", + val: pm, + want: 5, + }, + { + name: "nil_proto_map", + val: &protoMap{}, + want: 0, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + sizer, ok := tc.val.(AggregateSizeVisitor) + if !ok { + t.Fatalf("expected AggregateSizeVisitor implementation for %T", tc.val) + } + if got := sizer.AggregateSize(NewSizeCalculator()); got != tc.want { + t.Errorf("got aggregate size %d, want %d", got, tc.want) + } + // Caching check (memoized aggSize) + if got := sizer.AggregateSize(NewSizeCalculator()); got != tc.want { + t.Errorf("memoized AggregateSize() got %d, want %d", got, tc.want) + } + }) + } +} diff --git a/common/types/native.go b/common/types/native.go index 33c897b43..965089ddd 100644 --- a/common/types/native.go +++ b/common/types/native.go @@ -363,6 +363,23 @@ func (o *nativeObj) Value() any { return o.val } +// AggregateSize implements the AggregateSizeVisitor interface method. +func (o *nativeObj) AggregateSize(sizer AggregateSizer) uint32 { + refVal := reflect.Indirect(o.refValue) + if !refVal.IsValid() { + return 0 + } + total := uint32(1) + for _, fieldType := range o.valType.fieldsByName { + fieldValue := refVal.FieldByIndex(fieldType.Index) + if !fieldValue.IsValid() || fieldValue.IsZero() { + continue + } + total = safeAddUint32(total, sizer.AggregateSize(fieldValue)) + } + return total +} + func newNativeTypes(rawType reflect.Type, fieldNameHandler NativeTypesFieldNameHandler) ([]*NativeType, error) { nt, err := newNativeType(rawType, fieldNameHandler) if err != nil { diff --git a/common/types/native_test.go b/common/types/native_test.go index 161337538..c18dfc8ea 100644 --- a/common/types/native_test.go +++ b/common/types/native_test.go @@ -1303,6 +1303,88 @@ func TestNativeToValueDelegatesUnregisteredStructs(t *testing.T) { } } +func TestNativeObjectCalculateSize(t *testing.T) { + env, err := cel.NewEnv( + ext.NativeTypes( + reflect.TypeOf(TestAllTypes{}), + reflect.TypeOf(TestNestedType{}), + ), + ) + if err != nil { + t.Fatalf("cel.NewEnv() failed: %v", err) + } + adapter := env.CELTypeAdapter() + + tests := []struct { + name string + val any + want uint32 + }{ + { + name: "empty_struct", + val: &TestNestedType{}, + want: 1, // 1 (container) + }, + { + name: "struct_with_scalar_and_list", + val: &TestNestedType{ + NestedListVal: []string{"a", "b", "c"}, + }, + want: 5, // 1 (root struct) + ["a", "b", "c"] (1 list container + 3 elements = 4) = 5 + }, + { + name: "struct_with_nested_map", + val: &TestNestedType{ + NestedMapVal: map[int64]bool{1: true, 2: false}, + }, + want: 6, // 1 (root struct) + map (1 container + (1+1) + (1+1) = 5) = 6 + }, + { + name: "nested_struct", + val: &TestAllTypes{ + StringVal: "hello", + NestedVal: &TestNestedType{ + NestedListVal: []string{"a", "b"}, + }, + }, + // 1 (root struct) + "hello"(5) + NestedVal(1 container + ["a", "b"](1+2=3) = 4) = 10 + want: 10, + }, + { + name: "bytes_and_time", + val: &TestAllTypes{ + BytesVal: []byte("test"), + DurationVal: time.Second, + TimestampVal: time.Unix(100, 0), + }, + want: 7, + }, + { + name: "slice_of_structs", + val: &TestAllTypes{ + ListVal: []*TestNestedType{ + {NestedListVal: []string{"x"}}, + {NestedListVal: []string{"y", "z"}}, + }, + }, + want: 9, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + val := adapter.NativeToValue(tc.val) + sizer, ok := val.(types.AggregateSizeVisitor) + if !ok { + t.Fatalf("expected types.AggregateSizeVisitor implementation for %T", val) + } + if got := sizer.AggregateSize(types.NewSizeCalculator()); got != tc.want { + t.Errorf("got aggregate size %d, want %d", got, tc.want) + } + }) + } +} + func BenchmarkNativeTypesEval(b *testing.B) { benchmarks := []struct { name string diff --git a/common/types/object.go b/common/types/object.go index bb2a09e87..1d45d4e88 100644 --- a/common/types/object.go +++ b/common/types/object.go @@ -167,9 +167,17 @@ func (o *protoObj) Value() any { return o.value } -type protoObjField struct { - fd protoreflect.FieldDescriptor - v protoreflect.Value +// AggregateSize implements the AggregateSizeVisitor interface method. +func (o *protoObj) AggregateSize(sizer AggregateSizer) uint32 { + if o.value == nil { + return 0 + } + total := uint32(1) + o.value.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { + total = safeAddUint32(total, sizer.AggregateSize(v)) + return true + }) + return total } func (o *protoObj) format(sb *strings.Builder) { diff --git a/common/types/object_test.go b/common/types/object_test.go index b2e2207ea..3412d9188 100644 --- a/common/types/object_test.go +++ b/common/types/object_test.go @@ -257,3 +257,26 @@ func TestProtoObjectConvertToType(t *testing.T) { t.Error("identity type conversion failed") } } + +func TestProtoObjectCalculateSize(t *testing.T) { + msg := &exprpb.ParsedExpr{ + SourceInfo: &exprpb.SourceInfo{ + LineOffsets: []int32{1, 2, 3}, + }, + } + reg := newTestRegistry(t, ProtoTypeDefs(msg)) + objVal := reg.NativeToValue(msg) + sizer, ok := objVal.(AggregateSizeVisitor) + if !ok { + t.Fatalf("expected AggregateSizeVisitor implementation for protoObj") + } + // 1 (protoObj container) + SourceInfo field (1 container + 1 list container + 3 list elements = 5) = 6 + if got := sizer.AggregateSize(NewSizeCalculator()); got != 6 { + t.Errorf("got aggregate size %d, want 6", got) + } + + nilObj := &protoObj{} + if got := nilObj.AggregateSize(NewSizeCalculator()); got != 0 { + t.Errorf("got nil protoObj aggregate size %d, want 0", got) + } +} diff --git a/common/types/optional.go b/common/types/optional.go index 0d861823d..5d27f2c2d 100644 --- a/common/types/optional.go +++ b/common/types/optional.go @@ -120,3 +120,11 @@ func (o *Optional) Value() any { } return o.value.Value() } + +// AggregateSize implements the AggregateSizeVisitor interface method. +func (o *Optional) AggregateSize(sizer AggregateSizer) uint32 { + if !o.HasValue() { + return 0 + } + return safeAddUint32(1, sizer.AggregateSize(o.value)) +} diff --git a/common/types/optional_test.go b/common/types/optional_test.go index 89f28d7e7..f41b77b6c 100644 --- a/common/types/optional_test.go +++ b/common/types/optional_test.go @@ -185,3 +185,22 @@ func TestOptionalValue(t *testing.T) { t.Errorf("OptionalNone.Value() got %v, wanted nil", OptionalNone.Value()) } } + +func TestOptionalCalculateSize(t *testing.T) { + calc := NewSizeCalculator() + none := OptionalNone + if sizer, ok := any(none).(AggregateSizeVisitor); !ok || sizer.AggregateSize(calc) != 0 { + t.Errorf("expected 0 for OptionalNone") + } + + someScalar := OptionalOf(Int(42)) + if sizer, ok := any(someScalar).(AggregateSizeVisitor); !ok || sizer.AggregateSize(calc) != 2 { + t.Errorf("got %d for OptionalOf(scalar), want 2", sizer.AggregateSize(calc)) + } + + l := NewRefValList(DefaultTypeAdapter, []ref.Val{Int(1), Int(2)}) + someList := OptionalOf(l) + if sizer, ok := any(someList).(AggregateSizeVisitor); !ok || sizer.AggregateSize(calc) != 4 { + t.Errorf("got %d for OptionalOf(list of 2), want 4", sizer.AggregateSize(calc)) + } +} diff --git a/common/types/overflow.go b/common/types/overflow.go index dcb66ef59..49b15377f 100644 --- a/common/types/overflow.go +++ b/common/types/overflow.go @@ -427,3 +427,24 @@ func uint64ToInt64Lossless(v uint64) (int64, bool) { i, err := uint64ToInt64Checked(v) return i, err == nil } + +func safeAddUint32(a, b uint32) uint32 { + if math.MaxUint32-a < b { + return math.MaxUint32 + } + return a + b +} + +func safeUint32FromInt(n int) uint32 { + if n < 0 || uint64(n) > math.MaxUint32 { + return math.MaxUint32 + } + return uint32(n) +} + +func safeUint32FromBoxedInt(v Int) uint32 { + if v < 0 || v > math.MaxUint32 { + return math.MaxUint32 + } + return uint32(v) +} diff --git a/common/types/size_calc.go b/common/types/size_calc.go new file mode 100644 index 000000000..ab6cb4576 --- /dev/null +++ b/common/types/size_calc.go @@ -0,0 +1,323 @@ +// Copyright 2026 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 types + +import ( + "math" + "reflect" + "time" + "unicode/utf8" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" +) + +const ( + defaultSizeCalculatorMaxDepth = 5 + defaultSizeCalculatorMaxTraversal = 10000 +) + +// SizeCalculatorOption configures a SizeCalculator instance. +type SizeCalculatorOption func(*SizeCalculator) + +// SizeCalculatorMaxDepth sets the maximum object depth limit before saturating to math.MaxUint32. +func SizeCalculatorMaxDepth(depth int) SizeCalculatorOption { + return func(s *SizeCalculator) { + s.maxDepth = depth + } +} + +// SizeCalculatorMaxTraversal sets the maximum object traversal limit before saturating to math.MaxUint32. +func SizeCalculatorMaxTraversal(traversal int) SizeCalculatorOption { + return func(s *SizeCalculator) { + s.maxTraversal = traversal + } +} + +// SizeCalculator calculates the recursive element size of values. +type SizeCalculator struct { + version int + maxDepth int + maxTraversal int +} + +// NewSizeCalculator returns a new SizeCalculator configured with optional SizeCalculatorOption settings. +func NewSizeCalculator(opts ...SizeCalculatorOption) *SizeCalculator { + s := &SizeCalculator{ + version: 0, + maxDepth: defaultSizeCalculatorMaxDepth, + maxTraversal: defaultSizeCalculatorMaxTraversal, + } + for _, opt := range opts { + opt(s) + } + return s +} + +// Version returns the calculation version. +func (s *SizeCalculator) Version() int { + return s.version +} + +type sizeContext struct { + calc *SizeCalculator + depth int + traversalCount *int +} + +func (c sizeContext) childContext() sizeContext { + c.depth++ + return c +} + +func (c sizeContext) visitNode() bool { + *c.traversalCount++ + if *c.traversalCount > c.calc.maxTraversal || c.depth > c.calc.maxDepth { + return false + } + return true +} + +// AggregateSize returns the size of the input value, if known. +// Otherwise, a unit size of 1 is returned. +func (s *SizeCalculator) AggregateSize(val any) uint32 { + traversals := 0 + ctx := sizeContext{ + calc: s, + depth: 1, + traversalCount: &traversals, + } + return ctx.AggregateSize(val) +} + +// AggregateSize implements the ref.Val interface and allows for the generation of nested +// child context values which are necessary for correct traversal count tracking. +func (c sizeContext) AggregateSize(val any) uint32 { + if !c.visitNode() { + return math.MaxUint32 + } + switch v := val.(type) { + case AggregateSizeVisitor: + return v.AggregateSize(c.childContext()) + case traits.Foldable: + f := foldableAggregateSizer{sizer: c.childContext(), total: 1} + v.Fold(&f) + return f.total + case traits.Mapper: + total := uint32(1) + it := v.Iterator() + childCtx := c.childContext() + for it.HasNext() == True { + key := it.Next() + val, _ := v.Find(key) + total = safeAddUint32(total, childCtx.AggregateSize(key)) + total = safeAddUint32(total, childCtx.AggregateSize(val)) + } + return total + case traits.Lister: + total := uint32(1) + it := v.Iterator() + childCtx := c.childContext() + for it.HasNext() == True { + total = safeAddUint32(total, childCtx.AggregateSize(it.Next())) + } + return total + case traits.Sizer: + return safeUint32FromBoxedInt(v.Size().(Int)) + case Bool, Int, Uint, Double, Duration, Timestamp, Null, *Type, *Err, *Unknown: + return 1 + case ref.Val: + return c.AggregateSize(v.Value()) + case protoreflect.Value: + return c.AggregateSize(v.Interface()) + case protoreflect.MapKey: + return c.AggregateSize(v.Value().Interface()) + case protoreflect.Message: + return getProtoMessageAggregateSize(c, v) + case protoreflect.List: + return getProtoListAggregateSize(c, v) + case protoreflect.Map: + return getProtoMapAggregateSize(c, v) + case proto.Message: + if v == nil { + return 0 + } + return getProtoMessageAggregateSize(c, v.ProtoReflect()) + case reflect.Value: + return getReflectValueAggregateSize(c, v) + case string: + return safeUint32FromInt(utf8.RuneCountInString(v)) + case []byte: + return safeUint32FromInt(len(v)) + case int, int8, int16, int32, int64, + uint, uint8, uint16, uint32, uint64, + float32, float64, bool, time.Time, time.Duration, nil: + return 1 + default: + return getReflectValueAggregateSize(c, reflect.ValueOf(val)) + } +} + +func getProtoFieldAggregateSize(c sizeContext, fd protoreflect.FieldDescriptor, v protoreflect.Value) uint32 { + if !c.visitNode() { + return math.MaxUint32 + } + childCtx := c.childContext() + if fd.IsMap() { + return getProtoMapAggregateSize(childCtx, v.Map()) + } + if fd.IsList() { + return getProtoListAggregateSize(childCtx, v.List()) + } + return childCtx.AggregateSize(v.Interface()) +} + +func getProtoMessageAggregateSize(c sizeContext, m protoreflect.Message) uint32 { + if !m.IsValid() { + return 0 + } + if !c.visitNode() { + return math.MaxUint32 + } + childCtx := c.childContext() + total := uint32(1) + m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { + total = safeAddUint32(total, getProtoFieldAggregateSize(childCtx, fd, v)) + return true + }) + return total +} + +func getProtoListAggregateSize(c sizeContext, l protoreflect.List) uint32 { + if !l.IsValid() { + return 0 + } + if !c.visitNode() { + return math.MaxUint32 + } + childCtx := c.childContext() + total := uint32(1) + for i := range l.Len() { + total = safeAddUint32(total, childCtx.AggregateSize(l.Get(i).Interface())) + } + return total +} + +func getProtoMapAggregateSize(c sizeContext, m protoreflect.Map) uint32 { + if !m.IsValid() { + return 0 + } + if !c.visitNode() { + return math.MaxUint32 + } + childCtx := c.childContext() + total := uint32(1) + m.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { + total = safeAddUint32(total, childCtx.AggregateSize(k.Value().Interface())) + total = safeAddUint32(total, childCtx.AggregateSize(v.Interface())) + return true + }) + return total +} + +func getReflectValueAggregateSize(c sizeContext, fieldVal reflect.Value) uint32 { + if !fieldVal.IsValid() { + return 0 + } + if !c.visitNode() { + return math.MaxUint32 + } + childCtx := c.childContext() + switch fieldVal.Kind() { + case reflect.String: + return safeUint32FromInt(utf8.RuneCountInString(fieldVal.String())) + case reflect.Slice, reflect.Array: + elemType := fieldVal.Type().Elem() + if elemType.Kind() == reflect.Uint8 { + return safeUint32FromInt(fieldVal.Len()) + } + total := safeAddUint32(1, safeUint32FromInt(fieldVal.Len())) + switch elemType.Kind() { + case reflect.String: + total = 1 + for i := 0; i < fieldVal.Len(); i++ { + total = safeAddUint32(total, childCtx.AggregateSize(fieldVal.Index(i).String())) + } + case reflect.Struct, reflect.Pointer, reflect.Slice, reflect.Array, reflect.Map, reflect.Interface: + total = 1 + for i := 0; i < fieldVal.Len(); i++ { + total = safeAddUint32(total, getReflectValueAggregateSize(childCtx, fieldVal.Index(i))) + } + } + return total + case reflect.Map: + total := uint32(1) + iter := fieldVal.MapRange() + for iter.Next() { + total = safeAddUint32(total, getReflectValueAggregateSize(childCtx, iter.Key())) + total = safeAddUint32(total, getReflectValueAggregateSize(childCtx, iter.Value())) + } + return total + case reflect.Pointer, reflect.Interface: + if fieldVal.IsNil() { + return 0 + } + if sz, ok := checkCustomSizer(childCtx, fieldVal); ok { + return sz + } + return getReflectValueAggregateSize(c, fieldVal.Elem()) + case reflect.Struct: + if fieldVal.Type() == timestampType || fieldVal.Type() == durationType { + return 1 + } + if sz, ok := checkCustomSizer(childCtx, fieldVal); ok { + return sz + } + total := uint32(1) + t := fieldVal.Type() + numFields := fieldVal.NumField() + for i := range numFields { + if !t.Field(i).IsExported() { + continue + } + fVal := fieldVal.Field(i) + if !fVal.IsValid() || fVal.IsZero() { + continue + } + total = safeAddUint32(total, getReflectValueAggregateSize(childCtx, fVal)) + } + return total + default: + return 1 + } +} + +func checkCustomSizer(c sizeContext, fieldVal reflect.Value) (uint32, bool) { + if !fieldVal.CanInterface() { + return 0, false + } + + switch sizer := fieldVal.Interface().(type) { + case AggregateSizeVisitor: + return sizer.AggregateSize(c), true + case traits.Sizer: + return safeUint32FromBoxedInt(sizer.Size().(Int)), true + default: + return 0, false + } +} diff --git a/common/types/size_calc_test.go b/common/types/size_calc_test.go new file mode 100644 index 000000000..43a4b064e --- /dev/null +++ b/common/types/size_calc_test.go @@ -0,0 +1,794 @@ +// Copyright 2026 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 types + +import ( + "fmt" + "math" + "reflect" + "testing" + "time" + + "google.golang.org/protobuf/reflect/protoreflect" + + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + + proto3pb "github.com/google/cel-go/test/proto3pb" +) + +func TestCalculateSize(t *testing.T) { + adapter := DefaultTypeAdapter + + tests := []struct { + name string + val any + want uint32 + }{ + { + name: "aggregate_sizer_list", + val: NewRefValList(adapter, []ref.Val{Int(1), Int(2)}), + want: 3, + }, + { + name: "sizer_string", + val: String("hello"), + want: 5, + }, + { + name: "sizer_bytes", + val: Bytes("world"), + want: 5, + }, + { + name: "err_val", + val: NewErr("test error"), + want: 1, + }, + { + name: "unknown_val", + val: &Unknown{}, + want: 1, + }, + { + name: "type_val", + val: IntType, + want: 1, + }, + { + name: "null_val", + val: NullValue, + want: 1, + }, + { + name: "scalar_ref_val_int", + val: Int(42), + want: 1, + }, + { + name: "scalar_ref_val_double", + val: Double(1.5), + want: 1, + }, + { + name: "scalar_ref_val_bool", + val: True, + want: 1, + }, + { + name: "scalar_ref_val_timestamp", + val: Timestamp{Time: time.Unix(100, 0)}, + want: 1, + }, + { + name: "scalar_ref_val_duration", + val: Duration{Duration: time.Second}, + want: 1, + }, + { + name: "proto_value_string", + val: protoreflect.ValueOfString("hello"), + want: 5, + }, + { + name: "proto_value_bytes", + val: protoreflect.ValueOfBytes([]byte("world")), + want: 5, + }, + { + name: "proto_value_int", + val: protoreflect.ValueOfInt32(42), + want: 1, + }, + { + name: "proto_map_key", + val: protoreflect.MapKey(protoreflect.ValueOfString("key")), + want: 3, + }, + { + name: "proto_message", + val: &proto3pb.TestAllTypes{SingleString: "hello"}, + want: 6, // 1 (root) + 5 (string) = 6 + }, + { + name: "proto_message_with_list_and_map", + val: &proto3pb.TestAllTypes{ + RepeatedString: []string{"a", "b"}, + MapStringString: map[string]string{"k": "v"}, + }, + want: 7, + }, + { + name: "protoreflect_message", + val: (&proto3pb.TestAllTypes{SingleInt64: 10}).ProtoReflect(), + want: 2, // 1 (root) + 1 (int64) = 2 + }, + { + name: "protoreflect_list", + val: (&proto3pb.TestAllTypes{RepeatedString: []string{"a", "b"}}).ProtoReflect().Get((&proto3pb.TestAllTypes{}).ProtoReflect().Descriptor().Fields().ByName("repeated_string")).List(), + want: 3, // 1 (container) + 1("a") + 1("b") = 3 + }, + { + name: "protoreflect_map", + val: (&proto3pb.TestAllTypes{MapStringString: map[string]string{"k": "v"}}).ProtoReflect().Get((&proto3pb.TestAllTypes{}).ProtoReflect().Descriptor().Fields().ByName("map_string_string")).Map(), + want: 3, // 1 (container) + 1("k") + 1("v") = 3 + }, + { + name: "nil_proto_message", + val: (*proto3pb.TestAllTypes)(nil), + want: 0, + }, + { + name: "reflect_value", + val: reflect.ValueOf("reflected"), + want: 9, + }, + { + name: "native_string", + val: "hello", + want: 5, + }, + { + name: "native_bytes", + val: []byte("world"), + want: 5, + }, + { + name: "native_int", + val: 42, + want: 1, + }, + { + name: "native_float", + val: 3.14, + want: 1, + }, + { + name: "native_bool", + val: true, + want: 1, + }, + { + name: "native_time", + val: time.Now(), + want: 1, + }, + { + name: "native_duration", + val: time.Hour, + want: 1, + }, + { + name: "native_nil", + val: nil, + want: 1, + }, + { + name: "custom_struct", + val: struct{ Name string }{"cel"}, + want: 4, // 1 (root) + 3 ("cel") = 4 + }, + { + name: "custom_lister", + val: proxyLegacyList{proxy: NewRefValList(DefaultTypeAdapter, []ref.Val{String("a"), String("b")})}, + want: 3, // 1 (container) + 1 ("a") + 1 ("b") = 3 + }, + { + name: "custom_mapper", + val: interopFoldableMap{Mapper: NewStringStringMap(DefaultTypeAdapter, map[string]string{"key": "val"})}, + want: 7, // 1 (container) + 3 ("key") + 3 ("val") = 7 + }, + { + name: "custom_pure_mapper", + val: customPureMapper{Mapper: NewStringStringMap(DefaultTypeAdapter, map[string]string{"key": "val"})}, + want: 7, // 1 (container) + 3 ("key") + 3 ("val") = 7 + }, + { + name: "custom_sizer_struct_field", + val: struct{ Sizer traits.Sizer }{Sizer: customSizerVal(42)}, + want: 43, // 1 (struct container) + 42 (custom sizer) = 43 + }, + { + name: "custom_visitor_struct_field", + val: struct{ Visitor customVisitorVal }{Visitor: customVisitorVal{Val: 1}}, + want: 101, // 1 (struct container) + 100 (custom visitor) = 101 + }, + { + name: "custom_sizer_pointer", + val: newCustomSizerPtr(42), + want: 42, + }, + { + name: "custom_visitor_pointer", + val: &customVisitorVal{Val: 1}, + want: 100, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + calculator := NewSizeCalculator() + if got := calculator.AggregateSize(tc.val); got != tc.want { + t.Errorf("AggregateSize(%v) got %d, want %d", tc.val, got, tc.want) + } + }) + } +} + +type customPureMapper struct { + traits.Mapper +} + +type customSizerVal int + +func (c customSizerVal) Size() ref.Val { + return Int(c) +} + +type customSizerPtr struct { + val int +} + +func (c *customSizerPtr) Size() ref.Val { + return Int(c.val) +} + +func newCustomSizerPtr(v int) *customSizerPtr { + return &customSizerPtr{val: v} +} + +type customVisitorVal struct { + Val int +} + +func (c customVisitorVal) AggregateSize(sizer AggregateSizer) uint32 { + return 100 +} + +func TestNativeObjCalculateSizeNil(t *testing.T) { + nilNative := &nativeObj{} + if got := nilNative.AggregateSize(NewSizeCalculator()); got != 0 { + t.Errorf("nil nativeObj.AggregateSize() got %d, want 0", got) + } +} + +func TestSizeCalculatorOptions(t *testing.T) { + adapter := DefaultTypeAdapter + + var makeNestedList func(depth int) ref.Val + makeNestedList = func(depth int) ref.Val { + if depth <= 1 { + return NewRefValList(adapter, []ref.Val{Int(1)}) + } + return NewRefValList(adapter, []ref.Val{makeNestedList(depth - 1)}) + } + + t.Run("maxDepth default limit 5", func(t *testing.T) { + calc := NewSizeCalculator() + list5 := makeNestedList(4) + if got := calc.AggregateSize(list5); got == math.MaxUint32 { + t.Errorf("AggregateSize for depth 5 got MaxUint32, want calculated size") + } + + list6 := makeNestedList(5) + if got := calc.AggregateSize(list6); got != math.MaxUint32 { + t.Errorf("AggregateSize for depth 6 got %d, want MaxUint32", got) + } + }) + + t.Run("maxDepth custom option", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxDepth(2)) + list2 := makeNestedList(1) + if got := calc.AggregateSize(list2); got == math.MaxUint32 { + t.Errorf("AggregateSize for depth 2 got MaxUint32, want calculated size") + } + + list3 := makeNestedList(2) + if got := calc.AggregateSize(list3); got != math.MaxUint32 { + t.Errorf("AggregateSize for depth 3 got %d, want MaxUint32", got) + } + }) + + t.Run("maxTraversal default limit 10000", func(t *testing.T) { + calc := NewSizeCalculator() + smallElems := make([]ref.Val, 100) + for i := 0; i < 100; i++ { + smallElems[i] = Int(i) + } + smallList := NewRefValList(adapter, smallElems) + if got := calc.AggregateSize(smallList); got == math.MaxUint32 { + t.Errorf("AggregateSize for 100 elements got MaxUint32, want calculated size") + } + + largeElems := make([]ref.Val, 10001) + for i := 0; i < 10001; i++ { + largeElems[i] = Int(i) + } + largeList := NewRefValList(adapter, largeElems) + if got := calc.AggregateSize(largeList); got != math.MaxUint32 { + t.Errorf("AggregateSize for 10001 elements got %d, want MaxUint32", got) + } + }) + + t.Run("maxTraversal custom option", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxTraversal(5)) + list4 := NewRefValList(adapter, []ref.Val{Int(1), Int(2), Int(3), Int(4)}) + if got := calc.AggregateSize(list4); got == math.MaxUint32 { + t.Errorf("AggregateSize for 5 nodes got MaxUint32, want calculated size") + } + + list5 := NewRefValList(adapter, []ref.Val{Int(1), Int(2), Int(3), Int(4), Int(5)}) + if got := calc.AggregateSize(list5); got != math.MaxUint32 { + t.Errorf("AggregateSize for 6 nodes got %d, want MaxUint32", got) + } + }) + + t.Run("proto depth limit", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxDepth(2)) + msg := &proto3pb.TestAllTypes{ + RepeatedNestedMessage: []*proto3pb.TestAllTypes_NestedMessage{ + {Bb: 42}, + }, + } + if got := calc.AggregateSize(msg); got != math.MaxUint32 { + t.Errorf("AggregateSize for proto nested msg got %d, want MaxUint32", got) + } + }) + + t.Run("cel map depth limit", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxDepth(2)) + nestedMap := NewRefValMap(adapter, map[ref.Val]ref.Val{ + String("k"): NewRefValMap(adapter, map[ref.Val]ref.Val{ + String("subk"): String("subv"), + }), + }) + if got := calc.AggregateSize(nestedMap); got != math.MaxUint32 { + t.Errorf("AggregateSize for nested map got %d, want MaxUint32", got) + } + }) + + t.Run("native struct depth limit", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxDepth(2)) + type Level3 struct{ Val string } + type Level2 struct{ L3 Level3 } + type Level1 struct{ L2 Level2 } + + obj := Level1{L2: Level2{L3: Level3{Val: "deep"}}} + if got := calc.AggregateSize(obj); got != math.MaxUint32 { + t.Errorf("AggregateSize for native struct depth > 2 got %d, want MaxUint32", got) + } + }) + + t.Run("native map depth limit", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxDepth(2)) + m := map[string]map[string]string{ + "outer": {"inner": "val"}, + } + if got := calc.AggregateSize(m); got != math.MaxUint32 { + t.Errorf("AggregateSize for native nested map depth > 2 got %d, want MaxUint32", got) + } + }) + + t.Run("maxTraversal map limit", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxTraversal(3)) + m := NewRefValMap(adapter, map[ref.Val]ref.Val{ + String("k1"): String("v1"), + String("k2"): String("v2"), + }) + if got := calc.AggregateSize(m); got != math.MaxUint32 { + t.Errorf("AggregateSize for map traversal > 3 got %d, want MaxUint32", got) + } + }) + + t.Run("maxTraversal proto limit", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxTraversal(2)) + msg := &proto3pb.TestAllTypes{ + SingleString: "hello", + SingleInt64: 42, + } + if got := calc.AggregateSize(msg); got != math.MaxUint32 { + t.Errorf("AggregateSize for proto traversal > 2 got %d, want MaxUint32", got) + } + }) + + t.Run("maxTraversal native struct limit", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxTraversal(2)) + s := struct{ A, B, C int }{A: 1, B: 2, C: 3} + if got := calc.AggregateSize(s); got != math.MaxUint32 { + t.Errorf("AggregateSize for native struct traversal > 2 got %d, want MaxUint32", got) + } + }) + + t.Run("maxTraversal native map limit", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxTraversal(3)) + m := map[string]int{"a": 1, "b": 2} + if got := calc.AggregateSize(m); got != math.MaxUint32 { + t.Errorf("AggregateSize for native map traversal > 3 got %d, want MaxUint32", got) + } + }) + + t.Run("maxTraversal native slice limit", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxTraversal(3)) + slice := []string{"a", "b", "c"} + if got := calc.AggregateSize(slice); got != math.MaxUint32 { + t.Errorf("AggregateSize for native slice traversal > 3 got %d, want MaxUint32", got) + } + }) + + t.Run("zero depth limit saturation", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxDepth(0)) + if got := calc.AggregateSize(Int(42)); got != math.MaxUint32 { + t.Errorf("AggregateSize with depth 0 got %d, want MaxUint32", got) + } + }) + + t.Run("zero traversal limit saturation", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxTraversal(0)) + if got := calc.AggregateSize(Int(42)); got != math.MaxUint32 { + t.Errorf("AggregateSize with traversal 0 got %d, want MaxUint32", got) + } + }) +} + +type nestedNative struct { + NestedList []string + NestedMap map[string]int +} + +type rootNative struct { + Name string + Count int + Children []nestedNative +} + +func BenchmarkCalculateSizeAmortized(b *testing.B) { + adapter := DefaultTypeAdapter + + flatList := NewRefValList(adapter, []ref.Val{Int(1), Int(2), Int(3), Int(4), Int(5), Int(6), Int(7), Int(8)}) + nestedList := NewRefValList(adapter, []ref.Val{ + String("hello"), + NewRefValList(adapter, []ref.Val{String("nested1"), String("nested2")}), + NewRefValMap(adapter, map[ref.Val]ref.Val{String("k1"): String("v1")}), + }) + flatMap := NewRefValMap(adapter, map[ref.Val]ref.Val{ + String("k1"): Int(1), + String("k2"): Int(2), + String("k3"): Int(3), + }) + protoMsg := &proto3pb.TestAllTypes{ + SingleString: "hello world", + SingleInt64: 42, + RepeatedString: []string{"first", "second", "third"}, + MapStringString: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + } + nativeData := &rootNative{ + Name: "parent", + Count: 100, + Children: []nestedNative{ + {NestedList: []string{"a", "b", "c"}, NestedMap: map[string]int{"k1": 1, "k2": 2}}, + {NestedList: []string{"d", "e"}, NestedMap: map[string]int{"k3": 3}}, + }, + } + nativeVal := adapter.NativeToValue(nativeData) + + benchmarks := []struct { + name string + val any + }{ + {name: "scalar_int", val: Int(42)}, + {name: "scalar_string", val: String("hello world this is a test string")}, + {name: "native_string", val: "hello world this is a test string"}, + {name: "native_bytes", val: []byte("hello world this is a test string")}, + {name: "list_flat", val: flatList}, + {name: "list_nested", val: nestedList}, + {name: "map_flat", val: flatMap}, + {name: "custom_list_flat", val: proxyLegacyList{proxy: flatList}}, + {name: "custom_list_nested", val: proxyLegacyList{proxy: nestedList}}, + {name: "custom_map_flat", val: interopFoldableMap{Mapper: flatMap}}, + {name: "proto_message", val: protoMsg}, + {name: "proto_obj", val: adapter.NativeToValue(protoMsg)}, + {name: "native_obj", val: nativeVal}, + {name: "native_struct", val: nativeData}, + } + + for _, bm := range benchmarks { + b.Run(bm.name, func(b *testing.B) { + b.ReportAllocs() + calculator := NewSizeCalculator() + for i := 0; i < b.N; i++ { + _ = calculator.AggregateSize(bm.val) + } + }) + } +} + +func BenchmarkCalculateSizeDynamic(b *testing.B) { + adapter := DefaultTypeAdapter + + benchmarks := []struct { + name string + valFn func() any + }{ + { + name: "list_flat", + valFn: func() any { + return NewRefValList(adapter, []ref.Val{Int(1), Int(2), Int(3), Int(4), Int(5), Int(6), Int(7), Int(8)}) + }, + }, + { + name: "list_nested", + valFn: func() any { + return NewRefValList(adapter, []ref.Val{ + String("hello"), + NewRefValList(adapter, []ref.Val{String("nested1"), String("nested2")}), + NewRefValMap(adapter, map[ref.Val]ref.Val{String("k1"): String("v1")}), + }) + }, + }, + { + name: "map_flat", + valFn: func() any { + return NewRefValMap(adapter, map[ref.Val]ref.Val{ + String("k1"): Int(1), + String("k2"): Int(2), + String("k3"): Int(3), + }) + }, + }, + { + name: "custom_list_flat", + valFn: func() any { + return proxyLegacyList{proxy: NewRefValList(adapter, []ref.Val{Int(1), Int(2), Int(3), Int(4), Int(5), Int(6), Int(7), Int(8)})} + }, + }, + { + name: "custom_list_nested", + valFn: func() any { + return proxyLegacyList{proxy: NewRefValList(adapter, []ref.Val{ + String("hello"), + NewRefValList(adapter, []ref.Val{String("nested1"), String("nested2")}), + NewRefValMap(adapter, map[ref.Val]ref.Val{String("k1"): String("v1")}), + })} + }, + }, + { + name: "custom_map_flat", + valFn: func() any { + return interopFoldableMap{Mapper: NewRefValMap(adapter, map[ref.Val]ref.Val{ + String("k1"): Int(1), + String("k2"): Int(2), + String("k3"): Int(3), + })} + }, + }, + { + name: "proto_obj", + valFn: func() any { + return adapter.NativeToValue(&proto3pb.TestAllTypes{ + SingleString: "hello world", + SingleInt64: 42, + RepeatedString: []string{"first", "second", "third"}, + MapStringString: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + }) + }, + }, + { + name: "native_obj", + valFn: func() any { + return adapter.NativeToValue(&rootNative{ + Name: "parent", + Count: 100, + Children: []nestedNative{ + {NestedList: []string{"a", "b", "c"}, NestedMap: map[string]int{"k1": 1, "k2": 2}}, + {NestedList: []string{"d", "e"}, NestedMap: map[string]int{"k3": 3}}, + }, + }) + }, + }, + } + + for _, bm := range benchmarks { + b.Run(bm.name, func(b *testing.B) { + b.ReportAllocs() + calculator := NewSizeCalculator() + for i := 0; i < b.N; i++ { + val := bm.valFn() + _ = calculator.AggregateSize(val) + } + }) + } +} + +func BenchmarkCalculateSizeScaled(b *testing.B) { + adapter := DefaultTypeAdapter + sizes := []int{10, 100, 1000} + + for _, size := range sizes { + // Prepare list elements + listElems := make([]ref.Val, size) + for i := 0; i < size; i++ { + listElems[i] = Int(i) + } + builtinList := NewRefValList(adapter, listElems) + customList := proxyLegacyList{proxy: builtinList} + + // Prepare map entries + mapEntries := make(map[ref.Val]ref.Val, size) + for i := 0; i < size; i++ { + mapEntries[String(fmt.Sprintf("k%d", i))] = Int(i) + } + builtinMap := NewRefValMap(adapter, mapEntries) + customMap := interopFoldableMap{Mapper: builtinMap} + + // Amortized (repeated calculation on memoized vs unmemoized custom instance) + b.Run(fmt.Sprintf("Amortized/builtin_list/N=%d", size), func(b *testing.B) { + b.ReportAllocs() + calc := NewSizeCalculator() + for i := 0; i < b.N; i++ { + _ = calc.AggregateSize(builtinList) + } + }) + b.Run(fmt.Sprintf("Amortized/custom_list/N=%d", size), func(b *testing.B) { + b.ReportAllocs() + calc := NewSizeCalculator() + for i := 0; i < b.N; i++ { + _ = calc.AggregateSize(customList) + } + }) + b.Run(fmt.Sprintf("Amortized/builtin_map/N=%d", size), func(b *testing.B) { + b.ReportAllocs() + calc := NewSizeCalculator() + for i := 0; i < b.N; i++ { + _ = calc.AggregateSize(builtinMap) + } + }) + b.Run(fmt.Sprintf("Amortized/custom_map/N=%d", size), func(b *testing.B) { + b.ReportAllocs() + calc := NewSizeCalculator() + for i := 0; i < b.N; i++ { + _ = calc.AggregateSize(customMap) + } + }) + b.Run(fmt.Sprintf("Amortized/custom_pure_map/N=%d", size), func(b *testing.B) { + b.ReportAllocs() + calc := NewSizeCalculator() + pureMap := customPureMapper{Mapper: builtinMap} + for i := 0; i < b.N; i++ { + _ = calc.AggregateSize(pureMap) + } + }) + + // First-time / Uncached calculation + b.Run(fmt.Sprintf("FirstTime/builtin_list/N=%d", size), func(b *testing.B) { + b.ReportAllocs() + calc := NewSizeCalculator() + for i := 0; i < b.N; i++ { + l := NewRefValList(adapter, listElems) + _ = calc.AggregateSize(l) + } + }) + b.Run(fmt.Sprintf("FirstTime/custom_list/N=%d", size), func(b *testing.B) { + b.ReportAllocs() + calc := NewSizeCalculator() + for i := 0; i < b.N; i++ { + l := proxyLegacyList{proxy: NewRefValList(adapter, listElems)} + _ = calc.AggregateSize(l) + } + }) + b.Run(fmt.Sprintf("FirstTime/builtin_map/N=%d", size), func(b *testing.B) { + b.ReportAllocs() + calc := NewSizeCalculator() + for i := 0; i < b.N; i++ { + m := NewRefValMap(adapter, mapEntries) + _ = calc.AggregateSize(m) + } + }) + b.Run(fmt.Sprintf("FirstTime/custom_map/N=%d", size), func(b *testing.B) { + b.ReportAllocs() + calc := NewSizeCalculator() + for i := 0; i < b.N; i++ { + m := interopFoldableMap{Mapper: NewRefValMap(adapter, mapEntries)} + _ = calc.AggregateSize(m) + } + }) + b.Run(fmt.Sprintf("FirstTime/custom_pure_map/N=%d", size), func(b *testing.B) { + b.ReportAllocs() + calc := NewSizeCalculator() + for i := 0; i < b.N; i++ { + m := customPureMapper{Mapper: NewRefValMap(adapter, mapEntries)} + _ = calc.AggregateSize(m) + } + }) + } + + // Benchmark nested tree complexity (Depth x Width) + depths := []int{2, 3} + width := 10 + for _, depth := range depths { + builtinNested := createNestedList(adapter, depth, width) + customNested := createNestedCustomList(adapter, depth, width) + + b.Run(fmt.Sprintf("Complexity/builtin_nested_list/Depth=%d_Width=%d", depth, width), func(b *testing.B) { + b.ReportAllocs() + calc := NewSizeCalculator() + for i := 0; i < b.N; i++ { + _ = calc.AggregateSize(builtinNested) + } + }) + b.Run(fmt.Sprintf("Complexity/custom_nested_list/Depth=%d_Width=%d", depth, width), func(b *testing.B) { + b.ReportAllocs() + calc := NewSizeCalculator() + for i := 0; i < b.N; i++ { + _ = calc.AggregateSize(customNested) + } + }) + } +} + +func createNestedList(adapter Adapter, depth, width int) ref.Val { + if depth <= 1 { + elems := make([]ref.Val, width) + for i := 0; i < width; i++ { + elems[i] = Int(i) + } + return NewRefValList(adapter, elems) + } + elems := make([]ref.Val, width) + for i := 0; i < width; i++ { + elems[i] = createNestedList(adapter, depth-1, width) + } + return NewRefValList(adapter, elems) +} + +func createNestedCustomList(adapter Adapter, depth, width int) ref.Val { + if depth <= 1 { + elems := make([]ref.Val, width) + for i := 0; i < width; i++ { + elems[i] = Int(i) + } + return proxyLegacyList{proxy: NewRefValList(adapter, elems)} + } + elems := make([]ref.Val, width) + for i := 0; i < width; i++ { + elems[i] = createNestedCustomList(adapter, depth-1, width) + } + return proxyLegacyList{proxy: NewRefValList(adapter, elems)} +} diff --git a/common/types/util_test.go b/common/types/util_test.go index b10b3e84c..4d16495ee 100644 --- a/common/types/util_test.go +++ b/common/types/util_test.go @@ -14,7 +14,42 @@ package types -import "testing" +import ( + "math" + "testing" +) + +func TestSafeUint32Helpers(t *testing.T) { + // safeAddUint32 + if got := safeAddUint32(10, 20); got != 30 { + t.Errorf("safeAddUint32(10, 20) got %d, want 30", got) + } + if got := safeAddUint32(math.MaxUint32-5, 10); got != math.MaxUint32 { + t.Errorf("safeAddUint32(overflow) got %d, want MaxUint32", got) + } + + // safeUint32FromInt + if got := safeUint32FromInt(42); got != 42 { + t.Errorf("safeUint32FromInt(42) got %d, want 42", got) + } + if got := safeUint32FromInt(-1); got != math.MaxUint32 { + t.Errorf("safeUint32FromInt(-1) got %d, want MaxUint32", got) + } + if got := safeUint32FromInt(int(uint64(math.MaxUint32) + 100)); got != math.MaxUint32 { + t.Errorf("safeUint32FromInt(overflow) got %d, want MaxUint32", got) + } + + // safeUint32FromBoxedInt + if got := safeUint32FromBoxedInt(Int(42)); got != 42 { + t.Errorf("safeUint32FromBoxedInt(42) got %d, want 42", got) + } + if got := safeUint32FromBoxedInt(Int(-1)); got != math.MaxUint32 { + t.Errorf("safeUint32FromBoxedInt(-1) got %d, want MaxUint32", got) + } + if got := safeUint32FromBoxedInt(Int(int64(math.MaxUint32) + 100)); got != math.MaxUint32 { + t.Errorf("safeUint32FromBoxedInt(overflow) got %d, want MaxUint32", got) + } +} func BenchmarkIsUnknownOrError(b *testing.B) { err := NewErr("test") From 41262f37127a411e485d49cc94a7ffe691213450 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Mon, 17 Aug 2026 10:11:33 -0700 Subject: [PATCH 23/37] Fix native type panic during field traversal of nil-valued structs (#1417) --- common/types/native.go | 2 +- common/types/native_test.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/common/types/native.go b/common/types/native.go index 965089ddd..a784a8c54 100644 --- a/common/types/native.go +++ b/common/types/native.go @@ -371,7 +371,7 @@ func (o *nativeObj) AggregateSize(sizer AggregateSizer) uint32 { } total := uint32(1) for _, fieldType := range o.valType.fieldsByName { - fieldValue := refVal.FieldByIndex(fieldType.Index) + fieldValue := safeGetFieldByIndex(refVal, fieldType.Index) if !fieldValue.IsValid() || fieldValue.IsZero() { continue } diff --git a/common/types/native_test.go b/common/types/native_test.go index c18dfc8ea..51f5acaf8 100644 --- a/common/types/native_test.go +++ b/common/types/native_test.go @@ -1308,6 +1308,7 @@ func TestNativeObjectCalculateSize(t *testing.T) { ext.NativeTypes( reflect.TypeOf(TestAllTypes{}), reflect.TypeOf(TestNestedType{}), + reflect.TypeOf(TestEmbeddedPointerTypes{}), ), ) if err != nil { @@ -1325,6 +1326,11 @@ func TestNativeObjectCalculateSize(t *testing.T) { val: &TestNestedType{}, want: 1, // 1 (container) }, + { + name: "nil_embedded_pointer", + val: &TestEmbeddedPointerTypes{}, + want: 1, // 1 (container); promoted fields through the nil embedded pointer count as unset + }, { name: "struct_with_scalar_and_list", val: &TestNestedType{ From 76c0a6d4825573bd4e906f85258f5a180fb523a9 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Mon, 17 Aug 2026 10:51:40 -0700 Subject: [PATCH 24/37] Report every evaluation step to every observer (#1419) Cost tracking silently reported a cost of zero whenever state tracking was also enabled: cel.CostTracking(nil) -> 5 cel.CostTracking(nil) + cel.EvalOptions(OptTrackState) -> 0 cel.CostTracking(nil) + cel.EvalOptions(OptExhaustiveEval) -> 0 Each observer installed its own decorator, and decObserveEval returns a node which is already wrapped in a watcher untouched. Since the planner applies decorators in order, the state observer's decorator wrapped each node first and the cost observer's decorator then found a watcher and left it alone, so the cost observer's per-node callback was never installed. ObservableInterpretable still ran the tracker's InitState and GetState, so evaluation produced a CostTracker reporting a cost of zero rather than an error or a nil result -- including for programs configured with a cost limit, which then could not be exceeded. Observers now register only as observers, and the planner installs a single decorator which reports each observation to all of them. --- cel/cel_test.go | 58 ++++++++++++++++++++++++++++++++++++++ interpreter/interpreter.go | 1 - interpreter/planner.go | 19 +++++++++++++ interpreter/runtimecost.go | 1 - 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/cel/cel_test.go b/cel/cel_test.go index 7cb32afb0..eec096ddb 100644 --- a/cel/cel_test.go +++ b/cel/cel_test.go @@ -1683,6 +1683,64 @@ func TestVariadicLogicalOperators(t *testing.T) { } } +func TestCostTrackingWithStateTracking(t *testing.T) { + // Cost tracking and state tracking install separate observers. Every observer has to see + // every evaluation step, whichever combination of them is configured. + env := testEnv(t, Variable("a", StringType)) + ast, iss := env.Compile(`a.startsWith("x") && a.contains("yz")`) + if iss.Err() != nil { + t.Fatalf("env.Compile() failed: %v", iss.Err()) + } + baseline, _ := evalCostAndState(t, env, ast, CostTracking(nil)) + if baseline == 0 { + t.Fatalf("cost tracking alone reported a cost of 0") + } + tests := []struct { + name string + opts []ProgramOption + wantState bool + wantEqCost bool + }{ + {name: "cost", opts: []ProgramOption{CostTracking(nil)}, wantEqCost: true}, + {name: "cost and state", opts: []ProgramOption{CostTracking(nil), EvalOptions(OptTrackState)}, + wantState: true, wantEqCost: true}, + {name: "cost and exhaustive", opts: []ProgramOption{CostTracking(nil), EvalOptions(OptExhaustiveEval)}, + wantState: true, wantEqCost: true}, + } + for _, tst := range tests { + tc := tst + t.Run(tc.name, func(t *testing.T) { + cost, hasState := evalCostAndState(t, env, ast, tc.opts...) + if tc.wantEqCost && cost != baseline { + t.Errorf("actual cost got %d, wanted %d", cost, baseline) + } + if hasState != tc.wantState { + t.Errorf("state tracked got %t, wanted %t", hasState, tc.wantState) + } + }) + } +} + +// evalCostAndState evaluates the ast and reports the tracked cost along with whether evaluation +// state was recorded. +func evalCostAndState(t *testing.T, env *Env, ast *Ast, opts ...ProgramOption) (uint64, bool) { + t.Helper() + prg, err := env.Program(ast, opts...) + if err != nil { + t.Fatalf("env.Program() failed: %v", err) + } + _, det, err := prg.Eval(map[string]any{"a": "xyz-abcdefghij"}) + if err != nil { + t.Fatalf("prg.Eval() failed: %v", err) + } + cost := det.ActualCost() + if cost == nil { + t.Fatalf("det.ActualCost() returned nil") + } + state := det.State() + return *cost, state != nil && len(state.IDs()) != 0 +} + func TestParseError(t *testing.T) { env := testEnv(t) _, iss := env.Parse("invalid & logical_and") diff --git a/interpreter/interpreter.go b/interpreter/interpreter.go index 29df9d41e..8dcf8351f 100644 --- a/interpreter/interpreter.go +++ b/interpreter/interpreter.go @@ -101,7 +101,6 @@ func EvalStateObserver(opts ...evalStateOption) PlannerOption { return nil, errors.New("eval state factory not configured") } p.observers = append(p.observers, et) - p.decorators = append(p.decorators, decObserveEval(et.Observe)) return p, nil } } diff --git a/interpreter/planner.go b/interpreter/planner.go index 396a9803f..caaead80a 100644 --- a/interpreter/planner.go +++ b/interpreter/planner.go @@ -23,6 +23,7 @@ import ( "github.com/google/cel-go/common/functions" "github.com/google/cel-go/common/operators" "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // newPlanner creates an interpretablePlanner which references a Dispatcher, TypeProvider, @@ -73,6 +74,12 @@ type planBuilder struct { // such as state-tracking, expression re-write, and possibly efficient thread-safe memoization of // repeated expressions. func (p *planner) Plan(expr ast.Expr) (InterpretableV2, error) { + if len(p.observers) != 0 { + // A single decorator reports to every observer. One decorator per observer would not + // work, since the second decorator would find the node already wrapped by the first and + // leave it alone, silently dropping the second observer's observations. + p.decorators = append(p.decorators, decObserveEval(observeAll(p.observers))) + } pb := &planBuilder{planner: p, localVars: make(map[string]int)} i, err := pb.plan(expr) if err != nil { @@ -84,6 +91,18 @@ func (p *planner) Plan(expr ast.Expr) (InterpretableV2, error) { return &ObservableInterpretable{InterpretableV2: i, observers: p.observers}, nil } +// observeAll returns an EvalObserver which reports each observation to all of the observers. +func observeAll(observers []StatefulObserver) EvalObserver { + if len(observers) == 1 { + return observers[0].Observe + } + return func(vars Activation, id int64, programStep any, value ref.Val) { + for _, o := range observers { + o.Observe(vars, id, programStep, value) + } + } +} + func (p *planBuilder) plan(expr ast.Expr) (InterpretableV2, error) { switch expr.Kind() { case ast.CallKind: diff --git a/interpreter/runtimecost.go b/interpreter/runtimecost.go index 81e4ef63c..f9c525111 100644 --- a/interpreter/runtimecost.go +++ b/interpreter/runtimecost.go @@ -57,7 +57,6 @@ func CostObserver(opts ...costTrackPlanOption) PlannerOption { return nil, errors.New("cost tracker factory not configured") } p.observers = append(p.observers, ct) - p.decorators = append(p.decorators, decObserveEval(ct.Observe)) return p, nil } } From 4622a98087fcf80f1f152d04154970cf8d6160d0 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Mon, 17 Aug 2026 12:32:56 -0700 Subject: [PATCH 25/37] HMAC verify / compute library (#1416) --- ext/security/hmac/BUILD.bazel | 34 ++ ext/security/hmac/hmac.go | 359 +++++++++++++++++++ ext/security/hmac/hmac_test.go | 607 +++++++++++++++++++++++++++++++++ 3 files changed, 1000 insertions(+) create mode 100644 ext/security/hmac/BUILD.bazel create mode 100644 ext/security/hmac/hmac.go create mode 100644 ext/security/hmac/hmac_test.go diff --git a/ext/security/hmac/BUILD.bazel b/ext/security/hmac/BUILD.bazel new file mode 100644 index 000000000..51dbf314a --- /dev/null +++ b/ext/security/hmac/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +package( + default_visibility = ["//visibility:public"], + licenses = ["notice"], # Apache 2.0 +) + +go_library( + name = "go_default_library", + srcs = [ + "hmac.go", + ], + importpath = "github.com/google/cel-go/ext/security/hmac", + deps = [ + "//cel:go_default_library", + "//common/types:go_default_library", + "//common/types/ref:go_default_library", + ], +) + +go_test( + name = "go_default_test", + size = "small", + srcs = [ + "hmac_test.go", + ], + embed = [ + ":go_default_library", + ], + deps = [ + "//cel:go_default_library", + "//ext:go_default_library", + ], +) diff --git a/ext/security/hmac/hmac.go b/ext/security/hmac/hmac.go new file mode 100644 index 000000000..5b04f8ccf --- /dev/null +++ b/ext/security/hmac/hmac.go @@ -0,0 +1,359 @@ +// Copyright 2026 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 hmac implements CEL extension functions for Hash-based Message Authentication Code (HMAC) verification and computation. +package hmac + +import ( + "crypto" + "crypto/hmac" + _ "crypto/md5" + _ "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" + "encoding/base64" + "encoding/hex" + "fmt" + "strings" + + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" +) + +// Library returns a cel.EnvOption to configure extended functions for HMAC signature verification and computation. +func Library(options ...Option) cel.EnvOption { + l := &hmacLib{ + version: ^uint32(0), + customAlgorithms: make(map[string]crypto.Hash), + } + for _, o := range options { + l = o(l) + } + if len(l.customAlgorithms) == 0 { + l = CommonAlgorithms()(l) + } + return cel.Lib(l) +} + +// Option declares a functional operator for configuring HMAC extension library behavior. +type Option func(*hmacLib) *hmacLib + +// Version sets the library version for HMAC extensions. +func Version(version uint32) Option { + return func(l *hmacLib) *hmacLib { + l.version = version + return l + } +} + +// MaxPrefixLength sets the maximum signature prefix length to parse during verification. +// Defaults to 20. +func MaxPrefixLength(limit int) Option { + return func(l *hmacLib) *hmacLib { + l.maxPrefixLength = limit + return l + } +} + +// Algorithm registers a crypto.Hash algorithm with optional aliases +// (e.g. Algorithm(crypto.SHA256, "HS256")), +// exposing constant declarations (e.g., hmac.SHA256, hmac.HS256) in CEL and enabling it for HMAC operations. +func Algorithm(h crypto.Hash, aliases ...string) Option { + return func(l *hmacLib) *hmacLib { + if l.customAlgorithms == nil { + l.customAlgorithms = make(map[string]crypto.Hash) + } + name := h.String() + normName := normalizeAlgName(name) + l.customAlgorithms[normName] = h + l.customAlgorithms[name] = h + for _, alias := range aliases { + l.customAlgorithms[normalizeAlgName(alias)] = h + l.customAlgorithms[alias] = h + } + + if normName != "" { + l.addConstant("hmac."+normName, normName) + } + for _, alias := range aliases { + constAlias := normalizeAlgName(alias) + if constAlias != "" { + l.addConstant("hmac."+constAlias, normName) + } + } + + return l + } +} + +// CommonAlgorithms registers the most common HMAC hash algorithms (SHA256, SHA384, SHA512, SHA224, SHA512/256, SHA512/224) +// along with their JOSE/JWT aliases (HS256, HS384, HS512, HS224, HS512/256, HS512/224) using Algorithm options by proxy. +func CommonAlgorithms() Option { + return func(l *hmacLib) *hmacLib { + opts := []Option{ + Algorithm(crypto.SHA256, "HS256"), + Algorithm(crypto.SHA384, "HS384"), + Algorithm(crypto.SHA512, "HS512"), + Algorithm(crypto.SHA224, "HS224"), + Algorithm(crypto.SHA512_256, "HS512_256"), + Algorithm(crypto.SHA512_224, "HS512_224"), + } + for _, opt := range opts { + l = opt(l) + } + return l + } +} + +type celConstant struct { + name string + val string +} + +type hmacLib struct { + version uint32 + maxPrefixLength int + customAlgorithms map[string]crypto.Hash + constants []celConstant +} + +func (l *hmacLib) addConstant(name, val string) { + for _, c := range l.constants { + if c.name == name { + return + } + } + l.constants = append(l.constants, celConstant{name: name, val: val}) +} + +// LibraryName returns the CEL library identifier string. +func (*hmacLib) LibraryName() string { + return "cel.lib.ext.security.hmac" +} + +// CompileOptions returns environment options for declaring CEL functions and constants. +func (l *hmacLib) CompileOptions() []cel.EnvOption { + var opts []cel.EnvOption + + for _, c := range l.constants { + opts = append(opts, cel.Constant(c.name, cel.StringType, types.String(c.val))) + } + + opts = append(opts, + cel.Function("hmac.verify", + cel.Overload("hmac_verify_bytes_bytes_bytes_string", + []*cel.Type{cel.BytesType, cel.BytesType, cel.BytesType, cel.StringType}, + cel.BoolType, + cel.FunctionBinding(func(args ...ref.Val) ref.Val { + msg := args[0].(types.Bytes) + sig := args[1].(types.Bytes) + secret := args[2].(types.Bytes) + alg := args[3].(types.String) + return types.Bool(l.verifyBytes(msg, sig, secret, string(alg))) + }), + ), + cel.Overload("hmac_verify_string_string_string_string", + []*cel.Type{cel.StringType, cel.StringType, cel.StringType, cel.StringType}, + cel.BoolType, + cel.FunctionBinding(func(args ...ref.Val) ref.Val { + msg := args[0].(types.String) + sig := args[1].(types.String) + secret := args[2].(types.String) + alg := args[3].(types.String) + return types.Bool(l.verifyString(string(msg), string(sig), string(secret), string(alg))) + }), + ), + ), + + cel.Function("hmac.compute", + cel.Overload("hmac_compute_bytes_bytes_string", + []*cel.Type{cel.BytesType, cel.BytesType, cel.StringType}, + cel.BytesType, + cel.FunctionBinding(func(args ...ref.Val) ref.Val { + msg := args[0].(types.Bytes) + secret := args[1].(types.Bytes) + alg := args[2].(types.String) + mac, err := l.compute(msg, secret, string(alg)) + if err != nil { + return types.ValOrErr(args[0], "%v", err) + } + return types.Bytes(mac) + }), + ), + cel.Overload("hmac_compute_string_string_string", + []*cel.Type{cel.StringType, cel.StringType, cel.StringType}, + cel.BytesType, + cel.FunctionBinding(func(args ...ref.Val) ref.Val { + msg := args[0].(types.String) + secret := args[1].(types.String) + alg := args[2].(types.String) + mac, err := l.compute([]byte(string(msg)), []byte(string(secret)), string(alg)) + if err != nil { + return types.ValOrErr(args[0], "%v", err) + } + return types.Bytes(mac) + }), + ), + ), + ) + + return opts +} + +// ProgramOptions returns program options for HMAC extensions. +func (l *hmacLib) ProgramOptions() []cel.ProgramOption { + return nil +} + +func (l *hmacLib) compute(msg, secret []byte, alg string) ([]byte, error) { + hType, err := l.resolveHash(alg) + if err != nil { + return nil, err + } + return computeHMAC(msg, secret, hType) +} + +func (l *hmacLib) verifyBytes(msg, sig, secret []byte, alg string) bool { + hType, err := l.resolveHash(alg) + if err != nil { + return false + } + expectedMAC, err := computeHMAC(msg, secret, hType) + if err != nil { + return false + } + return hmac.Equal(expectedMAC, sig) +} + +func (l *hmacLib) verifyString(msgStr, sigStr, secretStr, alg string) bool { + sigStr = strings.TrimSpace(sigStr) + detectedAlg, cleanSig := l.parseSignaturePrefix(sigStr) + effectiveAlg := alg + if detectedAlg != "" { + effectiveAlg = detectedAlg + } + + hType, err := l.resolveHash(effectiveAlg) + if err != nil { + return false + } + + expectedMAC, err := computeHMAC([]byte(msgStr), []byte(secretStr), hType) + if err != nil { + return false + } + + // Try hex decoding + if hexBytes, err := hex.DecodeString(cleanSig); err == nil && len(hexBytes) == len(expectedMAC) { + if hmac.Equal(expectedMAC, hexBytes) { + return true + } + } + + // Try base64 standard decoding + if b64Bytes, err := decodeBase64StdSegment(cleanSig); err == nil && len(b64Bytes) == len(expectedMAC) { + if hmac.Equal(expectedMAC, b64Bytes) { + return true + } + } + + // Try base64 URL decoding + if b64URLBytes, err := decodeBase64URLSegment(cleanSig); err == nil && len(b64URLBytes) == len(expectedMAC) { + if hmac.Equal(expectedMAC, b64URLBytes) { + return true + } + } + + // Fallback raw string comparison + return hmac.Equal(expectedMAC, []byte(cleanSig)) +} + +func (l *hmacLib) parseSignaturePrefix(sig string) (string, string) { + sig = strings.TrimSpace(sig) + limit := l.maxPrefixLength + if limit <= 0 { + limit = 20 + } + if idx := strings.Index(sig, "="); idx > 0 && idx < limit { + prefix := strings.TrimSpace(sig[:idx]) + rest := strings.TrimSpace(sig[idx+1:]) + + normPrefix := normalizeAlgName(prefix) + if _, ok := l.customAlgorithms[normPrefix]; ok { + for name := range l.customAlgorithms { + if normalizeAlgName(name) == normPrefix { + return name, rest + } + } + } + if _, ok := l.customAlgorithms[prefix]; ok { + return prefix, rest + } + + if strings.EqualFold(prefix, "v1") || strings.EqualFold(prefix, "v0") { + return "", rest + } + } + return "", sig +} + +func normalizeAlgName(alg string) string { + s := strings.TrimSpace(alg) + s = strings.ReplaceAll(s, "-", "_") + s = strings.ReplaceAll(s, "/", "_") + s = strings.ToUpper(s) + if after, ok := strings.CutPrefix(s, "SHA_"); ok { + s = "SHA" + after + } + return s +} + +func (l *hmacLib) resolveHash(alg string) (crypto.Hash, error) { + norm := normalizeAlgName(alg) + for name, h := range l.customAlgorithms { + if strings.EqualFold(alg, name) || norm == normalizeAlgName(name) { + return h, nil + } + } + + return 0, fmt.Errorf("unsupported HMAC hash algorithm: %q", alg) +} + +func computeHMAC(msg, secret []byte, hType crypto.Hash) ([]byte, error) { + if !hType.Available() { + return nil, fmt.Errorf("hash algorithm %v is not available", hType) + } + mac := hmac.New(hType.New, secret) + mac.Write(msg) + return mac.Sum(nil), nil +} + +// decodeBase64URLSegment decodes a URL-safe base64 string with or without padding. +func decodeBase64URLSegment(seg string) ([]byte, error) { + seg = strings.TrimSpace(seg) + if data, err := base64.RawURLEncoding.DecodeString(seg); err == nil { + return data, nil + } + return base64.URLEncoding.DecodeString(seg) +} + +// decodeBase64StdSegment decodes a standard base64 string with or without padding. +func decodeBase64StdSegment(seg string) ([]byte, error) { + seg = strings.TrimSpace(seg) + if data, err := base64.RawStdEncoding.DecodeString(seg); err == nil { + return data, nil + } + return base64.StdEncoding.DecodeString(seg) +} diff --git a/ext/security/hmac/hmac_test.go b/ext/security/hmac/hmac_test.go new file mode 100644 index 000000000..6bfc488ba --- /dev/null +++ b/ext/security/hmac/hmac_test.go @@ -0,0 +1,607 @@ +// Copyright 2026 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 hmac_test + +import ( + "crypto" + "crypto/hmac" + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/base64" + "encoding/hex" + "reflect" + "testing" + + "github.com/google/cel-go/cel" + "github.com/google/cel-go/ext" + hmaclib "github.com/google/cel-go/ext/security/hmac" +) + +func evalExpr(t *testing.T, env *cel.Env, expr string, vars map[string]any) any { + ast, issues := env.Compile(expr) + if issues != nil && issues.Err() != nil { + t.Fatalf("Compile(%q) failed: %v", expr, issues.Err()) + } + prg, err := env.Program(ast) + if err != nil { + t.Fatalf("Program(%q) failed: %v", expr, err) + } + val, _, err := prg.Eval(vars) + if err != nil { + t.Fatalf("Eval(%q) failed: %v", expr, err) + } + return val.Value() +} + +func TestHMACUniformSignaturesAndConstants(t *testing.T) { + secretStr := "my-shared-secret-key" + secretBytes := []byte(secretStr) + msgStr := `{"action":"push","ref":"refs/heads/main"}` + msgBytes := []byte(msgStr) + + // Compute expected SHA256 MAC + h256 := hmac.New(sha256.New, secretBytes) + h256.Write(msgBytes) + mac256Bytes := h256.Sum(nil) + mac256Hex := hex.EncodeToString(mac256Bytes) + mac256B64 := base64.StdEncoding.EncodeToString(mac256Bytes) + mac256B64URL := base64.RawURLEncoding.EncodeToString(mac256Bytes) + + // Compute expected SHA512 MAC + h512 := hmac.New(sha512.New, secretBytes) + h512.Write(msgBytes) + mac512Bytes := h512.Sum(nil) + mac512Hex := hex.EncodeToString(mac512Bytes) + + env, err := cel.NewEnv( + hmaclib.Library(), + cel.Variable("msgStr", cel.StringType), + cel.Variable("msgBytes", cel.BytesType), + cel.Variable("secretStr", cel.StringType), + cel.Variable("secretBytes", cel.BytesType), + cel.Variable("sigHex", cel.StringType), + cel.Variable("sigB64", cel.StringType), + cel.Variable("sigB64URL", cel.StringType), + cel.Variable("sigBytes", cel.BytesType), + cel.Variable("sigGitHub", cel.StringType), + cel.Variable("sigStripe", cel.StringType), + cel.Variable("sig512Hex", cel.StringType), + cel.Variable("sig512Bytes", cel.BytesType), + ) + if err != nil { + t.Fatalf("cel.NewEnv failed: %v", err) + } + + vars := map[string]any{ + "msgStr": msgStr, + "msgBytes": msgBytes, + "secretStr": secretStr, + "secretBytes": secretBytes, + "sigHex": mac256Hex, + "sigB64": mac256B64, + "sigB64URL": mac256B64URL, + "sigBytes": mac256Bytes, + "sigGitHub": "sha256=" + mac256Hex, + "sigStripe": "v1=" + mac256Hex, + "sig512Hex": mac512Hex, + "sig512Bytes": mac512Bytes, + } + + tests := []struct { + name string + expr string + want any + }{ + // Uniform all-bytes verify + { + name: "verify_all_bytes_sha256", + expr: `hmac.verify(msgBytes, sigBytes, secretBytes, hmac.SHA256)`, + want: true, + }, + { + name: "verify_all_bytes_sha512", + expr: `hmac.verify(msgBytes, sig512Bytes, secretBytes, hmac.SHA512)`, + want: true, + }, + { + name: "verify_all_bytes_mismatch_sig", + expr: `hmac.verify(msgBytes, sig512Bytes, secretBytes, hmac.SHA256)`, + want: false, + }, + + // Uniform all-strings verify + { + name: "verify_all_strings_hex", + expr: `hmac.verify(msgStr, sigHex, secretStr, hmac.SHA256)`, + want: true, + }, + { + name: "verify_all_strings_b64", + expr: `hmac.verify(msgStr, sigB64, secretStr, hmac.SHA256)`, + want: true, + }, + { + name: "verify_all_strings_b64url", + expr: `hmac.verify(msgStr, sigB64URL, secretStr, hmac.SHA256)`, + want: true, + }, + { + name: "verify_all_strings_github_prefixed", + expr: `hmac.verify(msgStr, sigGitHub, secretStr, hmac.SHA256)`, + want: true, + }, + { + name: "verify_all_strings_stripe_prefixed", + expr: `hmac.verify(msgStr, sigStripe, secretStr, hmac.SHA256)`, + want: true, + }, + { + name: "verify_all_strings_sha512", + expr: `hmac.verify(msgStr, sig512Hex, secretStr, hmac.SHA512)`, + want: true, + }, + { + name: "verify_all_strings_string_literal_alg", + expr: `hmac.verify(msgStr, sigHex, secretStr, 'SHA256')`, + want: true, + }, + + // Uniform compute (returning bytes) + { + name: "compute_bytes_bytes_sha256", + expr: `hmac.compute(msgBytes, secretBytes, hmac.SHA256) == sigBytes`, + want: true, + }, + { + name: "compute_string_string_sha256", + expr: `hmac.compute(msgStr, secretStr, hmac.SHA256) == sigBytes`, + want: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := evalExpr(t, env, tc.expr, vars) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Eval(%q) = %v (%T), want %v (%T)", tc.expr, got, got, tc.want, tc.want) + } + }) + } +} + +func TestAlgorithmConstants(t *testing.T) { + env, err := cel.NewEnv( + hmaclib.Library(), + ) + if err != nil { + t.Fatalf("cel.NewEnv failed: %v", err) + } + + tests := []struct { + expr string + want string + }{ + {`hmac.SHA256`, "SHA256"}, + {`hmac.SHA384`, "SHA384"}, + {`hmac.SHA512`, "SHA512"}, + {`hmac.SHA224`, "SHA224"}, + {`hmac.SHA512_256`, "SHA512_256"}, + {`hmac.SHA512_224`, "SHA512_224"}, + } + + for _, tc := range tests { + t.Run(tc.expr, func(t *testing.T) { + got := evalExpr(t, env, tc.expr, nil) + if got != tc.want { + t.Errorf("Eval(%q) = %v, want %v", tc.expr, got, tc.want) + } + }) + } +} + +func TestHMACCompositionWithEncodersAndStrings(t *testing.T) { + env, err := cel.NewEnv( + hmaclib.Library(), + ext.Encoders(), + ext.Strings(), + cel.Variable("msg", cel.StringType), + cel.Variable("secret", cel.StringType), + ) + if err != nil { + t.Fatalf("cel.NewEnv failed: %v", err) + } + + vars := map[string]any{ + "msg": "hello world", + "secret": "key", + } + + resBytes := evalExpr(t, env, `hmac.compute(msg, secret, hmac.SHA256)`, vars).([]byte) + expectedHex := hex.EncodeToString(resBytes) + expectedB64 := base64.StdEncoding.EncodeToString(resBytes) + + tests := []struct { + name string + expr string + want any + }{ + { + name: "format_hex", + expr: `"%x".format([hmac.compute(msg, secret, hmac.SHA256)])`, + want: expectedHex, + }, + { + name: "base64_encode", + expr: `base64.encode(hmac.compute(msg, secret, hmac.SHA256))`, + want: expectedB64, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := evalExpr(t, env, tc.expr, vars) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Eval(%q) = %v (%T), want %v (%T)", tc.expr, got, got, tc.want, tc.want) + } + }) + } +} + +func TestHMACAllAlgorithmsAndPrefixes(t *testing.T) { + env, err := cel.NewEnv( + hmaclib.Library(hmaclib.Version(1)), + cel.Variable("msgStr", cel.StringType), + cel.Variable("secretStr", cel.StringType), + cel.Variable("sig", cel.StringType), + ) + if err != nil { + t.Fatalf("cel.NewEnv failed: %v", err) + } + + msg := "test-message" + secret := "secret-key" + vars := map[string]any{ + "msgStr": msg, + "secretStr": secret, + } + + algTests := []struct { + alg string + }{ + {"SHA256"}, + {"SHA384"}, + {"SHA512"}, + {"SHA224"}, + {"SHA512/256"}, + {"SHA512/224"}, + {"HS256"}, + {"HS384"}, + {"HS512"}, + {"HS224"}, + {"HS512/256"}, + {"HS512/224"}, + } + + for _, tc := range algTests { + t.Run("compute_"+tc.alg, func(t *testing.T) { + mac := evalExpr(t, env, `hmac.compute(msgStr, secretStr, '`+tc.alg+`')`, vars) + if len(mac.([]byte)) == 0 { + t.Errorf("empty mac for alg %q", tc.alg) + } + }) + } + + prefixTests := []struct { + prefix string + alg string + }{ + {"sha256=", "SHA256"}, + {"sha-256=", "SHA256"}, + {"hs256=", "SHA256"}, + {"sha384=", "SHA384"}, + {"sha-384=", "SHA384"}, + {"hs384=", "SHA384"}, + {"sha512=", "SHA512"}, + {"sha-512=", "SHA512"}, + {"hs512=", "SHA512"}, + {"v0=", "SHA256"}, + {"v1=", "SHA256"}, + } + + for _, tc := range prefixTests { + t.Run("prefix_"+tc.prefix, func(t *testing.T) { + macBytes := evalExpr(t, env, `hmac.compute(msgStr, secretStr, '`+tc.alg+`')`, vars).([]byte) + sigStr := tc.prefix + hex.EncodeToString(macBytes) + got := evalExpr(t, env, `hmac.verify(msgStr, '`+sigStr+`', secretStr, '`+tc.alg+`')`, vars) + if got != true { + t.Errorf("verify failed for prefix %q: got %v", tc.prefix, got) + } + }) + } + + rawSig := string(evalExpr(t, env, `hmac.compute(msgStr, secretStr, hmac.SHA256)`, vars).([]byte)) + rawVars := map[string]any{"msgStr": msg, "secretStr": secret, "sig": rawSig} + + invalidTests := []struct { + name string + expr string + vars map[string]any + want any + }{ + { + name: "verify_raw_string", + expr: `hmac.verify(msgStr, sig, secretStr, hmac.SHA256)`, + vars: rawVars, + want: true, + }, + { + name: "verify_unknown_alg_string", + expr: `hmac.verify(msgStr, 'sig', secretStr, 'UNKNOWN_ALG')`, + vars: vars, + want: false, + }, + { + name: "verify_unknown_alg_bytes", + expr: `hmac.verify(bytes(msgStr), bytes('sig'), bytes(secretStr), 'UNKNOWN_ALG')`, + vars: vars, + want: false, + }, + } + + for _, tc := range invalidTests { + t.Run(tc.name, func(t *testing.T) { + got := evalExpr(t, env, tc.expr, tc.vars) + if got != tc.want { + t.Errorf("Eval(%q) = %v, want %v", tc.expr, got, tc.want) + } + }) + } +} + +func TestHMACSpecificAlgorithmOptions(t *testing.T) { + env, err := cel.NewEnv( + hmaclib.Library(hmaclib.Algorithm(crypto.SHA256)), + cel.Variable("msgStr", cel.StringType), + cel.Variable("secretStr", cel.StringType), + ) + if err != nil { + t.Fatalf("cel.NewEnv failed: %v", err) + } + + vars := map[string]any{ + "msgStr": "msg", + "secretStr": "key", + } + + tests := []struct { + name string + expr string + mode string // "compile_error" or "eval_error" or "success" + }{ + { + name: "sha256_success", + expr: `hmac.compute(msgStr, secretStr, hmac.SHA256)`, + mode: "success", + }, + { + name: "unregistered_constant_sha512", + expr: `hmac.compute(msgStr, secretStr, hmac.SHA512)`, + mode: "compile_error", + }, + { + name: "unregistered_literal_sha512", + expr: `hmac.compute(msgStr, secretStr, 'SHA512')`, + mode: "eval_error", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ast, issues := env.Compile(tc.expr) + if tc.mode == "compile_error" { + if issues == nil || issues.Err() == nil { + t.Errorf("expected compile error for %q, got nil", tc.expr) + } + return + } + if issues != nil && issues.Err() != nil { + t.Fatalf("Compile(%q) failed unexpectedly: %v", tc.expr, issues.Err()) + } + prg, err := env.Program(ast) + if err != nil { + t.Fatalf("Program(%q) failed: %v", tc.expr, err) + } + _, _, evalErr := prg.Eval(vars) + if tc.mode == "eval_error" { + if evalErr == nil { + t.Errorf("expected eval error for %q, got nil", tc.expr) + } + } else if evalErr != nil { + t.Errorf("unexpected eval error for %q: %v", tc.expr, evalErr) + } + }) + } +} + +func TestHMACCustomAlgorithmOption(t *testing.T) { + env, err := cel.NewEnv( + hmaclib.Library( + hmaclib.Algorithm(crypto.MD5, "MD5", "HASH-MD5"), + hmaclib.Algorithm(crypto.SHA1, "SHA1"), + ), + cel.Variable("msg", cel.StringType), + cel.Variable("secret", cel.StringType), + ) + if err != nil { + t.Fatalf("cel.NewEnv failed: %v", err) + } + + msgStr := "hello custom alg" + secretStr := "key" + vars := map[string]any{ + "msg": msgStr, + "secret": secretStr, + } + + hMD5 := hmac.New(md5.New, []byte(secretStr)) + hMD5.Write([]byte(msgStr)) + macMD5Bytes := hMD5.Sum(nil) + macMD5Hex := hex.EncodeToString(macMD5Bytes) + + hSHA1 := hmac.New(sha1.New, []byte(secretStr)) + hSHA1.Write([]byte(msgStr)) + macSHA1Hex := hex.EncodeToString(hSHA1.Sum(nil)) + + tests := []struct { + name string + expr string + want any + }{ + { + name: "md5_constant", + expr: `hmac.MD5`, + want: "MD5", + }, + { + name: "sha1_constant", + expr: `hmac.SHA1`, + want: "SHA1", + }, + { + name: "compute_custom_md5", + expr: `hmac.compute(msg, secret, hmac.MD5)`, + want: macMD5Bytes, + }, + { + name: "compute_custom_md5_alias", + expr: `hmac.compute(msg, secret, 'HASH-MD5')`, + want: macMD5Bytes, + }, + { + name: "verify_custom_md5_prefixed", + expr: `hmac.verify(msg, 'md5=` + macMD5Hex + `', secret, hmac.MD5)`, + want: true, + }, + { + name: "verify_custom_sha1_prefixed", + expr: `hmac.verify(msg, 'sha1=` + macSHA1Hex + `', secret, hmac.SHA1)`, + want: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := evalExpr(t, env, tc.expr, vars) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Eval(%q) = %v (%T), want %v (%T)", tc.expr, got, got, tc.want, tc.want) + } + }) + } +} + +func TestHMACCommonAlgorithmsOption(t *testing.T) { + env, err := cel.NewEnv( + hmaclib.Library(hmaclib.CommonAlgorithms()), + cel.Variable("msgStr", cel.StringType), + cel.Variable("secretStr", cel.StringType), + ) + if err != nil { + t.Fatalf("cel.NewEnv failed: %v", err) + } + + vars := map[string]any{ + "msgStr": "msg", + "secretStr": "key", + } + + tests := []struct { + name string + expr string + }{ + { + name: "sha256", + expr: `hmac.compute(msgStr, secretStr, hmac.SHA256)`, + }, + { + name: "sha512", + expr: `hmac.compute(msgStr, secretStr, hmac.SHA512)`, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ast, issues := env.Compile(tc.expr) + if issues != nil && issues.Err() != nil { + t.Fatalf("Compile(%q) failed: %v", tc.expr, issues.Err()) + } + prg, err := env.Program(ast) + if err != nil { + t.Fatalf("Program(%q) failed: %v", tc.expr, err) + } + if _, _, err := prg.Eval(vars); err != nil { + t.Errorf("unexpected error for %s: %v", tc.name, err) + } + }) + } +} + +func TestHMACMaxPrefixLengthOption(t *testing.T) { + env, err := cel.NewEnv( + hmaclib.Library( + hmaclib.CommonAlgorithms(), + hmaclib.MaxPrefixLength(40), + hmaclib.Algorithm(crypto.SHA256, "very-long-prefix-custom-algorithm"), + ), + cel.Variable("msg", cel.StringType), + cel.Variable("secret", cel.StringType), + cel.Variable("sigStr", cel.StringType), + ) + if err != nil { + t.Fatalf("cel.NewEnv failed: %v", err) + } + + msg := "test" + secret := "key" + vars := map[string]any{ + "msg": msg, + "secret": secret, + } + mac := evalExpr(t, env, `hmac.compute(msg, secret, hmac.SHA256)`, vars).([]byte) + sigStr := "very-long-prefix-custom-algorithm=" + hex.EncodeToString(mac) + vars["sigStr"] = sigStr + + tests := []struct { + name string + expr string + want any + }{ + { + name: "verify_long_prefix", + expr: `hmac.verify(msg, sigStr, secret, hmac.SHA256)`, + want: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := evalExpr(t, env, tc.expr, vars) + if got != tc.want { + t.Errorf("Eval(%q) = %v, want %v", tc.expr, got, tc.want) + } + }) + } +} From c9ce5a25920d5d72632b57b8ff533f3fca5d3aee Mon Sep 17 00:00:00 2001 From: dplotnikov Date: Mon, 17 Aug 2026 13:27:34 -0700 Subject: [PATCH 26/37] Add parser benchmarks --- parser/bench/BUILD.bazel | 41 +++++++++ parser/bench/bench.go | 173 +++++++++++++++++++++++++++++++++++++ parser/bench/bench_test.go | 106 +++++++++++++++++++++++ 3 files changed, 320 insertions(+) create mode 100644 parser/bench/BUILD.bazel create mode 100644 parser/bench/bench.go create mode 100644 parser/bench/bench_test.go diff --git a/parser/bench/BUILD.bazel b/parser/bench/BUILD.bazel new file mode 100644 index 000000000..e97556fd6 --- /dev/null +++ b/parser/bench/BUILD.bazel @@ -0,0 +1,41 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +package( + licenses = ["notice"], # Apache 2.0 +) + +go_library( + name = "go_default_library", + srcs = [ + "bench.go", + ], + importpath = "github.com/google/cel-go/parser/bench", + visibility = ["//visibility:public"], + deps = [ + "//common:go_default_library", + "//common/ast:go_default_library", + "//common/operators:go_default_library", + "//common/types:go_default_library", + "//parser:go_default_library", + ], +) + +go_test( + name = "bench_test", + size = "small", + srcs = [ + "bench_test.go", + ], + embed = [ + ":go_default_library", + ], + deps = [ + "//common:go_default_library", + "//parser:go_default_library", + ], +) + +alias( + name = "go_default_test", + actual = ":bench_test", +) diff --git a/parser/bench/bench.go b/parser/bench/bench.go new file mode 100644 index 000000000..1d03fc961 --- /dev/null +++ b/parser/bench/bench.go @@ -0,0 +1,173 @@ +// Copyright 2026 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 bench defines benchmark test cases and utilities for CEL parsers. +package bench + +import ( + "strings" + + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/parser" +) + +// ParseResult indicates whether a parse is expected to succeed or fail. +type ParseResult int + +const ( + // ParseResultSuccess indicates an expression that is expected to parse without error. + ParseResultSuccess ParseResult = iota + // ParseResultError indicates an expression that is expected to produce parse error(s). + ParseResultError +) + +// TestCase represents an expression to parse and the expected parse result. +type TestCase struct { + Expr string + Result ParseResult +} + +// ErrorCase returns a TestCase expecting a parse error. +func ErrorCase(expr string) TestCase { + return TestCase{ + Expr: expr, + Result: ParseResultError, + } +} + +// SuccessCase returns a TestCase expecting successful parsing. +func SuccessCase(expr string) TestCase { + return TestCase{ + Expr: expr, + Result: ParseResultSuccess, + } +} + +// Category represents a named group of test cases for benchmarking and verification. +type Category struct { + Name string + Cases []TestCase +} + +// OptMapMacro expands `m.optMap(v, f)` into a conditional comprehension. +var OptMapMacro = parser.NewReceiverMacro("optMap", 2, optMapExpander) + +func optMapExpander(meh parser.ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) { + varIdent := args[0] + varName := "" + switch varIdent.Kind() { + case ast.IdentKind: + varName = varIdent.AsIdent() + default: + return nil, meh.NewError(varIdent.ID(), "optMap() variable name must be a simple identifier") + } + mapExpr := args[1] + return meh.NewCall( + operators.Conditional, + meh.NewMemberCall("hasValue", target), + meh.NewCall("optional.of", + meh.NewComprehension( + meh.NewList(), + "#unused", + varName, + meh.NewMemberCall("value", meh.Copy(target)), + meh.NewLiteral(types.False), + meh.NewIdent(varName), + mapExpr, + ), + ), + meh.NewCall("optional.none"), + ), nil +} + +// GetCategories returns benchmark and correctness test cases organized by category. +func GetCategories() []Category { + return categories +} + +// GetTestCases returns benchmark and correctness test cases flattened across all categories. +func GetTestCases() []TestCase { + var allCases []TestCase + for _, cat := range categories { + allCases = append(allCases, cat.Cases...) + } + return allCases +} + +var categories = []Category{ + // Simple: common, representative CEL expressions covering basic syntax, operators, calls, and literals + { + Name: "Simple", + Cases: []TestCase{ + SuccessCase("x * 2 + y / 3"), + SuccessCase(`foo.bar.baz(1, 2, "abc")`), + SuccessCase(`a > 5 && b < 10 || c == "xyz"`), + SuccessCase("x ? y : z"), + SuccessCase(`{"foo": 1, "bar": [2, 3]}`), + SuccessCase("a[b]"), + SuccessCase("a.b.c"), + SuccessCase("a.`b-c`"), + SuccessCase("\"\\a\\b\\f\\n\\r\\t\\v'\\\"\\\\ Legal escapes \\u2764\""), + }, + }, + + // Complex: expressions with deep chaining, nesting, precedence, and complex structures + { + Name: "Complex", + Cases: []TestCase{ + SuccessCase("a" + strings.Repeat(" + a", 49)), + SuccessCase("a" + strings.Repeat(" || a", 49)), + SuccessCase("a" + strings.Repeat(".f", 49)), + SuccessCase(strings.Repeat("(", 20) + "a" + strings.Repeat(")", 20)), + SuccessCase(`SomeMessage{foo: 5, bar: "xyz"}`), + SuccessCase("1 + 2 * 3 - 1 / 2 == 6 % 1"), + SuccessCase("[] + [1, 2, 3] + [4]"), + }, + }, + + // Macros: standard and receiver comprehension macros, optional syntax traversal + { + Name: "Macros", + Cases: []TestCase{ + SuccessCase("has(m.f)"), + SuccessCase("[1, 2, 3].all(x, x > 0)"), + SuccessCase("m.map(v, v * 2)"), + SuccessCase("m.filter(v, v > 0)"), + SuccessCase("m.exists_one(v, v == 1)"), + SuccessCase("x.filter(y, y.exists(z, has(z.a)))"), + SuccessCase("a.?b[?0] && a[?c]"), + SuccessCase("m.optMap(v, v + 1)"), + }, + }, + + // Errors: representative syntax errors, invalid tokens, keywords, and unclosed delimiters + { + Name: "Errors", + Cases: []TestCase{ + ErrorCase("x * 2 + y /"), + ErrorCase(`foo.bar.baz(1, 2, "abc"`), + ErrorCase("a > 5 && && b < 10"), + ErrorCase(`{"foo": 1, "bar": [2, 3`), + ErrorCase("1 + $"), + ErrorCase("break"), + ErrorCase(`"\xFh"`), + ErrorCase("a" + strings.Repeat(" + a", 49) + " +"), + ErrorCase(strings.Repeat("(", 20) + "a"), + ErrorCase("f(*" + strings.Repeat(", *", 9) + ")"), + }, + }, +} diff --git a/parser/bench/bench_test.go b/parser/bench/bench_test.go new file mode 100644 index 000000000..d016d0955 --- /dev/null +++ b/parser/bench/bench_test.go @@ -0,0 +1,106 @@ +// Copyright 2026 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 bench + +import ( + "fmt" + "testing" + + "github.com/google/cel-go/common" + "github.com/google/cel-go/parser" +) + +func newBenchmarkParser(tb testing.TB) *parser.Parser { + tb.Helper() + p, err := parser.NewParser( + parser.Macros(append(parser.AllMacros, OptMapMacro)...), + parser.EnableOptionalSyntax(true), + parser.EnableIdentEscapeSyntax(true), + parser.MaxRecursionDepth(512), + ) + if err != nil { + tb.Fatalf("parser.NewParser() failed: %v", err) + } + return p +} + +func TestExpectedResult(t *testing.T) { + p := newBenchmarkParser(t) + for _, cat := range GetCategories() { + t.Run(cat.Name, func(t *testing.T) { + for i, tc := range cat.Cases { + t.Run(fmt.Sprintf("%d_%s", i, tc.Expr), func(t *testing.T) { + src := common.NewTextSource(tc.Expr) + _, errs := p.Parse(src) + hasErr := len(errs.GetErrors()) > 0 + switch tc.Result { + case ParseResultSuccess: + if hasErr { + t.Errorf("p.Parse(%q) failed unexpectedly: %v", tc.Expr, errs.ToDisplayString()) + } + case ParseResultError: + if !hasErr { + t.Errorf("p.Parse(%q) succeeded unexpectedly, wanted error", tc.Expr) + } + } + }) + } + }) + } +} + +// BenchmarkParse benchmarks parsing organized by workload categories. +func BenchmarkParse(b *testing.B) { + p := newBenchmarkParser(b) + for _, cat := range GetCategories() { + b.Run(cat.Name, func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, tc := range cat.Cases { + src := common.NewTextSource(tc.Expr) + _, errs := p.Parse(src) + hasErr := len(errs.GetErrors()) > 0 + expectedErr := tc.Result == ParseResultError + if hasErr != expectedErr { + b.Fatalf("p.Parse(%q) got error: %v, expected error: %v", tc.Expr, hasErr, expectedErr) + } + } + } + }) + } +} + +// BenchmarkParseParallel benchmarks parsing concurrently across goroutines by category. +func BenchmarkParseParallel(b *testing.B) { + p := newBenchmarkParser(b) + for _, cat := range GetCategories() { + b.Run(cat.Name, func(b *testing.B) { + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + for _, tc := range cat.Cases { + src := common.NewTextSource(tc.Expr) + _, errs := p.Parse(src) + hasErr := len(errs.GetErrors()) > 0 + expectedErr := tc.Result == ParseResultError + if hasErr != expectedErr { + b.Fatalf("p.Parse(%q) got error: %v, expected error: %v", tc.Expr, hasErr, expectedErr) + } + } + } + }) + }) + } +} From f38faa2d9a3c1b0dad08d371466be4271391217e Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Mon, 17 Aug 2026 13:55:02 -0700 Subject: [PATCH 27/37] Scale sizes for strings and bytes (#1421) String and bytes values now count as one element per ten bytes, rounding up with a minimum of one element, configurable via SizeCalculatorStringUnitLength. Sizes are measured in bytes rather than characters so that sizing large values is O(1) rather than a full UTF-8 scan per observation; byte length is never smaller than the character count, so byte-based sizing is conservative for limit enforcement. --- common/types/list_test.go | 2 +- common/types/map_test.go | 2 +- common/types/native_test.go | 7 ++-- common/types/size_calc.go | 57 ++++++++++++++++++++------ common/types/size_calc_test.go | 75 ++++++++++++++++++++++++++++------ 5 files changed, 113 insertions(+), 30 deletions(-) diff --git a/common/types/list_test.go b/common/types/list_test.go index a962a3f1b..e77b39f65 100644 --- a/common/types/list_test.go +++ b/common/types/list_test.go @@ -969,7 +969,7 @@ func TestListCalculateSize(t *testing.T) { { name: "string_list", val: NewStringList(adapter, []string{"hello", "world"}), - want: 11, + want: 3, // 1 (container) + 1 ("hello" unit) + 1 ("world" unit) = 3 }, { name: "dynamic_list", diff --git a/common/types/map_test.go b/common/types/map_test.go index f085b7e67..3d0b8b08a 100644 --- a/common/types/map_test.go +++ b/common/types/map_test.go @@ -1312,7 +1312,7 @@ func TestMapCalculateSize(t *testing.T) { { name: "string_string_map", val: NewStringStringMap(adapter, map[string]string{"k1": "v1", "k2": "v2"}), - want: 9, + want: 5, // 1 (container) + 4 (single-unit keys and values) = 5 }, { name: "mutable_map_after_insert", diff --git a/common/types/native_test.go b/common/types/native_test.go index 51f5acaf8..5be655a89 100644 --- a/common/types/native_test.go +++ b/common/types/native_test.go @@ -1353,8 +1353,8 @@ func TestNativeObjectCalculateSize(t *testing.T) { NestedListVal: []string{"a", "b"}, }, }, - // 1 (root struct) + "hello"(5) + NestedVal(1 container + ["a", "b"](1+2=3) = 4) = 10 - want: 10, + // 1 (root struct) + "hello"(1 unit) + NestedVal(1 container + ["a", "b"](1+2=3) = 4) = 6 + want: 6, }, { name: "bytes_and_time", @@ -1363,7 +1363,8 @@ func TestNativeObjectCalculateSize(t *testing.T) { DurationVal: time.Second, TimestampVal: time.Unix(100, 0), }, - want: 7, + // 1 (root struct) + "test"(1 unit) + duration(1) + timestamp(1) = 4 + want: 4, }, { name: "slice_of_structs", diff --git a/common/types/size_calc.go b/common/types/size_calc.go index ab6cb4576..a5fdbae69 100644 --- a/common/types/size_calc.go +++ b/common/types/size_calc.go @@ -18,7 +18,6 @@ import ( "math" "reflect" "time" - "unicode/utf8" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" @@ -28,8 +27,9 @@ import ( ) const ( - defaultSizeCalculatorMaxDepth = 5 - defaultSizeCalculatorMaxTraversal = 10000 + defaultSizeCalculatorMaxDepth = 5 + defaultSizeCalculatorMaxTraversal = 10000 + defaultSizeCalculatorStringUnitLength = 10 ) // SizeCalculatorOption configures a SizeCalculator instance. @@ -49,19 +49,37 @@ func SizeCalculatorMaxTraversal(traversal int) SizeCalculatorOption { } } +// SizeCalculatorStringUnitLength sets the number of string or bytes value bytes which count as +// a single element (default 10). Values less than 1 are treated as 1, meaning each byte counts +// as a whole element. +// +// String sizes are measured in bytes rather than characters so that sizing large values is +// O(1) rather than a full UTF-8 scan per observation; byte length is never smaller than the +// character count, so byte-based sizing is conservative for limit enforcement. +func SizeCalculatorStringUnitLength(length int) SizeCalculatorOption { + return func(s *SizeCalculator) { + if length < 1 { + length = 1 + } + s.stringUnitLength = length + } +} + // SizeCalculator calculates the recursive element size of values. type SizeCalculator struct { - version int - maxDepth int - maxTraversal int + version int + maxDepth int + maxTraversal int + stringUnitLength int } // NewSizeCalculator returns a new SizeCalculator configured with optional SizeCalculatorOption settings. func NewSizeCalculator(opts ...SizeCalculatorOption) *SizeCalculator { s := &SizeCalculator{ - version: 0, - maxDepth: defaultSizeCalculatorMaxDepth, - maxTraversal: defaultSizeCalculatorMaxTraversal, + version: 0, + maxDepth: defaultSizeCalculatorMaxDepth, + maxTraversal: defaultSizeCalculatorMaxTraversal, + stringUnitLength: defaultSizeCalculatorStringUnitLength, } for _, opt := range opts { opt(s) @@ -105,6 +123,15 @@ func (s *SizeCalculator) AggregateSize(val any) uint32 { return ctx.AggregateSize(val) } +// stringSize converts a byte length to an element count where stringUnitLength bytes count +// as a single element, rounding up with a minimum size of 1. +func (s *SizeCalculator) stringSize(length int) uint32 { + if length <= 0 { + return 1 + } + return safeUint32FromInt((length + s.stringUnitLength - 1) / s.stringUnitLength) +} + // AggregateSize implements the ref.Val interface and allows for the generation of nested // child context values which are necessary for correct traversal count tracking. func (c sizeContext) AggregateSize(val any) uint32 { @@ -112,6 +139,10 @@ func (c sizeContext) AggregateSize(val any) uint32 { return math.MaxUint32 } switch v := val.(type) { + case String: + return c.calc.stringSize(len(v)) + case Bytes: + return c.calc.stringSize(len(v)) case AggregateSizeVisitor: return v.AggregateSize(c.childContext()) case traits.Foldable: @@ -161,9 +192,9 @@ func (c sizeContext) AggregateSize(val any) uint32 { case reflect.Value: return getReflectValueAggregateSize(c, v) case string: - return safeUint32FromInt(utf8.RuneCountInString(v)) + return c.calc.stringSize(len(v)) case []byte: - return safeUint32FromInt(len(v)) + return c.calc.stringSize(len(v)) case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, bool, time.Time, time.Duration, nil: @@ -245,11 +276,11 @@ func getReflectValueAggregateSize(c sizeContext, fieldVal reflect.Value) uint32 childCtx := c.childContext() switch fieldVal.Kind() { case reflect.String: - return safeUint32FromInt(utf8.RuneCountInString(fieldVal.String())) + return c.calc.stringSize(fieldVal.Len()) case reflect.Slice, reflect.Array: elemType := fieldVal.Type().Elem() if elemType.Kind() == reflect.Uint8 { - return safeUint32FromInt(fieldVal.Len()) + return c.calc.stringSize(fieldVal.Len()) } total := safeAddUint32(1, safeUint32FromInt(fieldVal.Len())) switch elemType.Kind() { diff --git a/common/types/size_calc_test.go b/common/types/size_calc_test.go index 43a4b064e..9a65dfc46 100644 --- a/common/types/size_calc_test.go +++ b/common/types/size_calc_test.go @@ -45,12 +45,12 @@ func TestCalculateSize(t *testing.T) { { name: "sizer_string", val: String("hello"), - want: 5, + want: 1, // 5 bytes round up to a single 10-byte element unit }, { name: "sizer_bytes", val: Bytes("world"), - want: 5, + want: 1, }, { name: "err_val", @@ -100,12 +100,12 @@ func TestCalculateSize(t *testing.T) { { name: "proto_value_string", val: protoreflect.ValueOfString("hello"), - want: 5, + want: 1, }, { name: "proto_value_bytes", val: protoreflect.ValueOfBytes([]byte("world")), - want: 5, + want: 1, }, { name: "proto_value_int", @@ -115,12 +115,12 @@ func TestCalculateSize(t *testing.T) { { name: "proto_map_key", val: protoreflect.MapKey(protoreflect.ValueOfString("key")), - want: 3, + want: 1, }, { name: "proto_message", val: &proto3pb.TestAllTypes{SingleString: "hello"}, - want: 6, // 1 (root) + 5 (string) = 6 + want: 2, // 1 (root) + 1 (string unit) = 2 }, { name: "proto_message_with_list_and_map", @@ -153,17 +153,17 @@ func TestCalculateSize(t *testing.T) { { name: "reflect_value", val: reflect.ValueOf("reflected"), - want: 9, + want: 1, }, { name: "native_string", val: "hello", - want: 5, + want: 1, }, { name: "native_bytes", val: []byte("world"), - want: 5, + want: 1, }, { name: "native_int", @@ -198,7 +198,7 @@ func TestCalculateSize(t *testing.T) { { name: "custom_struct", val: struct{ Name string }{"cel"}, - want: 4, // 1 (root) + 3 ("cel") = 4 + want: 2, // 1 (root) + 1 ("cel") = 2 }, { name: "custom_lister", @@ -208,12 +208,12 @@ func TestCalculateSize(t *testing.T) { { name: "custom_mapper", val: interopFoldableMap{Mapper: NewStringStringMap(DefaultTypeAdapter, map[string]string{"key": "val"})}, - want: 7, // 1 (container) + 3 ("key") + 3 ("val") = 7 + want: 3, // 1 (container) + 1 ("key") + 1 ("val") = 3 }, { name: "custom_pure_mapper", val: customPureMapper{Mapper: NewStringStringMap(DefaultTypeAdapter, map[string]string{"key": "val"})}, - want: 7, // 1 (container) + 3 ("key") + 3 ("val") = 7 + want: 3, // 1 (container) + 1 ("key") + 1 ("val") = 3 }, { name: "custom_sizer_struct_field", @@ -792,3 +792,54 @@ func createNestedCustomList(adapter Adapter, depth, width int) ref.Val { } return proxyLegacyList{proxy: NewRefValList(adapter, elems)} } + +func TestSizeCalculatorStringUnitLength(t *testing.T) { + tests := []struct { + name string + opts []SizeCalculatorOption + val any + want uint32 + }{ + {name: "empty_string_unit", val: String(""), want: 1}, + {name: "one_unit_exact", val: String("0123456789"), want: 1}, + {name: "one_unit_plus_one", val: String("0123456789a"), want: 2}, + {name: "three_units", val: String("0123456789012345678901"), want: 3}, + {name: "bytes_two_units", val: Bytes("01234567890"), want: 2}, + {name: "native_string_two_units", val: "01234567890", want: 2}, + {name: "native_bytes_two_units", val: []byte("01234567890"), want: 2}, + {name: "reflect_string_two_units", val: reflect.ValueOf("01234567890"), want: 2}, + { + name: "unit_length_one", + opts: []SizeCalculatorOption{SizeCalculatorStringUnitLength(1)}, + val: String("hello"), + want: 5, + }, + { + name: "unit_length_below_one_clamped", + opts: []SizeCalculatorOption{SizeCalculatorStringUnitLength(0)}, + val: String("hello"), + want: 5, + }, + { + name: "unit_length_large", + opts: []SizeCalculatorOption{SizeCalculatorStringUnitLength(100)}, + val: String("hello world, hello world, hello world"), + want: 1, + }, + { + // Sizes are measured in bytes, not characters: four 3-byte CJK characters + // occupy 12 bytes and count as two 10-byte units. + name: "multibyte_counted_in_bytes", + val: String("日本語字"), + want: 2, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + calc := NewSizeCalculator(tc.opts...) + if got := calc.AggregateSize(tc.val); got != tc.want { + t.Errorf("AggregateSize(%v) got %d, want %d", tc.val, got, tc.want) + } + }) + } +} From 9f82d0778528cd729bfb0906ff6b16ed0a5a48c5 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Mon, 17 Aug 2026 13:55:24 -0700 Subject: [PATCH 28/37] Aggregate semantics in Policy Compiler (#1408) * Aggregate policy evaluation semantics support * Additional aggregation tests showcasing multiple aggregation rules with nested match behavior * Replace 'emit' with 'output' to conform to spec update * Updates to the composition base step and aggregation --- common/env/env_test.go | 2 +- policy/BUILD.bazel | 3 +- policy/compiler.go | 33 +- policy/compiler_test.go | 325 +++++++++++++++--- policy/composer.go | 109 ++++-- policy/composer_test.go | 264 ++++++++++---- policy/config_test.go | 6 +- policy/helper_test.go | 76 +++- policy/parser.go | 74 +++- policy/parser_test.go | 70 ++++ .../config.yaml | 42 +++ .../policy.yaml | 44 +++ .../tests.yaml | 72 ++++ policy/testdata/aggregate_errors/config.yaml | 15 + policy/testdata/aggregate_errors/policy.yaml | 24 ++ .../aggregate_list_errors/config.yaml | 15 + .../aggregate_list_errors/policy.yaml | 24 ++ .../config.yaml | 15 + .../policy.yaml | 25 ++ 19 files changed, 1078 insertions(+), 160 deletions(-) create mode 100644 policy/testdata/agent_tool_execution_governance/config.yaml create mode 100644 policy/testdata/agent_tool_execution_governance/policy.yaml create mode 100644 policy/testdata/agent_tool_execution_governance/tests.yaml create mode 100644 policy/testdata/aggregate_errors/config.yaml create mode 100644 policy/testdata/aggregate_errors/policy.yaml create mode 100644 policy/testdata/aggregate_list_errors/config.yaml create mode 100644 policy/testdata/aggregate_list_errors/policy.yaml create mode 100644 policy/testdata/aggregate_nested_mixed_semantics/config.yaml create mode 100644 policy/testdata/aggregate_nested_mixed_semantics/policy.yaml diff --git a/common/env/env_test.go b/common/env/env_test.go index 866807ef1..b2c0047aa 100644 --- a/common/env/env_test.go +++ b/common/env/env_test.go @@ -1534,7 +1534,7 @@ func unmarshalYAML(t *testing.T, data []byte) *Config { t.Helper() config, err := ConfigFromYAML(data) if err != nil { - t.Fatalf("ConfigFromYaml(%q) failed: %v", string(data), err) + t.Fatalf("ConfigFromYAML(%q) failed: %v", string(data), err) } return config } diff --git a/policy/BUILD.bazel b/policy/BUILD.bazel index d443c6d7c..60871012c 100644 --- a/policy/BUILD.bazel +++ b/policy/BUILD.bazel @@ -72,8 +72,9 @@ go_test( "//test:go_default_library", "//common/debug:go_default_library", "//common/types:go_default_library", - "//interpreter:go_default_library", "//common/types/ref:go_default_library", + "//common/types/traits:go_default_library", + "//interpreter:go_default_library", "//test/proto3pb:go_default_library", "@in_yaml_go_yaml_v3//:go_default_library", "@com_github_google_go_cmp//cmp:go_default_library", diff --git a/policy/compiler.go b/policy/compiler.go index 2ca84e0a2..d2a4212c1 100644 --- a/policy/compiler.go +++ b/policy/compiler.go @@ -34,6 +34,7 @@ type CompiledRule struct { id *ValueString variables []*CompiledVariable matches []*CompiledMatch + semantic SemanticType } // SourceID returns the source metadata identifier associated with the compiled rule. @@ -56,11 +57,21 @@ func (r *CompiledRule) Matches() []*CompiledMatch { return r.matches[:] } +// Semantic returns the evaluation semantic for the compiled rule. +func (r *CompiledRule) Semantic() SemanticType { + return r.semantic +} + // OutputType returns the output type of the first match clause as all match clauses // are validated for agreement prior to construction fo the CompiledRule. func (r *CompiledRule) OutputType() *cel.Type { // It's a compilation error if the output types of the matches don't agree - for _, m := range r.Matches() { + matches := r.Matches() + if len(matches) > 0 { + m := matches[0] + if r.semantic == aggregate { + return cel.ListType(m.OutputType()) + } return m.OutputType() } return cel.DynType @@ -69,6 +80,9 @@ func (r *CompiledRule) OutputType() *cel.Type { // HasOptionalOutput returns whether the rule returns a concrete or optional value. // The rule may return an optional value if all match expressions under the rule are conditional. func (r *CompiledRule) HasOptionalOutput() bool { + if r.semantic == aggregate { + return false + } optionalOutput := false for _, m := range r.Matches() { if m.NestedRule() != nil && m.NestedRule().HasOptionalOutput() { @@ -297,7 +311,7 @@ func CompileRule(env *cel.Env, p *Policy, opts ...CompilerOption) (*CompiledRule c.env = env } } - return c.compileRule(p.Rule(), p, c.env, iss) + return c.compileRule(p.Rule(), p, c.env, iss, false) } type compiler struct { @@ -310,7 +324,10 @@ type compiler struct { nestedCount int } -func (c *compiler) compileRule(r *Rule, p *Policy, ruleEnv *cel.Env, iss *cel.Issues) (*CompiledRule, *cel.Issues) { +func (c *compiler) compileRule(r *Rule, p *Policy, ruleEnv *cel.Env, iss *cel.Issues, hasAggregateAncestor bool) (*CompiledRule, *cel.Issues) { + if hasAggregateAncestor && r.semantic == aggregate { + iss.ReportErrorAtID(r.SourceID(), "nested aggregate rules are not allowed") + } compiledVars := make([]*CompiledVariable, len(r.Variables())) for i, v := range r.Variables() { exprSrc := c.relSource(v.Expression()) @@ -379,7 +396,8 @@ func (c *compiler) compileRule(r *Rule, p *Policy, ruleEnv *cel.Env, iss *cel.Is continue } if m.HasRule() { - nestedRule, ruleIss := c.compileRule(m.Rule(), p, ruleEnv, iss) + nextHasAggregateAncestor := hasAggregateAncestor || r.semantic == aggregate + nestedRule, ruleIss := c.compileRule(m.Rule(), p, ruleEnv, iss, nextHasAggregateAncestor) iss = iss.Append(ruleIss) compiledMatches = append(compiledMatches, &CompiledMatch{ exprID: m.exprID, @@ -401,6 +419,7 @@ func (c *compiler) compileRule(r *Rule, p *Policy, ruleEnv *cel.Env, iss *cel.Is id: r.id, variables: compiledVars, matches: compiledMatches, + semantic: r.semantic, } // Note: Consider supporting configurable policy validators that take the policy, rule, and issues @@ -453,10 +472,14 @@ func (c *compiler) checkUnreachableCode(rule *CompiledRule, iss *cel.Issues) { m := compiledMatches[i] triviallyTrue := m.ConditionIsLiteral(types.True) + if m.ConditionIsLiteral(types.False) { + iss.ReportErrorAtID(m.SourceID(), "Condition is always false") + } + // If the match is a single output or a nested rule that always returns a value, it is // exhaustive. If the condition is trivially true, then all subsequent branches are unreachable. isExhaustive := triviallyTrue && (m.NestedRule() == nil || !m.NestedRule().HasOptionalOutput()) - if isExhaustive && i != matchCount-1 { + if rule.semantic == firstMatch && isExhaustive && i != matchCount-1 { if m.Output() != nil { iss.ReportErrorAtID(m.SourceID(), "match creates unreachable outputs") } diff --git a/policy/compiler_test.go b/policy/compiler_test.go index 6f9bc4f3b..b44c841f9 100644 --- a/policy/compiler_test.go +++ b/policy/compiler_test.go @@ -46,52 +46,6 @@ func TestCompile(t *testing.T) { } } -func TestRuleComposerError(t *testing.T) { - env, err := cel.NewEnv() - if err != nil { - t.Fatalf("NewEnv() failed: %v", err) - } - _, err = NewRuleComposer(env, ExpressionUnnestHeight(-1)) - if err == nil || !strings.Contains(err.Error(), "invalid unnest") { - t.Errorf("NewRuleComposer() got %v, wanted 'invalid unnest'", err) - } -} - -func TestRuleComposerUnnest(t *testing.T) { - for _, tst := range composerUnnestTests { - tc := tst - t.Run(tc.name, func(t *testing.T) { - r := newRunner(tc.name, tc.expr, []ParserOption{}) - env, rule, iss := r.compileRule(t) - if iss.Err() != nil { - t.Fatalf("CompileRule() failed: %v", iss.Err()) - } - rc, err := NewRuleComposer(env, tc.composerOpts...) - if err != nil { - t.Fatalf("NewRuleComposer() failed: %v", err) - } - ast, iss := rc.Compose(rule) - if iss.Err() != nil { - t.Fatalf("Compose(rule) failed: %v", iss.Err()) - } - policy := parsePolicy(t, tc.name, []ParserOption{}) - verifySourceInfoCoverage(t, policy, ast) - unparsed, err := cel.AstToString(ast) - if err != nil { - t.Fatalf("cel.AstToString() failed: %v", err) - } - if normalize(unparsed) != normalize(tc.composed) { - t.Errorf("cel.AstToString() got %s, wanted %s", unparsed, tc.composed) - } - if !ast.OutputType().IsEquivalentType(tc.outputType) { - t.Errorf("ast.OutputType() got %v, wanted %v", ast.OutputType(), tc.outputType) - } - r.setup(t, env, ast) - r.run(t) - }) - } -} - func TestCompileError(t *testing.T) { for _, tst := range policyErrorTests { policy := parsePolicy(t, tst.name, []ParserOption{}) @@ -290,16 +244,48 @@ func BenchmarkCompile(b *testing.B) { } } -func newRunner(name, expr string, parseOpts []ParserOption, opts ...cel.EnvOption) *runner { +func parsePolicySource(t testing.TB, name string, policySource string, parseOpts ...ParserOption) *Policy { + t.Helper() + p := StringSource(policySource, name) + parser, err := NewParser(parseOpts...) + if err != nil { + t.Fatalf("NewParser() failed: %v", err) + } + policy, iss := parser.Parse(p) + if iss.Err() != nil { + t.Fatalf("parser.Parse() failed: %v", iss.Err()) + } + return policy +} + +func parseAndCompilePolicy(t testing.TB, name string, policySource string, envOpts []cel.EnvOption, compilerOpts []CompilerOption) (*cel.Env, *cel.Ast, *cel.Issues) { + t.Helper() + policy := parsePolicySource(t, name, policySource) + envOpts = append([]cel.EnvOption{ + cel.OptionalTypes(), + cel.EnableMacroCallTracking(), + ext.Bindings(), + }, envOpts...) + env, err := cel.NewEnv(envOpts...) + if err != nil { + t.Fatalf("cel.NewEnv() failed: %v", err) + } + ast, iss := Compile(env, policy, compilerOpts...) + return env, ast, iss +} + +func newRunner(name, expr string, parseOpts []ParserOption, envOpts ...cel.EnvOption) *runner { return &runner{ name: name, parseOpts: parseOpts, + envOpts: envOpts, expr: expr} } type runner struct { name string parseOpts []ParserOption + envOpts []cel.EnvOption env *cel.Env expr string prg cel.Program @@ -322,6 +308,12 @@ func (r *runner) compileRule(t testing.TB) (*cel.Env, *CompiledRule, *cel.Issues if err != nil { t.Fatalf("cel.NewEnv() failed: %v", err) } + if len(r.envOpts) > 0 { + env, err = env.Extend(r.envOpts...) + if err != nil { + t.Fatalf("env.Extend() with env options failed: %v", err) + } + } // Configure declarations env, err = env.Extend(FromConfig(config)) if err != nil { @@ -605,7 +597,9 @@ func exprLinesFromPolicy(policy *Policy) map[int]bool { addExpectedLines(v.Expression()) } for _, m := range r.Matches() { - addExpectedLines(m.Condition()) + if !strings.HasPrefix(m.Condition().Value, "true") { + addExpectedLines(m.Condition()) + } if m.HasOutput() { addExpectedLines(m.Output()) } @@ -617,3 +611,238 @@ func exprLinesFromPolicy(policy *Policy) map[int]bool { traverseRule(policy.Rule()) return lines } + +func TestCompileYAMLPolicy_Aggregate(t *testing.T) { + type testEval struct { + input map[string]any + output ref.Val + } + tests := []struct { + name string + policy string + envOpts []cel.EnvOption + expectedUnparsed string + evals []testEval + wantErr string + }{ + { + name: "eval_aggregate", + policy: `name: "aggregate_policy" +rule: + aggregate: + - condition: 'true' + output: '"PII"' + - condition: 'true' + output: '"CONFIDENTIAL"'`, + expectedUnparsed: `["PII"] + ["CONFIDENTIAL"]`, + evals: []testEval{ + { + input: map[string]any{}, + output: types.NewStringList(types.DefaultTypeAdapter, []string{"PII", "CONFIDENTIAL"}), + }, + }, + }, + { + name: "aggregate_with_block_variables", + policy: `name: "block_policy" +rule: + variables: + - name: val1 + expression: '"PII"' + - name: val2 + expression: '"CONFIDENTIAL"' + aggregate: + - condition: 'true' + output: 'variables.val1' + - condition: 'true' + output: 'variables.val2'`, + expectedUnparsed: `cel.@block(["PII", "CONFIDENTIAL"], [@index0] + [@index1])`, + evals: []testEval{ + { + input: map[string]any{}, + output: types.NewStringList(types.DefaultTypeAdapter, []string{"PII", "CONFIDENTIAL"}), + }, + }, + }, + { + name: "aggregate_conditions_and_block_variables", + policy: `name: "cse_policy" +rule: + variables: + - name: threshold + expression: "5" + aggregate: + - condition: "size(resource.payload) > variables.threshold" + output: '"CSE1"' + - condition: "size(resource.payload) > variables.threshold" + output: '"CSE2"' + - condition: 'true' + output: '"ALWAYS"'`, + envOpts: []cel.EnvOption{ + cel.Variable("resource", cel.MapType(cel.StringType, cel.ListType(cel.IntType))), + }, + expectedUnparsed: `cel.@block([5], ((size(resource.payload) > @index0) ? ["CSE1"] : []) + (((size(resource.payload) > @index0) ? ["CSE2"] : []) + ["ALWAYS"]))`, + evals: []testEval{ + { + input: map[string]any{ + "resource": map[string]any{ + "payload": []int64{1, 2, 3, 4, 5, 6}, + }, + }, + output: types.NewStringList(types.DefaultTypeAdapter, []string{"CSE1", "CSE2", "ALWAYS"}), + }, + { + input: map[string]any{ + "resource": map[string]any{ + "payload": []int64{1, 2, 3}, + }, + }, + output: types.NewStringList(types.DefaultTypeAdapter, []string{"ALWAYS"}), + }, + }, + }, + { + name: "aggregate_macros_preserved", + policy: `name: aggregate_macros_preserved +rule: + variables: + - name: min_val + expression: "10" + aggregate: + - condition: "cond" + rule: + match: + - condition: "true" + output: "payload.filter(x, x > variables.min_val).exists(y, y % 2 == 0)" + - condition: "true" + output: "payload.all(x, x > 0)"`, + envOpts: []cel.EnvOption{ + cel.Variable("cond", cel.BoolType), + cel.Variable("payload", cel.ListType(cel.IntType)), + }, + expectedUnparsed: `cel.@block([10], (cond ? [payload.filter(x, x > @index0).exists(y, y % 2 == 0)] : []) + [payload.all(x, x > 0)])`, + }, + { + name: "nested_aggregate_throws", + policy: `name: nested_aggregate +rule: + aggregate: + - condition: 'true' + rule: + aggregate: + - condition: 'true' + output: "'foo'"`, + wantErr: "nested aggregate rules are not allowed", + }, + { + name: "nested_aggregate_with_match_throws", + policy: `name: nested_aggregate_with_match +rule: + aggregate: + - condition: 'true' + rule: + match: + - condition: 'true' + rule: + aggregate: + - condition: 'true' + output: "'foo'"`, + wantErr: "nested aggregate rules are not allowed", + }, + { + name: "aggregate_under_match_success", + policy: `name: aggregate_under_match +rule: + match: + - condition: 'true' + rule: + aggregate: + - condition: 'true' + output: "'foo'"`, + expectedUnparsed: `["foo"]`, + }, + } + + for _, tst := range tests { + tc := tst + t.Run(tc.name, func(t *testing.T) { + env, ast, iss := parseAndCompilePolicy(t, tc.name, tc.policy, tc.envOpts, nil) + if tc.wantErr != "" { + if iss.Err() == nil { + t.Fatalf("Compile() succeeded, wanted error %q", tc.wantErr) + } + if !strings.Contains(iss.Err().Error(), tc.wantErr) { + t.Errorf("Compile() got %v, wanted error containing %q", iss.Err(), tc.wantErr) + } + return + } + + if iss.Err() != nil { + t.Fatalf("Compile() failed: %v", iss.Err()) + } + + unparsed, err := cel.AstToString(ast) + if err != nil { + t.Fatalf("cel.AstToString() failed: %v", err) + } + if tc.expectedUnparsed != "" && normalize(unparsed) != normalize(tc.expectedUnparsed) { + t.Errorf("cel.AstToString() got %s, wanted %s", unparsed, tc.expectedUnparsed) + } + + _, err = cel.AstToCheckedExpr(ast) + if err != nil { + t.Fatalf("cel.AstToCheckedExpr() failed: %v", err) + } + + prg, err := env.Program(ast) + if err != nil { + t.Fatalf("env.Program(ast) failed: %v", err) + } + + for _, ev := range tc.evals { + out, _, err := prg.Eval(ev.input) + if err != nil { + t.Fatalf("prg.Eval(%v) failed: %v", ev.input, err) + } + if out.Equal(ev.output) != types.True { + t.Errorf("prg.Eval(%v) got %v, wanted %v", ev.input, out, ev.output) + } + } + }) + } +} + +func TestCompiledRuleSemantic(t *testing.T) { + policySource := `name: aggregate_semantic +rule: + aggregate: + - condition: 'true' + output: "'foo'"` + policy := parsePolicySource(t, "aggregate_semantic", policySource) + env, err := cel.NewEnv() + if err != nil { + t.Fatalf("cel.NewEnv() failed: %v", err) + } + compiledRule, iss := CompileRule(env, policy) + if iss.Err() != nil { + t.Fatalf("CompileRule() failed: %v", iss.Err()) + } + if compiledRule.Semantic() != aggregate { + t.Errorf("got %v, wanted aggregate", compiledRule.Semantic()) + } +} + +func TestCompileYAMLPolicy_ConditionAlwaysFalse(t *testing.T) { + policySource := `name: condition_always_false +rule: + aggregate: + - condition: 'false' + output: "'foo'"` + _, _, iss := parseAndCompilePolicy(t, "condition_always_false", policySource, nil, nil) + if iss.Err() == nil { + t.Fatalf("Compile() succeeded, wanted error") + } + if !strings.Contains(iss.Err().Error(), "Condition is always false") { + t.Errorf("Compile() got %v, wanted 'Condition is always false'", iss.Err()) + } +} diff --git a/policy/composer.go b/policy/composer.go index ef392184f..7f1e78f68 100644 --- a/policy/composer.go +++ b/policy/composer.go @@ -92,7 +92,7 @@ func (c *RuleComposer) Compose(r *CompiledRule) (*cel.Ast, *cel.Issues) { return nil, iss } unnester := &ruleUnnesterImpl{ - nextVarIndex: len(composer.varIndices), + nextVarIndex: len(composer.varIndices), varIndices: composer.varIndices, exprUnnestHeight: c.exprUnnestHeight, } @@ -159,7 +159,7 @@ func (opt *ruleComposerImpl) exitScope() { func (opt *ruleComposerImpl) Optimize(ctx *cel.OptimizerContext, a *ast.AST) *ast.AST { // The input to optimize is a dummy expression which is completely replaced according // to the configuration of the rule composition graph. - ruleExpr := opt.optimizeRule(ctx, opt.rule) + ruleExpr := opt.optimizeRule(ctx, opt.rule, false) // If there were no variables, return the expression. if len(opt.varIndices) == 0 { @@ -180,7 +180,7 @@ func (opt *ruleComposerImpl) Optimize(ctx *cel.OptimizerContext, a *ast.AST) *as return ctx.NewAST(blockExpr) } -func (opt *ruleComposerImpl) optimizeRule(ctx *cel.OptimizerContext, r *CompiledRule) ast.Expr { +func (opt *ruleComposerImpl) optimizeRule(ctx *cel.OptimizerContext, r *CompiledRule, asList bool) ast.Expr { // Visitor to rewrite variables-prefixed identifiers with index names. opt.enterScope() defer opt.exitScope() @@ -189,43 +189,59 @@ func (opt *ruleComposerImpl) optimizeRule(ctx *cel.OptimizerContext, r *Compiled opt.registerVariable(ctx, v) } + isAggregate := r.semantic == aggregate + returnList := isAggregate || asList + matches := r.Matches() matchCount := len(matches) - var output compositionStep = nil - // If the rule has an optional output, the last result in the ternary should return - // `optional.none`. This output is implicit and created here to reflect the desired - // last possible output of this type of rule. - if r.HasOptionalOutput() { - output = newOptionalCompositionStep(ctx, ctx.NewLiteral(types.True), ctx.NewCall("optional.none")) - } + output := opt.createBaseStep(ctx, returnList, r.HasOptionalOutput()) + // Build the rule subgraph. for i := matchCount - 1; i >= 0; i-- { m := matches[i] cond := ctx.CopyASTAndMetadata(m.Condition().NativeRep()) - // If the output is non-nil, then it is considered a non-optional output since - // it is explictly stated. If the rule itself is optional, then the base case value - // of output being optional.none() will convert the non-optional value to an optional - // one. + var currentStep compositionStep if m.Output() != nil { + // If the output is non-nil, then it is considered a non-optional output since + // it is explicitly stated. If the rule itself is optional, then the base case value + // of output being optional.none() will convert the non-optional value to an optional + // one. out := ctx.CopyASTAndMetadata(m.Output().Expr().NativeRep()) - step := newNonOptionalCompositionStep(ctx, cond, out) - output = step.combine(output) - continue + if returnList { + out = ctx.NewList([]ast.Expr{out}, []int32{}) + } + currentStep = newNonOptionalCompositionStep(ctx, cond, out) + + } else if m.NestedRule() != nil { + // If the match has a nested rule, then compute the rule and whether it has + // an optional return value. + // + // Semantics for nesting: + // - With optional values (nestedHasOptional = true): The step is treated as optional. + // If the nested rule yields optional.none, composition allows fall-through to + // subsequent match cases. + // - Without optional values (nestedHasOptional = false): The step is treated as non-optional. + // A matching result produces a concrete value that short-circuits further match evaluation, + // though it may be wrapped into optional.of(...) if the outer rule produces optional output. + child := m.NestedRule() + nestedRule := opt.optimizeRule(ctx, child, returnList) + if child.HasOptionalOutput() { + currentStep = newOptionalCompositionStep(ctx, cond, nestedRule) + } else { + currentStep = newNonOptionalCompositionStep(ctx, cond, nestedRule) + } + } else { + // Report an error for an unknown rule kind: + ctx.ReportErrorAtID(cond.ID(), "unknown match kind: %v", m.SourceID()) + return nil } - // If the match has a nested rule, then compute the rule and whether it has - // an optional return value. - child := m.NestedRule() - nestedRule := opt.optimizeRule(ctx, child) - nestedHasOptional := child.HasOptionalOutput() - if nestedHasOptional { - step := newOptionalCompositionStep(ctx, cond, nestedRule) - output = step.combine(output) - continue + if isAggregate { + output = opt.combineAggregate(ctx, currentStep, output) + } else { + output = currentStep.combine(output) } - step := newNonOptionalCompositionStep(ctx, cond, nestedRule) - output = step.combine(output) } matchExpr := output.expr() @@ -235,6 +251,34 @@ func (opt *ruleComposerImpl) optimizeRule(ctx *cel.OptimizerContext, r *Compiled return matchExpr } +func (opt *ruleComposerImpl) createBaseStep(ctx *cel.OptimizerContext, returnList, hasOptionalOutput bool) compositionStep { + if returnList { + return newNonOptionalCompositionStep(ctx, ctx.NewLiteral(types.True), ctx.NewList([]ast.Expr{}, []int32{})) + } + if hasOptionalOutput { + return newOptionalCompositionStep(ctx, ctx.NewLiteral(types.True), ctx.NewCall("optional.none")) + } + return nil +} + +func (opt *ruleComposerImpl) combineAggregate(ctx *cel.OptimizerContext, step, accumulatedStep compositionStep) compositionStep { + trueCondition := ctx.NewLiteral(types.True) + currentListPart := step.expr() + var conditionalListPart ast.Expr + if step.isConditional() { + emptyList := ctx.NewList([]ast.Expr{}, []int32{}) + conditionalListPart = ctx.NewCall(operators.Conditional, step.condition(), currentListPart, emptyList) + } else { + conditionalListPart = currentListPart + } + + if accumulatedStep.expr().Kind() == ast.ListKind && len(accumulatedStep.expr().AsList().Elements()) == 0 { + return newNonOptionalCompositionStep(ctx, trueCondition, conditionalListPart) + } + concatenated := ctx.NewCall(operators.Add, conditionalListPart, accumulatedStep.expr()) + return newNonOptionalCompositionStep(ctx, trueCondition, concatenated) +} + func (opt *ruleComposerImpl) rewriteVariableName(ctx *cel.OptimizerContext) ast.Visitor { return ast.NewExprVisitor(func(expr ast.Expr) { if expr.Kind() != ast.IdentKind || !strings.HasPrefix(expr.AsIdent(), "variables.") { @@ -265,7 +309,7 @@ func (opt *ruleComposerImpl) registerVariable(ctx *cel.OptimizerContext, v *Comp celType: v.Declaration().Type()} opt.varIndices = append(opt.varIndices, vi) if len(opt.scopes) > 0 { - opt.scopes[len(opt.scopes) - 1][varName] = len(opt.varIndices) - 1 + opt.scopes[len(opt.scopes)-1][varName] = len(opt.varIndices) - 1 } opt.nextVarIndex++ } @@ -494,6 +538,9 @@ func (s nonOptionalCompositionStep) combine(step compositionStep) compositionSte // Likely a candidate for dead-code warnings. return s } + if !s.isConditional() { + return s + } return newNonOptionalCompositionStep(ctx, trueCondition, ctx.NewCall(operators.Conditional, @@ -587,9 +634,7 @@ func isOptionalNone(e ast.Expr) bool { func removeIneligibleSubExprs(e ast.NavigableExpr, unnestMap map[int64]bool) { for _, id := range comprehensionSubExprIDs(e) { - if _, found := unnestMap[id]; found { - delete(unnestMap, id) - } + delete(unnestMap, id) } } diff --git a/policy/composer_test.go b/policy/composer_test.go index 5cd601c2c..1ca055a11 100644 --- a/policy/composer_test.go +++ b/policy/composer_test.go @@ -1,97 +1,204 @@ package policy import ( + "fmt" "strings" "testing" "github.com/google/cel-go/cel" "github.com/google/cel-go/common/ast" "github.com/google/cel-go/common/debug" + "github.com/google/cel-go/common/types" "github.com/google/cel-go/ext" ) -func TestCompose_SourceInfo(t *testing.T) { - policyYAML := `name: test_policy +func TestCompose(t *testing.T) { + tests := []struct { + name string + policy string + composerOpts []ComposerOption + wantUnparsed string + wantEval string + checkInfo bool + }{ + { + name: "source_info", + policy: `name: test_policy rule: match: - condition: "2 == 1" output: "'hi'" - output: "'hello' + ' world'" -` - src := StringSource(policyYAML, "test_policy.yaml") - parser, err := NewParser() - if err != nil { - t.Fatalf("NewParser() failed: %v", err) - } - policy, iss := parser.Parse(src) - if iss.Err() != nil { - t.Fatalf("parser.Parse() failed: %v", iss.Err()) - } - - env, err := cel.NewEnv(cel.OptionalTypes(), ext.Bindings()) - if err != nil { - t.Fatalf("cel.NewEnv() failed: %v", err) - } - compiledRule, iss := CompileRule(env, policy) - if iss.Err() != nil { - t.Fatalf("CompileRule() failed: %v", iss.Err()) - } - composer, err := NewRuleComposer(env) - if err != nil { - t.Fatalf("NewRuleComposer() failed: %v", err) - } - compAST, iss := composer.Compose(compiledRule) - if iss.Err() != nil { - t.Fatalf("composer.Compose() failed: %v", iss.Err()) - } - - si := compAST.SourceInfo() - if si.Location != "test_policy.yaml" { - t.Errorf("SourceInfo.Location got %q, wanted test_policy.yaml", si.Location) - } - verifySourceInfoTransfer(t, compiledRule, compAST) -} - -func TestCompose_Unnest(t *testing.T) { - policyYAML := `name: unnest +`, + checkInfo: true, + }, + { + name: "unnest", + policy: `name: unnest rule: match: - condition: "2 == 1" output: "'hi'" - output: "'hello'" -` - src := StringSource(policyYAML, "unnest.yaml") - parser, err := NewParser() - if err != nil { - t.Fatalf("NewParser() failed: %v", err) +`, + composerOpts: []ComposerOption{ExpressionUnnestHeight(1)}, + checkInfo: true, + }, + { + name: "empty_aggregate", + policy: `name: empty_nested_match_under_aggregate +rule: + aggregate: + - condition: "true" + rule: + match: [] +`, + wantUnparsed: "[]", + wantEval: "[]", + }, + { + name: "conditional_optional_nested", + policy: `name: conditional_optional_nested +rule: + match: + - condition: "2 == 2" + rule: + match: + - condition: "1 == 1" + output: "'foo'" + - condition: "true" + rule: + match: + - condition: "3 == 3" + output: "'bar'" +`, + wantUnparsed: `(2 == 2) ? ((1 == 1) ? optional.of("foo") : optional.none()) : ((3 == 3) ? optional.of("bar") : optional.none())`, + wantEval: `foo`, + }, } - policy, iss := parser.Parse(src) - if iss.Err() != nil { - t.Fatalf("parser.Parse() failed: %v", iss.Err()) + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + env, compiledRule, compAST := parseAndComposeRule(t, tc.policy, tc.name+".yaml", tc.composerOpts...) + if tc.checkInfo { + si := compAST.SourceInfo() + if si.Location != tc.name+".yaml" { + t.Errorf("SourceInfo.Location got %q, wanted %s.yaml", si.Location, tc.name) + } + verifySourceInfoTransfer(t, compiledRule, compAST) + if t.Failed() { + t.Logf("composed AST: %s", debug.ToDebugStringWithIDs(compAST.NativeRep().Expr())) + t.Logf("SourceInfo: %v", compAST.NativeRep().SourceInfo().OffsetRanges()) + } + } + if tc.wantUnparsed != "" { + exprStr, err := cel.AstToString(compAST) + if err != nil { + t.Fatalf("cel.AstToString() failed: %v", err) + } + if normalize(exprStr) != normalize(tc.wantUnparsed) { + t.Errorf("cel.AstToString() got %q, wanted %q", exprStr, tc.wantUnparsed) + } + } + if tc.wantEval != "" { + prg, err := env.Program(compAST) + if err != nil { + t.Fatalf("env.Program() failed: %v", err) + } + res, _, err := prg.Eval(cel.NoVars()) + if err != nil { + t.Fatalf("prg.Eval() failed: %v", err) + } + if fmt.Sprintf("%v", res.Value()) != tc.wantEval { + t.Errorf("eval result got %v, wanted %s", res.Value(), tc.wantEval) + } + } + }) } +} - env, err := cel.NewEnv(cel.OptionalTypes(), ext.Bindings()) +type testUnconditionalComposer struct{} + +func (t testUnconditionalComposer) Optimize(ctx *cel.OptimizerContext, a *ast.AST) *ast.AST { + trueCond := ctx.NewLiteral(types.True) + out1 := ctx.NewLiteral(types.String("first")) + out2 := ctx.NewLiteral(types.String("second")) + + s := newNonOptionalCompositionStep(ctx, trueCond, out1) + step := newNonOptionalCompositionStep(ctx, trueCond, out2) + + combined := s.combine(step) + return ctx.NewAST(combined.expr()) +} + +// Note: This test case cannot be reached through the policy format (because the compiler +// statically rejects policies with unreachable outputs), but is expressed in code for defense in depth. +func TestNonOptionalCompositionStep_UnconditionalCombine(t *testing.T) { + env, err := cel.NewEnv() if err != nil { t.Fatalf("cel.NewEnv() failed: %v", err) } - compiledRule, iss := CompileRule(env, policy) + opt, err := cel.NewStaticOptimizer(testUnconditionalComposer{}) + if err != nil { + t.Fatalf("cel.NewStaticOptimizer() failed: %v", err) + } + dummyAST, _ := env.Compile("true") + resultAST, iss := opt.Optimize(env, dummyAST) if iss.Err() != nil { - t.Fatalf("CompileRule() failed: %v", iss.Err()) + t.Fatalf("Optimize() failed: %v", iss.Err()) } + exprStr, err := cel.AstToString(resultAST) + if err != nil { + t.Fatalf("cel.AstToString() failed: %v", err) + } + if exprStr != `"first"` { + t.Errorf("got %q, wanted \"first\"", exprStr) + } +} - composer, err := NewRuleComposer(env, ExpressionUnnestHeight(1)) +func TestRuleComposerError(t *testing.T) { + env, err := cel.NewEnv() if err != nil { - t.Fatalf("NewRuleComposer() failed: %v", err) + t.Fatalf("NewEnv() failed: %v", err) } - compAST, iss := composer.Compose(compiledRule) - if iss.Err() != nil { - t.Fatalf("composer.Compose() failed: %v", iss.Err()) + _, err = NewRuleComposer(env, ExpressionUnnestHeight(-1)) + if err == nil || !strings.Contains(err.Error(), "invalid unnest") { + t.Errorf("NewRuleComposer() got %v, wanted 'invalid unnest'", err) } +} - verifySourceInfoTransfer(t, compiledRule, compAST) - if t.Failed() { - t.Logf("composed AST: %s", debug.ToDebugStringWithIDs(compAST.NativeRep().Expr())) - t.Logf("SourceInfo: %v", compAST.NativeRep().SourceInfo().OffsetRanges()) +func TestRuleComposerUnnest(t *testing.T) { + for _, tst := range composerUnnestTests { + tc := tst + t.Run(tc.name, func(t *testing.T) { + r := newRunner(tc.name, tc.expr, []ParserOption{}, tc.envOpts...) + env, rule, iss := r.compileRule(t) + if iss.Err() != nil { + t.Fatalf("CompileRule() failed: %v", iss.Err()) + } + rc, err := NewRuleComposer(env, tc.composerOpts...) + if err != nil { + t.Fatalf("NewRuleComposer() failed: %v", err) + } + ast, iss := rc.Compose(rule) + if iss.Err() != nil { + t.Fatalf("Compose(rule) failed: %v", iss.Err()) + } + policy := parsePolicy(t, tc.name, []ParserOption{}) + verifySourceInfoCoverage(t, policy, ast) + unparsed, err := cel.AstToString(ast) + if err != nil { + t.Fatalf("cel.AstToString() failed: %v", err) + } + if normalize(unparsed) != normalize(tc.composed) { + t.Errorf("cel.AstToString() got %s, wanted %s", unparsed, tc.composed) + } + if !ast.OutputType().IsEquivalentType(tc.outputType) { + t.Errorf("ast.OutputType() got %v, wanted %v", ast.OutputType(), tc.outputType) + } + r.setup(t, env, ast) + r.run(t) + }) } } @@ -107,9 +214,16 @@ func verifySourceInfoTransfer(t *testing.T, compiledRule *CompiledRule, composed ranges: &dstRanges}) } ast.PostOrderVisit(composed.NativeRep().Expr(), &collectRanges{sourceInfo: composed.NativeRep().SourceInfo(), ranges: &dstRanges}) + for _, v := range compiledRule.variables { + check(v.expr) + } for _, match := range compiledRule.matches { check(match.cond) - check(match.output.expr) + if match.output != nil { + check(match.output.expr) + } else if match.nestedRule != nil { + verifySourceInfoTransfer(t, match.nestedRule, composed) + } } } @@ -169,3 +283,33 @@ func (c *transferChecker) VisitExpr(srcExpr ast.Expr) { func (c *transferChecker) VisitEntryExpr(ast.EntryExpr) { } + +func parseAndComposeRule(t testing.TB, policyYAML, filename string, composerOpts ...ComposerOption) (*cel.Env, *CompiledRule, *cel.Ast) { + t.Helper() + src := StringSource(policyYAML, filename) + parser, err := NewParser() + if err != nil { + t.Fatalf("NewParser() failed: %v", err) + } + policy, iss := parser.Parse(src) + if iss.Err() != nil { + t.Fatalf("parser.Parse() failed: %v", iss.Err()) + } + env, err := cel.NewEnv(cel.OptionalTypes(), ext.Bindings()) + if err != nil { + t.Fatalf("cel.NewEnv() failed: %v", err) + } + compiledRule, iss := CompileRule(env, policy) + if iss.Err() != nil { + t.Fatalf("CompileRule() failed: %v", iss.Err()) + } + composer, err := NewRuleComposer(env, composerOpts...) + if err != nil { + t.Fatalf("NewRuleComposer() failed: %v", err) + } + compAST, iss := composer.Compose(compiledRule) + if iss.Err() != nil { + t.Fatalf("composer.Compose() failed: %v", iss.Err()) + } + return env, compiledRule, compAST +} diff --git a/policy/config_test.go b/policy/config_test.go index 77fcce274..108e4972c 100644 --- a/policy/config_test.go +++ b/policy/config_test.go @@ -102,7 +102,7 @@ variables: t.Fatalf("cel.NewEnv() failed: %v", err) } for _, tst := range tests { - c := parseConfigYaml(t, tst) + c := parseConfigYAML(t, tst) _, err := baseEnv.Extend(FromConfig(c)) if err != nil { t.Errorf("AsEnvOptions() generated error: %v", err) @@ -233,7 +233,7 @@ functions: t.Fatalf("cel.NewEnv() failed: %v", err) } for _, tst := range tests { - c := parseConfigYaml(t, tst.config) + c := parseConfigYAML(t, tst.config) _, err := baseEnv.Extend(FromConfig(c)) if err == nil || err.Error() != tst.err { t.Errorf("AsEnvOptions() got error: %v, wanted %s", err, tst.err) @@ -241,7 +241,7 @@ functions: } } -func parseConfigYaml(t *testing.T, doc string) *env.Config { +func parseConfigYAML(t *testing.T, doc string) *env.Config { config := &env.Config{} if err := yaml.Unmarshal([]byte(doc), config); err != nil { t.Fatalf("yaml.Unmarshal(%q) failed: %v", doc, err) diff --git a/policy/helper_test.go b/policy/helper_test.go index fbb62b55a..3b6150f63 100644 --- a/policy/helper_test.go +++ b/policy/helper_test.go @@ -23,10 +23,10 @@ import ( "github.com/google/cel-go/common/env" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" "github.com/google/cel-go/test" "go.yaml.in/yaml/v3" - ) var ( @@ -129,6 +129,28 @@ var ( ? optional.of(((y == 1) ? optional.of("a") : optional.none()).orValue("b")) : optional.none()`, }, + { + name: "agent_tool_execution_governance", + expr: `(request.is_emergency ? ["REQUIRE_VP_APPROVAL"] : ((tool.is_mutation && request.env == "prod") ? ["REQUIRE_TECH_LEAD_2FA"] : (tool.is_mutation ? ["REQUIRE_PEER_CONFIRMATION"] : []))) + ((classifier.has_credit_card(tool.call.args) ? ["REDACT_PCI"] : (classifier.has_email_or_phone(tool.call.args) ? ["REDACT_PII"] : [])) + ((tool.call.args.batch_size > 10000) ? ["THROTTLE_TIER_3"] : ((tool.call.args.batch_size > 1000) ? ["THROTTLE_TIER_2"] : ((tool.call.args.batch_size > 100) ? ["THROTTLE_TIER_1"] : []))))`, + envOpts: []cel.EnvOption{ + cel.Function("classifier.has_credit_card", + cel.Overload("classifier_has_credit_card", []*cel.Type{cel.DynType}, cel.BoolType, + cel.UnaryBinding(func(args ref.Val) ref.Val { + if m, ok := args.(traits.Mapper); ok { + return types.Bool(m.Contains(types.String("cc")) == types.True) + } + return types.False + }))), + cel.Function("classifier.has_email_or_phone", + cel.Overload("classifier_has_email_or_phone", []*cel.Type{cel.DynType}, cel.BoolType, + cel.UnaryBinding(func(args ref.Val) ref.Val { + if m, ok := args.(traits.Mapper); ok { + return types.Bool(m.Contains(types.String("email")) == types.True || m.Contains(types.String("phone")) == types.True) + } + return types.False + }))), + }, + }, } composerUnnestTests = []struct { @@ -136,6 +158,7 @@ var ( expr string composed string composerOpts []ComposerOption + envOpts []cel.EnvOption outputType *cel.Type }{ { @@ -209,6 +232,30 @@ var ( (now.getHours() >= 20) ? @index5 : optional.of(@index3.format([@index0, @index2])))`, outputType: cel.OptionalType(cel.StringType), }, + { + name: "agent_tool_execution_governance", + composerOpts: []ComposerOption{ExpressionUnnestHeight(2)}, + envOpts: []cel.EnvOption{ + cel.Function("classifier.has_credit_card", + cel.Overload("classifier_has_credit_card", []*cel.Type{cel.DynType}, cel.BoolType, + cel.UnaryBinding(func(args ref.Val) ref.Val { + if m, ok := args.(traits.Mapper); ok { + return types.Bool(m.Contains(types.String("cc")) == types.True) + } + return types.False + }))), + cel.Function("classifier.has_email_or_phone", + cel.Overload("classifier_has_email_or_phone", []*cel.Type{cel.DynType}, cel.BoolType, + cel.UnaryBinding(func(args ref.Val) ref.Val { + if m, ok := args.(traits.Mapper); ok { + return types.Bool(m.Contains(types.String("email")) == types.True || m.Contains(types.String("phone")) == types.True) + } + return types.False + }))), + }, + composed: `cel.@block([tool.is_mutation && request.env == "prod", tool.is_mutation ? ["REQUIRE_PEER_CONFIRMATION"] : [], classifier.has_email_or_phone(tool.call.args) ? ["REDACT_PII"] : [], tool.call.args.batch_size > 10000, tool.call.args.batch_size > 1000, tool.call.args.batch_size > 100, request.is_emergency ? ["REQUIRE_VP_APPROVAL"] : (@index0 ? ["REQUIRE_TECH_LEAD_2FA"] : @index1)], @index6 + ((classifier.has_credit_card(tool.call.args) ? ["REDACT_PCI"] : @index2) + (@index3 ? ["THROTTLE_TIER_3"] : (@index4 ? ["THROTTLE_TIER_2"] : (@index5 ? ["THROTTLE_TIER_1"] : [])))))`, + outputType: cel.ListType(cel.StringType), + }, } policyErrorTests = []struct { @@ -270,6 +317,9 @@ ERROR: testdata/errors/policy.yaml:45:16: incompatible output types: block has o | ........^ ERROR: testdata/errors_unreachable/policy.yaml:36:13: match creates unreachable outputs | - output: | + | ............^ +ERROR: testdata/errors_unreachable/policy.yaml:38:13: Condition is always false + | - condition: "false" | ............^`, }, { @@ -278,6 +328,30 @@ ERROR: testdata/errors_unreachable/policy.yaml:36:13: match creates unreachable | match: | ........^`, }, + { + name: "aggregate_errors", + err: `ERROR: testdata/aggregate_errors/policy.yaml:21:13: match creates unreachable outputs + | - condition: "true" + | ............^ +ERROR: testdata/aggregate_errors/policy.yaml:24:22: incompatible output types: block has output type int, but previous outputs have type optional_type(string) + | output: "403" + | .....................^`, + }, + { + name: "aggregate_list_errors", + err: `ERROR: testdata/aggregate_list_errors/policy.yaml:21:13: match creates unreachable outputs + | - condition: "true" + | ............^ +ERROR: testdata/aggregate_list_errors/policy.yaml:24:22: incompatible output types: block has output type int, but previous outputs have type list(string) + | output: "403" + | .....................^`, + }, + { + name: "aggregate_nested_mixed_semantics", + err: `ERROR: testdata/aggregate_nested_mixed_semantics/policy.yaml:23:15: nested aggregate rules are not allowed + | aggregate: + | ..............^`, + }, } ) diff --git a/policy/parser.go b/policy/parser.go index a42c7a3b9..bc81d995c 100644 --- a/policy/parser.go +++ b/policy/parser.go @@ -25,11 +25,13 @@ import ( "github.com/google/cel-go/common/ast" ) -type semanticType int +// SemanticType describes the evaluation semantic for a given policy block. +type SemanticType int const ( - unspecified semanticType = iota + unspecified SemanticType = iota firstMatch + aggregate ) // NewPolicy creates a policy object which references a policy source and source information. @@ -38,7 +40,7 @@ func NewPolicy(src *Source, info *ast.SourceInfo) *Policy { metadata: map[string]any{}, source: src, info: info, - semantic: firstMatch, + semantic: unspecified, imports: []*Import{}, } } @@ -49,13 +51,29 @@ type Policy struct { description ValueString imports []*Import rule *Rule - semantic semanticType + semantic SemanticType info *ast.SourceInfo source *Source metadata map[string]any } +// Semantic returns the evaluation semantic for the policy. +func (p *Policy) Semantic() SemanticType { + if p.semantic == unspecified { + return firstMatch + } + return p.semantic +} + +// SetSemantic configures the evaluation semantic for the policy. +func (p *Policy) SetSemantic(s SemanticType) { + if p.semantic != unspecified && p.semantic != s { + return + } + p.semantic = s +} + // Source returns the policy file contents as a CEL source object. func (p *Policy) Source() *Source { return p.source @@ -179,6 +197,7 @@ func NewRule(exprID int64) *Rule { exprID: exprID, variables: []*Variable{}, matches: []*Match{}, + semantic: unspecified, } } @@ -189,6 +208,28 @@ type Rule struct { description *ValueString variables []*Variable matches []*Match + semantic SemanticType +} + +// Semantic returns the evaluation semantic for the rule. +func (r *Rule) Semantic() SemanticType { + if r.semantic == unspecified { + return firstMatch + } + return r.semantic +} + +// SetSemantic configures the evaluation semantic for the rule. +func (r *Rule) SetSemantic(s SemanticType) { + if r.semantic != unspecified && r.semantic != s { + return + } + r.semantic = s +} + +// SourceID returns the source identifier associated with the rule. +func (r *Rule) SourceID() int64 { + return r.exprID } // ID returns the id value of the rule if it is set. @@ -249,6 +290,7 @@ func (r *Rule) getExplanationOutputRule() *Rule { er := Rule{ id: r.id, description: r.description, + semantic: r.semantic, } er.AddVariables(r.Variables()) for _, match := range r.matches { @@ -769,8 +811,18 @@ func (p *parserImpl) ParseRule(ctx ParserContext, policy *Policy, node *yaml.Nod r.SetDescription(ctx.NewString(val)) case "variables": p.parseVariables(ctx, policy, r, val) - case "match": - p.parseMatches(ctx, policy, r, val) + case "match", "aggregate": + sem := firstMatch + if fieldName == "aggregate" { + sem = aggregate + } + if r.semantic != unspecified && r.semantic != sem { + p.ReportErrorAtID(tagID, "Only one of 'match' or 'aggregate' may be set in a rule") + } else { + r.SetSemantic(sem) + policy.SetSemantic(sem) + p.parseMatches(ctx, policy, r, val) + } default: p.visitor.RuleTag(ctx, tagID, fieldName, val, policy, r) } @@ -802,7 +854,7 @@ func (p *parserImpl) ParseVariable(ctx ParserContext, policy *Policy, node *yaml return p.parseVariableObject(ctx, policy, v, node) } -func (p *parserImpl) parseVariableInline(ctx ParserContext, policy *Policy, v *Variable, node *yaml.Node) *Variable { +func (p *parserImpl) parseVariableInline(ctx ParserContext, _ *Policy, v *Variable, node *yaml.Node) *Variable { iterations := 0 p.RangeMap(node, func(key, val *yaml.Node) bool { keyVal := ctx.NewString(key) @@ -841,12 +893,16 @@ func (p *parserImpl) parseMatches(ctx ParserContext, policy *Policy, r *Rule, no return } for _, val := range node.Content { - r.AddMatch(p.ParseMatch(ctx, policy, val)) + r.AddMatch(p.parseMatchInternal(ctx, policy, r, val)) } } // ParseMatch will parse the current yaml node as though it is the entry point to a match. func (p *parserImpl) ParseMatch(ctx ParserContext, policy *Policy, node *yaml.Node) *Match { + return p.parseMatchInternal(ctx, policy, nil, node) +} + +func (p *parserImpl) parseMatchInternal(ctx ParserContext, policy *Policy, r *Rule, node *yaml.Node) *Match { m, id := ctx.NewMatch(node) if p.assertYAMLType(id, node, yamlMap) == nil || !p.checkMapValid(ctx, id, node) { return m @@ -868,7 +924,7 @@ func (p *parserImpl) ParseMatch(ctx ParserContext, policy *Policy, node *yaml.No p.ReportErrorAtID(keyID, "explanation can only be set on output match cases, not nested rules") } m.SetExplanation(ctx.NewString(val)) - case "rule": + case "rule", "match", "aggregate": if m.HasOutput() { p.ReportErrorAtID(keyID, "only the rule or the output may be set") } diff --git a/policy/parser_test.go b/policy/parser_test.go index f407a8600..9956875f6 100644 --- a/policy/parser_test.go +++ b/policy/parser_test.go @@ -147,6 +147,19 @@ rule: }, { txt: ` +rule: + match: + - condition: "true" + output: "'foo'" + aggregate: + - condition: "true" + output: "'bar'"`, + err: `ERROR: :6:3: Only one of 'match' or 'aggregate' may be set in a rule + | aggregate: + | ..^`, + }, + { + txt: ` rule: match: - condition: "true" @@ -217,6 +230,30 @@ rule: | - name | ......^`, }, + { + txt: ` +name: test +rule: + match: + - output: 'true' + aggregate: + - output: 'true'`, + err: `ERROR: :6:3: Only one of 'match' or 'aggregate' may be set in a rule + | aggregate: + | ..^`, + }, + { + txt: ` +name: test +rule: + aggregate: + - output: 'true' + match: + - output: 'true'`, + err: `ERROR: :6:3: Only one of 'match' or 'aggregate' may be set in a rule + | match: + | ..^`, + }, } for _, tst := range tests { @@ -389,3 +426,36 @@ func (t *testTagHandler) PolicyTag(ctx ParserContext, id int64, tagName string, p.SetMetadata(tagName, node.Value) } } + +func TestPolicyAndRuleSemanticMethods(t *testing.T) { + p := NewPolicy(nil, nil) + if p.Semantic() != firstMatch { + t.Errorf("got %v, wanted firstMatch", p.Semantic()) + } + p.SetSemantic(aggregate) + if p.Semantic() != aggregate { + t.Errorf("got %v, wanted aggregate", p.Semantic()) + } + // Attempt to set conflicting semantic + p.SetSemantic(firstMatch) + if p.Semantic() != aggregate { + t.Errorf("got %v, wanted aggregate after conflicting SetSemantic", p.Semantic()) + } + + r := NewRule(123) + if r.SourceID() != 123 { + t.Errorf("got %v, wanted 123", r.SourceID()) + } + if r.Semantic() != firstMatch { + t.Errorf("got %v, wanted firstMatch", r.Semantic()) + } + r.SetSemantic(aggregate) + if r.Semantic() != aggregate { + t.Errorf("got %v, wanted aggregate", r.Semantic()) + } + // Attempt to set conflicting semantic + r.SetSemantic(firstMatch) + if r.Semantic() != aggregate { + t.Errorf("got %v, wanted aggregate after conflicting SetSemantic", r.Semantic()) + } +} diff --git a/policy/testdata/agent_tool_execution_governance/config.yaml b/policy/testdata/agent_tool_execution_governance/config.yaml new file mode 100644 index 000000000..83a111eea --- /dev/null +++ b/policy/testdata/agent_tool_execution_governance/config.yaml @@ -0,0 +1,42 @@ +# Copyright 2026 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 +# +# https://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. + +name: agent_tool_execution_governance +variables: + - name: "request.is_emergency" + type_name: "bool" + - name: "request.env" + type_name: "string" + - name: "tool.is_mutation" + type_name: "bool" + - name: "tool.call.args" + type_name: "map" + params: + - type_name: "string" + - type_name: "dyn" +functions: + - name: "classifier.has_credit_card" + overloads: + - id: "classifier_has_credit_card" + args: + - type_name: "dyn" + return: + type_name: "bool" + - name: "classifier.has_email_or_phone" + overloads: + - id: "classifier_has_email_or_phone" + args: + - type_name: "dyn" + return: + type_name: "bool" diff --git a/policy/testdata/agent_tool_execution_governance/policy.yaml b/policy/testdata/agent_tool_execution_governance/policy.yaml new file mode 100644 index 000000000..0a88e8ac5 --- /dev/null +++ b/policy/testdata/agent_tool_execution_governance/policy.yaml @@ -0,0 +1,44 @@ +# Copyright 2026 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 +# +# https://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. + +name: agent_tool_execution_governance +rule: + aggregate: + # Dimension 1: Approval Requirements (First-Match Escalation) + - rule: + match: + - condition: "request.is_emergency" + output: "'REQUIRE_VP_APPROVAL'" + - condition: "tool.is_mutation && request.env == 'prod'" + output: "'REQUIRE_TECH_LEAD_2FA'" + - condition: "tool.is_mutation" + output: "'REQUIRE_PEER_CONFIRMATION'" + + # Dimension 2: Data Redaction (First-Match Specificity) + - rule: + match: + - condition: "classifier.has_credit_card(tool.call.args)" + output: "'REDACT_PCI'" + - condition: "classifier.has_email_or_phone(tool.call.args)" + output: "'REDACT_PII'" + + # Dimension 3: Rate Limiting (First-Match Threshold Ladder) + - rule: + match: + - condition: "tool.call.args.batch_size > 10000" + output: "'THROTTLE_TIER_3'" + - condition: "tool.call.args.batch_size > 1000" + output: "'THROTTLE_TIER_2'" + - condition: "tool.call.args.batch_size > 100" + output: "'THROTTLE_TIER_1'" diff --git a/policy/testdata/agent_tool_execution_governance/tests.yaml b/policy/testdata/agent_tool_execution_governance/tests.yaml new file mode 100644 index 000000000..ef4aa11a9 --- /dev/null +++ b/policy/testdata/agent_tool_execution_governance/tests.yaml @@ -0,0 +1,72 @@ +# Copyright 2026 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 +# +# https://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. + +description: "Tests governance policy evaluation with multi-dimensional aggregate rules" +section: + - name: "emergency_approval" + tests: + - name: "emergency_trumps_all_approval_rules" + input: + request.is_emergency: + value: true + request.env: + value: "prod" + tool.is_mutation: + value: true + tool.call.args: + expr: "{'batch_size': 50}" + output: + expr: "['REQUIRE_VP_APPROVAL']" + - name: "prod_mutation_with_pci_and_throttling" + tests: + - name: "prod_mutation_pci_tier2" + input: + request.is_emergency: + value: false + request.env: + value: "prod" + tool.is_mutation: + value: true + tool.call.args: + expr: "{'batch_size': dyn(1500), 'cc': dyn('411111111111')}" + output: + expr: "['REQUIRE_TECH_LEAD_2FA', 'REDACT_PCI', 'THROTTLE_TIER_2']" + - name: "dev_mutation_with_pii_and_tier1" + tests: + - name: "dev_mutation_pii_tier1" + input: + request.is_emergency: + value: false + request.env: + value: "dev" + tool.is_mutation: + value: true + tool.call.args: + expr: "{'batch_size': dyn(500), 'email': dyn('user@example.com')}" + output: + expr: "['REQUIRE_PEER_CONFIRMATION', 'REDACT_PII', 'THROTTLE_TIER_1']" + - name: "read_only_tool" + tests: + - name: "no_rules_matched" + input: + request.is_emergency: + value: false + request.env: + value: "prod" + tool.is_mutation: + value: false + tool.call.args: + expr: "{'batch_size': 10}" + output: + expr: "[]" diff --git a/policy/testdata/aggregate_errors/config.yaml b/policy/testdata/aggregate_errors/config.yaml new file mode 100644 index 000000000..b0c22629d --- /dev/null +++ b/policy/testdata/aggregate_errors/config.yaml @@ -0,0 +1,15 @@ +# Copyright 2026 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 +# +# https://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. + +name: aggregate_errors diff --git a/policy/testdata/aggregate_errors/policy.yaml b/policy/testdata/aggregate_errors/policy.yaml new file mode 100644 index 000000000..b0f73e174 --- /dev/null +++ b/policy/testdata/aggregate_errors/policy.yaml @@ -0,0 +1,24 @@ +# Copyright 2024 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 +# +# https://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. + +name: aggregate_errors +rule: + aggregate: + - condition: "true" + rule: + match: + - condition: "true" + output: "optional.of('USER_PII')" + - condition: "true" + output: "403" diff --git a/policy/testdata/aggregate_list_errors/config.yaml b/policy/testdata/aggregate_list_errors/config.yaml new file mode 100644 index 000000000..20edb6308 --- /dev/null +++ b/policy/testdata/aggregate_list_errors/config.yaml @@ -0,0 +1,15 @@ +# Copyright 2026 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 +# +# https://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. + +name: aggregate_list_errors diff --git a/policy/testdata/aggregate_list_errors/policy.yaml b/policy/testdata/aggregate_list_errors/policy.yaml new file mode 100644 index 000000000..3836e59f1 --- /dev/null +++ b/policy/testdata/aggregate_list_errors/policy.yaml @@ -0,0 +1,24 @@ +# Copyright 2024 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 +# +# https://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. + +name: aggregate_list_errors +rule: + aggregate: + - condition: "true" + rule: + match: + - condition: "true" + output: "['tag1', 'tag2']" + - condition: "true" + output: "403" diff --git a/policy/testdata/aggregate_nested_mixed_semantics/config.yaml b/policy/testdata/aggregate_nested_mixed_semantics/config.yaml new file mode 100644 index 000000000..2c61341c9 --- /dev/null +++ b/policy/testdata/aggregate_nested_mixed_semantics/config.yaml @@ -0,0 +1,15 @@ +# Copyright 2026 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 +# +# https://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. + +name: aggregate_nested_mixed_semantics diff --git a/policy/testdata/aggregate_nested_mixed_semantics/policy.yaml b/policy/testdata/aggregate_nested_mixed_semantics/policy.yaml new file mode 100644 index 000000000..1b0afdd7a --- /dev/null +++ b/policy/testdata/aggregate_nested_mixed_semantics/policy.yaml @@ -0,0 +1,25 @@ +# Copyright 2024 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 +# +# https://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. + +name: aggregate_nested_mixed_semantics +rule: + aggregate: + - condition: "true" + rule: + match: + - condition: "true" + rule: + aggregate: + - condition: "true" + output: "'foo'" From 425a3e9e0eb8ac7eef6f06d4f3805e369a5ca602 Mon Sep 17 00:00:00 2001 From: l46kok Date: Mon, 17 Aug 2026 15:30:00 -0700 Subject: [PATCH 29/37] Fix agent_tool_execution_governance policy example to match cel-policy conformance test (#1424) --- policy/helper_test.go | 20 +++++++++---------- .../config.yaml | 8 ++++---- .../policy.yaml | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/policy/helper_test.go b/policy/helper_test.go index 3b6150f63..bf228a783 100644 --- a/policy/helper_test.go +++ b/policy/helper_test.go @@ -131,18 +131,18 @@ var ( }, { name: "agent_tool_execution_governance", - expr: `(request.is_emergency ? ["REQUIRE_VP_APPROVAL"] : ((tool.is_mutation && request.env == "prod") ? ["REQUIRE_TECH_LEAD_2FA"] : (tool.is_mutation ? ["REQUIRE_PEER_CONFIRMATION"] : []))) + ((classifier.has_credit_card(tool.call.args) ? ["REDACT_PCI"] : (classifier.has_email_or_phone(tool.call.args) ? ["REDACT_PII"] : [])) + ((tool.call.args.batch_size > 10000) ? ["THROTTLE_TIER_3"] : ((tool.call.args.batch_size > 1000) ? ["THROTTLE_TIER_2"] : ((tool.call.args.batch_size > 100) ? ["THROTTLE_TIER_1"] : []))))`, + expr: `(request.is_emergency ? ["REQUIRE_VP_APPROVAL"] : ((tool.is_mutation && request.env == "prod") ? ["REQUIRE_TECH_LEAD_2FA"] : (tool.is_mutation ? ["REQUIRE_PEER_CONFIRMATION"] : []))) + ((hasCreditCard(tool.call.args) ? ["REDACT_PCI"] : (hasEmailOrPhone(tool.call.args) ? ["REDACT_PII"] : [])) + ((tool.call.args.batch_size > 10000) ? ["THROTTLE_TIER_3"] : ((tool.call.args.batch_size > 1000) ? ["THROTTLE_TIER_2"] : ((tool.call.args.batch_size > 100) ? ["THROTTLE_TIER_1"] : []))))`, envOpts: []cel.EnvOption{ - cel.Function("classifier.has_credit_card", - cel.Overload("classifier_has_credit_card", []*cel.Type{cel.DynType}, cel.BoolType, + cel.Function("hasCreditCard", + cel.Overload("hasCreditCard", []*cel.Type{cel.DynType}, cel.BoolType, cel.UnaryBinding(func(args ref.Val) ref.Val { if m, ok := args.(traits.Mapper); ok { return types.Bool(m.Contains(types.String("cc")) == types.True) } return types.False }))), - cel.Function("classifier.has_email_or_phone", - cel.Overload("classifier_has_email_or_phone", []*cel.Type{cel.DynType}, cel.BoolType, + cel.Function("hasEmailOrPhone", + cel.Overload("hasEmailOrPhone", []*cel.Type{cel.DynType}, cel.BoolType, cel.UnaryBinding(func(args ref.Val) ref.Val { if m, ok := args.(traits.Mapper); ok { return types.Bool(m.Contains(types.String("email")) == types.True || m.Contains(types.String("phone")) == types.True) @@ -236,16 +236,16 @@ var ( name: "agent_tool_execution_governance", composerOpts: []ComposerOption{ExpressionUnnestHeight(2)}, envOpts: []cel.EnvOption{ - cel.Function("classifier.has_credit_card", - cel.Overload("classifier_has_credit_card", []*cel.Type{cel.DynType}, cel.BoolType, + cel.Function("hasCreditCard", + cel.Overload("hasCreditCard", []*cel.Type{cel.DynType}, cel.BoolType, cel.UnaryBinding(func(args ref.Val) ref.Val { if m, ok := args.(traits.Mapper); ok { return types.Bool(m.Contains(types.String("cc")) == types.True) } return types.False }))), - cel.Function("classifier.has_email_or_phone", - cel.Overload("classifier_has_email_or_phone", []*cel.Type{cel.DynType}, cel.BoolType, + cel.Function("hasEmailOrPhone", + cel.Overload("hasEmailOrPhone", []*cel.Type{cel.DynType}, cel.BoolType, cel.UnaryBinding(func(args ref.Val) ref.Val { if m, ok := args.(traits.Mapper); ok { return types.Bool(m.Contains(types.String("email")) == types.True || m.Contains(types.String("phone")) == types.True) @@ -253,7 +253,7 @@ var ( return types.False }))), }, - composed: `cel.@block([tool.is_mutation && request.env == "prod", tool.is_mutation ? ["REQUIRE_PEER_CONFIRMATION"] : [], classifier.has_email_or_phone(tool.call.args) ? ["REDACT_PII"] : [], tool.call.args.batch_size > 10000, tool.call.args.batch_size > 1000, tool.call.args.batch_size > 100, request.is_emergency ? ["REQUIRE_VP_APPROVAL"] : (@index0 ? ["REQUIRE_TECH_LEAD_2FA"] : @index1)], @index6 + ((classifier.has_credit_card(tool.call.args) ? ["REDACT_PCI"] : @index2) + (@index3 ? ["THROTTLE_TIER_3"] : (@index4 ? ["THROTTLE_TIER_2"] : (@index5 ? ["THROTTLE_TIER_1"] : [])))))`, + composed: `cel.@block([tool.is_mutation && request.env == "prod", tool.is_mutation ? ["REQUIRE_PEER_CONFIRMATION"] : [], hasEmailOrPhone(tool.call.args) ? ["REDACT_PII"] : [], tool.call.args.batch_size > 10000, tool.call.args.batch_size > 1000, tool.call.args.batch_size > 100, request.is_emergency ? ["REQUIRE_VP_APPROVAL"] : (@index0 ? ["REQUIRE_TECH_LEAD_2FA"] : @index1)], @index6 + ((hasCreditCard(tool.call.args) ? ["REDACT_PCI"] : @index2) + (@index3 ? ["THROTTLE_TIER_3"] : (@index4 ? ["THROTTLE_TIER_2"] : (@index5 ? ["THROTTLE_TIER_1"] : [])))))`, outputType: cel.ListType(cel.StringType), }, } diff --git a/policy/testdata/agent_tool_execution_governance/config.yaml b/policy/testdata/agent_tool_execution_governance/config.yaml index 83a111eea..d695615f2 100644 --- a/policy/testdata/agent_tool_execution_governance/config.yaml +++ b/policy/testdata/agent_tool_execution_governance/config.yaml @@ -26,16 +26,16 @@ variables: - type_name: "string" - type_name: "dyn" functions: - - name: "classifier.has_credit_card" + - name: "hasCreditCard" overloads: - - id: "classifier_has_credit_card" + - id: "hasCreditCard" args: - type_name: "dyn" return: type_name: "bool" - - name: "classifier.has_email_or_phone" + - name: "hasEmailOrPhone" overloads: - - id: "classifier_has_email_or_phone" + - id: "hasEmailOrPhone" args: - type_name: "dyn" return: diff --git a/policy/testdata/agent_tool_execution_governance/policy.yaml b/policy/testdata/agent_tool_execution_governance/policy.yaml index 0a88e8ac5..a8c0c01ef 100644 --- a/policy/testdata/agent_tool_execution_governance/policy.yaml +++ b/policy/testdata/agent_tool_execution_governance/policy.yaml @@ -28,9 +28,9 @@ rule: # Dimension 2: Data Redaction (First-Match Specificity) - rule: match: - - condition: "classifier.has_credit_card(tool.call.args)" + - condition: "hasCreditCard(tool.call.args)" output: "'REDACT_PCI'" - - condition: "classifier.has_email_or_phone(tool.call.args)" + - condition: "hasEmailOrPhone(tool.call.args)" output: "'REDACT_PII'" # Dimension 3: Rate Limiting (First-Match Threshold Ladder) From 49f20a19b060a176819b7ed0561cc74154458c64 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Mon, 17 Aug 2026 16:36:48 -0700 Subject: [PATCH 30/37] Consolidate saturating cost arithmetic into common/cost (#1420) The same saturating arithmetic existed in four places under three names: `safeAdd`/`safeMul` in `interpreter`, byte-identical copies in `ext`, and `addUint64NoOverflow`/`multiplyUint64NoOverflow`/`multiplyByCostFactor` in `checker`. Costs and sizes are uint64 values where math.MaxUint64 doubles as "unbounded", so every operation on them has to saturate rather than wrap; having one implementation of that per package made it easy for the domains to drift. `common/cost` now owns the single exported set: SafeAdd, SafeMultiply, SafeMultiplyByFactor, and SafeCeil. The package depends on nothing but `math`, so `checker`, `interpreter`, and `ext` can all reach it without cycles. Behavioral notes, all in extreme-value territory: - `CostTracker.costCall` computed the `matches` and `contains` products with an unguarded `*` while `safeMul` sat unused in the same file. Those products now saturate instead of wrapping, which matters as soon as an operand size can itself be saturated. - Inline `uint64(math.Ceil(float64(x)*factor))` conversions became SafeMultiplyByFactor, which is identical in range and returns MaxUint64 rather than an implementation-defined value out of range. - Truncating conversions (`uint64(float64(size)*costFactor)`) were left alone so that no in-range cost changes value. Locals named `cost` in the touched functions were renamed to `total`/`estimate` so the package identifier is not shadowed. --- checker/BUILD.bazel | 1 + checker/cost.go | 102 +++++++++++--------------------- common/cost/BUILD.bazel | 25 ++++++++ common/cost/cost.go | 78 ++++++++++++++++++++++++ common/cost/cost_test.go | 118 +++++++++++++++++++++++++++++++++++++ ext/BUILD.bazel | 1 + ext/costs.go | 20 +------ ext/encoders.go | 9 +-- ext/lists.go | 7 ++- ext/math.go | 5 +- ext/network.go | 33 ++++++----- ext/regex.go | 5 +- ext/sets.go | 5 +- ext/strings.go | 44 +++++++------- interpreter/BUILD.bazel | 1 + interpreter/runtimecost.go | 57 +++++++----------- 16 files changed, 338 insertions(+), 173 deletions(-) create mode 100644 common/cost/BUILD.bazel create mode 100644 common/cost/cost.go create mode 100644 common/cost/cost_test.go diff --git a/checker/BUILD.bazel b/checker/BUILD.bazel index 678b412a9..d48473ead 100644 --- a/checker/BUILD.bazel +++ b/checker/BUILD.bazel @@ -25,6 +25,7 @@ go_library( "//common:go_default_library", "//common/ast:go_default_library", "//common/containers:go_default_library", + "//common/cost:go_default_library", "//common/debug:go_default_library", "//common/decls:go_default_library", "//common/operators:go_default_library", diff --git a/checker/cost.go b/checker/cost.go index 3d7dd7ec4..45e0d9454 100644 --- a/checker/cost.go +++ b/checker/cost.go @@ -19,6 +19,7 @@ import ( "github.com/google/cel-go/common" "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/cost" "github.com/google/cel-go/common/overloads" "github.com/google/cel-go/common/types" "github.com/google/cel-go/parser" @@ -115,8 +116,8 @@ func FixedSizeEstimate(size uint64) SizeEstimate { // If add would result in an uint64 overflow, the result is math.MaxUint64. func (se SizeEstimate) Add(sizeEstimate SizeEstimate) SizeEstimate { return SizeEstimate{ - addUint64NoOverflow(se.Min, sizeEstimate.Min), - addUint64NoOverflow(se.Max, sizeEstimate.Max), + cost.SafeAdd(se.Min, sizeEstimate.Min), + cost.SafeAdd(se.Max, sizeEstimate.Max), } } @@ -124,8 +125,8 @@ func (se SizeEstimate) Add(sizeEstimate SizeEstimate) SizeEstimate { // If multiply would result in an uint64 overflow, the result is math.MaxUint64. func (se SizeEstimate) Multiply(sizeEstimate SizeEstimate) SizeEstimate { return SizeEstimate{ - multiplyUint64NoOverflow(se.Min, sizeEstimate.Min), - multiplyUint64NoOverflow(se.Max, sizeEstimate.Max), + cost.SafeMultiply(se.Min, sizeEstimate.Min), + cost.SafeMultiply(se.Max, sizeEstimate.Max), } } @@ -133,17 +134,17 @@ func (se SizeEstimate) Multiply(sizeEstimate SizeEstimate) SizeEstimate { // nearest integer of the result, rounded up. func (se SizeEstimate) MultiplyByCostFactor(costPerUnit float64) CostEstimate { return CostEstimate{ - multiplyByCostFactor(se.Min, costPerUnit), - multiplyByCostFactor(se.Max, costPerUnit), + cost.SafeMultiplyByFactor(se.Min, costPerUnit), + cost.SafeMultiplyByFactor(se.Max, costPerUnit), } } // MultiplyByCost multiplies by the cost and returns the product. // If multiply would result in an uint64 overflow, the result is math.MaxUint64. -func (se SizeEstimate) MultiplyByCost(cost CostEstimate) CostEstimate { +func (se SizeEstimate) MultiplyByCost(estimate CostEstimate) CostEstimate { return CostEstimate{ - multiplyUint64NoOverflow(se.Min, cost.Min), - multiplyUint64NoOverflow(se.Max, cost.Max), + cost.SafeMultiply(se.Min, estimate.Min), + cost.SafeMultiply(se.Max, estimate.Max), } } @@ -176,25 +177,25 @@ func UnknownCostEstimate() CostEstimate { } // FixedCostEstimate returns a cost with a fixed min and max range. -func FixedCostEstimate(cost uint64) CostEstimate { - return CostEstimate{Min: cost, Max: cost} +func FixedCostEstimate(fixedCost uint64) CostEstimate { + return CostEstimate{Min: fixedCost, Max: fixedCost} } // Add adds the costs and returns the sum. // If add would result in an uint64 overflow for the min or max, the value is set to math.MaxUint64. -func (ce CostEstimate) Add(cost CostEstimate) CostEstimate { +func (ce CostEstimate) Add(estimate CostEstimate) CostEstimate { return CostEstimate{ - Min: addUint64NoOverflow(ce.Min, cost.Min), - Max: addUint64NoOverflow(ce.Max, cost.Max), + Min: cost.SafeAdd(ce.Min, estimate.Min), + Max: cost.SafeAdd(ce.Max, estimate.Max), } } // Multiply multiplies by the cost and returns the product. // If multiply would result in an uint64 overflow, the result is math.MaxUint64. -func (ce CostEstimate) Multiply(cost CostEstimate) CostEstimate { +func (ce CostEstimate) Multiply(estimate CostEstimate) CostEstimate { return CostEstimate{ - Min: multiplyUint64NoOverflow(ce.Min, cost.Min), - Max: multiplyUint64NoOverflow(ce.Max, cost.Max), + Min: cost.SafeMultiply(ce.Min, estimate.Min), + Max: cost.SafeMultiply(ce.Max, estimate.Max), } } @@ -202,8 +203,8 @@ func (ce CostEstimate) Multiply(cost CostEstimate) CostEstimate { // nearest integer of the result, rounded up. func (ce CostEstimate) MultiplyByCostFactor(costPerUnit float64) CostEstimate { return CostEstimate{ - Min: multiplyByCostFactor(ce.Min, costPerUnit), - Max: multiplyByCostFactor(ce.Max, costPerUnit), + Min: cost.SafeMultiplyByFactor(ce.Min, costPerUnit), + Max: cost.SafeMultiplyByFactor(ce.Max, costPerUnit), } } @@ -219,37 +220,6 @@ func (ce CostEstimate) Union(size CostEstimate) CostEstimate { return result } -// addUint64NoOverflow adds non-negative ints. If the result is exceeds math.MaxUint64, math.MaxUint64 -// is returned. -func addUint64NoOverflow(x, y uint64) uint64 { - if y > 0 && x > math.MaxUint64-y { - return math.MaxUint64 - } - return x + y -} - -// multiplyUint64NoOverflow multiplies non-negative ints. If the result is exceeds math.MaxUint64, math.MaxUint64 -// is returned. -func multiplyUint64NoOverflow(x, y uint64) uint64 { - if y != 0 && x > math.MaxUint64/y { - return math.MaxUint64 - } - return x * y -} - -// multiplyByFactor multiplies an integer by a cost factor float and returns the nearest integer value, rounded up. -func multiplyByCostFactor(x uint64, y float64) uint64 { - xFloat := float64(x) - if xFloat > 0 && y > 0 && xFloat > math.MaxUint64/y { - return math.MaxUint64 - } - ceil := math.Ceil(xFloat * y) - if ceil >= doubleTwoTo64 { - return math.MaxUint64 - } - return uint64(ceil) -} - // CostOption configures flags which affect cost computations. type CostOption func(*coster) error @@ -465,32 +435,32 @@ func (c *coster) cost(e ast.Expr) CostEstimate { if e == nil { return CostEstimate{} } - var cost CostEstimate + var estimate CostEstimate switch e.Kind() { case ast.LiteralKind: - cost = constCost + estimate = constCost case ast.IdentKind: - cost = c.costIdent(e) + estimate = c.costIdent(e) case ast.SelectKind: - cost = c.costSelect(e) + estimate = c.costSelect(e) case ast.CallKind: - cost = c.costCall(e) + estimate = c.costCall(e) case ast.ListKind: - cost = c.costCreateList(e) + estimate = c.costCreateList(e) case ast.MapKind: - cost = c.costCreateMap(e) + estimate = c.costCreateMap(e) case ast.StructKind: - cost = c.costCreateStruct(e) + estimate = c.costCreateStruct(e) case ast.ComprehensionKind: if c.isBind(e) { - cost = c.costBind(e) + estimate = c.costBind(e) } else { - cost = c.costComprehension(e) + estimate = c.costComprehension(e) } default: return CostEstimate{} } - return cost + return estimate } func (c *coster) costIdent(e ast.Expr) CostEstimate { @@ -1013,14 +983,14 @@ func computeExprSize(expr ast.Expr) *SizeEstimate { default: return nil } - cost := FixedSizeEstimate(v) - return &cost + size := FixedSizeEstimate(v) + return &size } func computeTypeSize(t *types.Type) *SizeEstimate { if isScalar(t) { - cost := FixedSizeEstimate(1) - return &cost + size := FixedSizeEstimate(1) + return &size } return nil } @@ -1041,8 +1011,6 @@ func isScalar(t *types.Type) bool { } var ( - doubleTwoTo64 = math.Ldexp(1.0, 64) - unknownSizeEstimate = SizeEstimate{Min: 0, Max: math.MaxUint64} unknownCostEstimate = unknownSizeEstimate.MultiplyByCostFactor(1) diff --git a/common/cost/BUILD.bazel b/common/cost/BUILD.bazel new file mode 100644 index 000000000..8697d41f8 --- /dev/null +++ b/common/cost/BUILD.bazel @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +package( + default_visibility = ["//visibility:public"], + licenses = ["notice"], # Apache 2.0 +) + +go_library( + name = "go_default_library", + srcs = [ + "cost.go", + ], + importpath = "github.com/google/cel-go/common/cost", +) + +go_test( + name = "go_default_test", + size = "small", + srcs = [ + "cost_test.go", + ], + embed = [ + ":go_default_library", + ], +) diff --git a/common/cost/cost.go b/common/cost/cost.go new file mode 100644 index 000000000..1a81a88e5 --- /dev/null +++ b/common/cost/cost.go @@ -0,0 +1,78 @@ +// Copyright 2026 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 cost provides the saturating arithmetic shared by cost estimation and cost tracking. +// +// Costs and sizes are unsigned 64-bit values where math.MaxUint64 doubles as the representation +// of an unbounded, or unknown, quantity. Every operation in this package saturates at +// math.MaxUint64 rather than wrapping so that an unbounded input remains unbounded through any +// sequence of operations. +package cost + +import "math" + +// maxUint64AsFloat is the smallest float64 value greater than math.MaxUint64. +// +// Conversion of a float64 to a uint64 is undefined when the value is out of range, so float +// results are compared against this bound before conversion. +var maxUint64AsFloat = math.Ldexp(1.0, 64) + +// SafeAdd returns the sum of the input values, saturating at math.MaxUint64. +func SafeAdd(x, y uint64, rest ...uint64) uint64 { + sum := x + if y > 0 && sum > math.MaxUint64-y { + return math.MaxUint64 + } + sum += y + for _, r := range rest { + if r > 0 && sum > math.MaxUint64-r { + return math.MaxUint64 + } + sum += r + } + return sum +} + +// SafeMultiply returns the product of the input values, saturating at math.MaxUint64. +func SafeMultiply(x, y uint64) uint64 { + if y != 0 && x > math.MaxUint64/y { + return math.MaxUint64 + } + return x * y +} + +// SafeMultiplyByFactor multiplies a value by a cost factor and returns the nearest integer +// value, rounded up, saturating at math.MaxUint64. +func SafeMultiplyByFactor(x uint64, factor float64) uint64 { + xFloat := float64(x) + if xFloat > 0 && factor > 0 && xFloat > math.MaxUint64/factor { + return math.MaxUint64 + } + return SafeCeil(xFloat * factor) +} + +// SafeCeil returns the smallest integer value greater than or equal to the input, saturating at +// math.MaxUint64 and flooring at zero. +// +// Negative and NaN inputs return zero. +func SafeCeil(x float64) uint64 { + if math.IsNaN(x) || x <= 0 { + return 0 + } + ceil := math.Ceil(x) + if ceil >= maxUint64AsFloat { + return math.MaxUint64 + } + return uint64(ceil) +} diff --git a/common/cost/cost_test.go b/common/cost/cost_test.go new file mode 100644 index 000000000..83a3fd5f8 --- /dev/null +++ b/common/cost/cost_test.go @@ -0,0 +1,118 @@ +// Copyright 2026 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 cost + +import ( + "math" + "testing" +) + +func TestSafeAdd(t *testing.T) { + tests := []struct { + name string + x, y uint64 + rest []uint64 + want uint64 + }{ + {name: "zero", x: 0, y: 0, want: 0}, + {name: "simple", x: 2, y: 3, want: 5}, + {name: "variadic", x: 1, y: 2, rest: []uint64{3, 4}, want: 10}, + {name: "max plus zero", x: math.MaxUint64, y: 0, want: math.MaxUint64}, + {name: "overflow", x: math.MaxUint64, y: 1, want: math.MaxUint64}, + {name: "overflow near max", x: math.MaxUint64 - 5, y: 10, want: math.MaxUint64}, + {name: "overflow in rest", x: 1, y: 2, rest: []uint64{math.MaxUint64}, want: math.MaxUint64}, + {name: "saturated stays saturated", x: math.MaxUint64, y: math.MaxUint64, + rest: []uint64{math.MaxUint64}, want: math.MaxUint64}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if got := SafeAdd(tc.x, tc.y, tc.rest...); got != tc.want { + t.Errorf("SafeAdd(%d, %d, %v) got %d, want %d", tc.x, tc.y, tc.rest, got, tc.want) + } + }) + } +} + +func TestSafeMultiply(t *testing.T) { + tests := []struct { + name string + x, y uint64 + want uint64 + }{ + {name: "zero", x: 0, y: 0, want: 0}, + {name: "max by zero", x: math.MaxUint64, y: 0, want: 0}, + {name: "simple", x: 3, y: 4, want: 12}, + {name: "max by one", x: math.MaxUint64, y: 1, want: math.MaxUint64}, + {name: "overflow", x: math.MaxUint64, y: 2, want: math.MaxUint64}, + {name: "overflow squared", x: math.MaxUint32, y: math.MaxUint32 * 2, want: math.MaxUint64}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if got := SafeMultiply(tc.x, tc.y); got != tc.want { + t.Errorf("SafeMultiply(%d, %d) got %d, want %d", tc.x, tc.y, got, tc.want) + } + }) + } +} + +func TestSafeMultiplyByFactor(t *testing.T) { + tests := []struct { + name string + x uint64 + factor float64 + want uint64 + }{ + {name: "zero value", x: 0, factor: 0.1, want: 0}, + {name: "zero factor", x: 100, factor: 0, want: 0}, + {name: "rounds up", x: 15, factor: 0.1, want: 2}, + {name: "exact", x: 10, factor: 0.1, want: 1}, + {name: "whole factor", x: 10, factor: 3, want: 30}, + {name: "max saturates", x: math.MaxUint64, factor: 2, want: math.MaxUint64}, + {name: "max scaled down", x: math.MaxUint64, factor: 0.1, want: 1844674407370955264}, + {name: "negative factor", x: 10, factor: -1, want: 0}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if got := SafeMultiplyByFactor(tc.x, tc.factor); got != tc.want { + t.Errorf("SafeMultiplyByFactor(%d, %f) got %d, want %d", tc.x, tc.factor, got, tc.want) + } + }) + } +} + +func TestSafeCeil(t *testing.T) { + tests := []struct { + name string + x float64 + want uint64 + }{ + {name: "zero", x: 0, want: 0}, + {name: "negative", x: -1.5, want: 0}, + {name: "nan", x: math.NaN(), want: 0}, + {name: "fraction", x: 0.1, want: 1}, + {name: "rounds up", x: 2.5, want: 3}, + {name: "whole", x: 3.0, want: 3}, + {name: "infinity", x: math.Inf(1), want: math.MaxUint64}, + {name: "out of range", x: math.Ldexp(1.0, 64), want: math.MaxUint64}, + {name: "largest in range", x: math.Ldexp(1.0, 63), want: 1 << 63}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if got := SafeCeil(tc.x); got != tc.want { + t.Errorf("SafeCeil(%f) got %d, want %d", tc.x, got, tc.want) + } + }) + } +} diff --git a/ext/BUILD.bazel b/ext/BUILD.bazel index f362fd97b..57522b899 100644 --- a/ext/BUILD.bazel +++ b/ext/BUILD.bazel @@ -31,6 +31,7 @@ go_library( "//checker:go_default_library", "//common:go_default_library", "//common/ast:go_default_library", + "//common/cost:go_default_library", "//common/decls:go_default_library", "//common/env:go_default_library", "//common/operators:go_default_library", diff --git a/ext/costs.go b/ext/costs.go index d2cf7c757..4a6ba270a 100644 --- a/ext/costs.go +++ b/ext/costs.go @@ -66,6 +66,8 @@ func actualSize(value ref.Val) uint64 { return 1 } +// nodeAsUintValue returns the value of a literal int node as a uint64, or the default value if the +// node is not a non-negative int literal. func nodeAsUintValue(node checker.AstNode, defaultVal uint64) uint64 { if node.Expr().Kind() != ast.LiteralKind { return defaultVal @@ -102,21 +104,3 @@ func atLeastOne(size checker.SizeEstimate) checker.SizeEstimate { } return size } - -func safeAdd(x, y uint64, rest ...uint64) uint64 { - if y > 0 && x > math.MaxUint64-y { - return math.MaxUint64 - } - next := x + y - if len(rest) == 0 { - return next - } - return safeAdd(next, rest[0], rest[1:]...) -} - -func safeMul(x, y uint64) uint64 { - if y != 0 && x > math.MaxUint64/y { - return math.MaxUint64 - } - return x * y -} diff --git a/ext/encoders.go b/ext/encoders.go index 97fc932a5..f1bf5f9d5 100644 --- a/ext/encoders.go +++ b/ext/encoders.go @@ -22,6 +22,7 @@ import ( "github.com/google/cel-go/cel" "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/cost" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/interpreter" @@ -184,8 +185,8 @@ func estimateDecode(estimator checker.CostEstimator, target *checker.AstNode, ar func trackEncode(args []ref.Val, _ ref.Val) *uint64 { sz := actualSize(args[0]) - cost := uint64(math.Ceil(float64(sz)*stringCostFactor)) + callCost - return &cost + total := cost.SafeAdd(cost.SafeMultiplyByFactor(sz, stringCostFactor), callCost) + return &total } func trackJSONEncode(args []ref.Val, _ ref.Val) *uint64 { @@ -195,8 +196,8 @@ func trackJSONEncode(args []ref.Val, _ ref.Val) *uint64 { func trackDecode(args []ref.Val, _ ref.Val) *uint64 { sz := actualSize(args[0]) - cost := uint64(math.Ceil(float64(sz)*stringCostFactor)) + callCost - return &cost + total := cost.SafeAdd(cost.SafeMultiplyByFactor(sz, stringCostFactor), callCost) + return &total } func estimateEncodeSize(sz checker.SizeEstimate) checker.SizeEstimate { diff --git a/ext/lists.go b/ext/lists.go index 2196d218c..7d2883fe5 100644 --- a/ext/lists.go +++ b/ext/lists.go @@ -23,6 +23,7 @@ import ( "github.com/google/cel-go/checker" "github.com/google/cel-go/common" "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/cost" "github.com/google/cel-go/common/decls" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" @@ -893,7 +894,7 @@ func trackListSelfCompare(l traits.Lister) *uint64 { if elem.Type() == types.StringType || elem.Type() == types.BytesType { costFactor += common.StringTraversalCostFactor } - return trackAllocatingListCall(costFactor, safeMul(sz, sz)) + return trackAllocatingListCall(costFactor, cost.SafeMultiply(sz, sz)) } // trackAllocatingListCall computes costs as a function of the size of the result list with a baseline cost @@ -902,8 +903,8 @@ func trackAllocatingListCall(costFactor float64, size uint64) *uint64 { if costFactor < 0.0 { costFactor = 1.0 } - cost := safeAdd(uint64(float64(size)*costFactor), callCost, common.ListCreateBaseCost) - return &cost + total := cost.SafeAdd(uint64(float64(size)*costFactor), callCost, common.ListCreateBaseCost) + return &total } func estimateListDistinctLegacy(estimator checker.CostEstimator, target *checker.AstNode, args []checker.AstNode) *checker.CallEstimate { diff --git a/ext/math.go b/ext/math.go index e67b205de..ea969c8ed 100644 --- a/ext/math.go +++ b/ext/math.go @@ -22,6 +22,7 @@ import ( "github.com/google/cel-go/cel" "github.com/google/cel-go/checker" "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/cost" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/common/types/traits" @@ -982,6 +983,6 @@ func estimateMathListCost(estimator checker.CostEstimator, target *checker.AstNo func trackMathListCost(args []ref.Val, _ ref.Val) *uint64 { sz := actualSize(args[0]) - cost := safeAdd(sz, callCost) - return &cost + total := cost.SafeAdd(sz, callCost) + return &total } diff --git a/ext/network.go b/ext/network.go index bca065707..1115e2e04 100644 --- a/ext/network.go +++ b/ext/network.go @@ -23,6 +23,7 @@ import ( "github.com/google/cel-go/cel" "github.com/google/cel-go/checker" "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/cost" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/interpreter" @@ -766,13 +767,13 @@ func estimateNetworkContainsCIDRStringCost(estimator checker.CostEstimator, targ // Runtime cost tracking functions for network extensions. func trackNetworkParseCost(args []ref.Val, result ref.Val) *uint64 { - cost := uint64(math.Ceil(float64(actualSize(args[0])) * stringCostFactor)) - return &cost + total := cost.SafeMultiplyByFactor(actualSize(args[0]), stringCostFactor) + return &total } func trackIPIsCanonicalCost(args []ref.Val, result ref.Val) *uint64 { - cost := uint64(math.Ceil(float64(actualSize(args[0])) * 2 * stringCostFactor)) - return &cost + total := cost.SafeMultiplyByFactor(actualSize(args[0]), 2*stringCostFactor) + return &total } func trackNetworkNominalCost(args []ref.Val, result ref.Val) *uint64 { @@ -781,30 +782,30 @@ func trackNetworkNominalCost(args []ref.Val, result ref.Val) *uint64 { func trackNetworkContainsIPIPCost(args []ref.Val, result ref.Val) *uint64 { cidrSize := actualSize(args[0]) - cost := uint64(math.Ceil(float64(cidrSize+cidrSize) * stringCostFactor)) - return &cost + total := cost.SafeMultiplyByFactor(cost.SafeAdd(cidrSize, cidrSize), stringCostFactor) + return &total } func trackNetworkContainsIPStringCost(args []ref.Val, result ref.Val) *uint64 { cidrSize := actualSize(args[0]) otherSize := actualSize(args[1]) - cost := uint64(math.Ceil(float64(cidrSize+cidrSize) * stringCostFactor)) - cost = safeAdd(cost, uint64(math.Ceil(float64(otherSize)*stringCostFactor))) - return &cost + total := cost.SafeMultiplyByFactor(cost.SafeAdd(cidrSize, cidrSize), stringCostFactor) + total = cost.SafeAdd(total, cost.SafeMultiplyByFactor(otherSize, stringCostFactor)) + return &total } func trackNetworkContainsCIDRCIDRCost(args []ref.Val, result ref.Val) *uint64 { cidrSize := actualSize(args[0]) - cost := uint64(math.Ceil(float64(cidrSize+cidrSize) * stringCostFactor)) - cost = safeAdd(cost, uint64(math.Ceil(float64(cidrSize)*stringCostFactor)), 1) - return &cost + total := cost.SafeMultiplyByFactor(cost.SafeAdd(cidrSize, cidrSize), stringCostFactor) + total = cost.SafeAdd(total, cost.SafeMultiplyByFactor(cidrSize, stringCostFactor), 1) + return &total } func trackNetworkContainsCIDRStringCost(args []ref.Val, result ref.Val) *uint64 { cidrSize := actualSize(args[0]) otherSize := actualSize(args[1]) - cost := uint64(math.Ceil(float64(cidrSize+cidrSize) * stringCostFactor)) - cost = safeAdd(cost, uint64(math.Ceil(float64(cidrSize)*stringCostFactor)), 1) - cost = safeAdd(cost, uint64(math.Ceil(float64(otherSize)*stringCostFactor))) - return &cost + total := cost.SafeMultiplyByFactor(cost.SafeAdd(cidrSize, cidrSize), stringCostFactor) + total = cost.SafeAdd(total, cost.SafeMultiplyByFactor(cidrSize, stringCostFactor), 1) + total = cost.SafeAdd(total, cost.SafeMultiplyByFactor(otherSize, stringCostFactor)) + return &total } diff --git a/ext/regex.go b/ext/regex.go index bd222f170..e59fee00e 100644 --- a/ext/regex.go +++ b/ext/regex.go @@ -25,6 +25,7 @@ import ( "github.com/google/cel-go/cel" "github.com/google/cel-go/checker" "github.com/google/cel-go/common" + "github.com/google/cel-go/common/cost" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/interpreter" @@ -411,8 +412,8 @@ func estimateReplaceCost() checker.FunctionEstimator { func extractCostTracker() interpreter.FunctionTracker { return func(args []ref.Val, result ref.Val) *uint64 { - targetCost := float64(safeAdd(actualSize(args[0]), 1)) * common.StringTraversalCostFactor - regexCost := float64(safeAdd(actualSize(args[1]), 1)) * common.RegexStringLengthCostFactor + targetCost := float64(cost.SafeAdd(actualSize(args[0]), 1)) * common.StringTraversalCostFactor + regexCost := float64(cost.SafeAdd(actualSize(args[1]), 1)) * common.RegexStringLengthCostFactor // Actual search cost calculation = targetCost + regexCost searchCost := targetCost * regexCost // The total cost is the base call cost + search cost + result string allocation. diff --git a/ext/sets.go b/ext/sets.go index 63c019ad9..1235305f2 100644 --- a/ext/sets.go +++ b/ext/sets.go @@ -18,6 +18,7 @@ import ( "github.com/google/cel-go/cel" "github.com/google/cel-go/checker" "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/cost" "github.com/google/cel-go/common/operators" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" @@ -248,7 +249,7 @@ func trackSetsCost(costFactor float64) interpreter.FunctionTracker { return func(args []ref.Val, _ ref.Val) *uint64 { lhsSize := actualSize(args[0]) rhsSize := actualSize(args[1]) - cost := safeAdd(callCost, uint64(float64(lhsSize*rhsSize)*costFactor)) - return &cost + total := cost.SafeAdd(callCost, uint64(float64(lhsSize*rhsSize)*costFactor)) + return &total } } diff --git a/ext/strings.go b/ext/strings.go index 1f7732f2f..bdaa3a93d 100644 --- a/ext/strings.go +++ b/ext/strings.go @@ -30,6 +30,7 @@ import ( "github.com/google/cel-go/cel" "github.com/google/cel-go/checker" "github.com/google/cel-go/common" + "github.com/google/cel-go/common/cost" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/common/types/traits" @@ -972,7 +973,7 @@ func estimateStringReplaceCost(estimator checker.CostEstimator, target *checker. searchCost := atLeastOne(targetSize).Multiply(needleSize).MultiplyByCostFactor(stringCostFactor) replacementSize := estimateSize(estimator, args[1]).Add(fixedSizeEstimate(1)) - allReplacedSize := safeMul(safeAdd(targetSize.Max, 1), replacementSize.Max) + allReplacedSize := cost.SafeMultiply(cost.SafeAdd(targetSize.Max, 1), replacementSize.Max) resultMinSize := targetSize.Min if resultMinSize > replacementSize.Min { resultMinSize = replacementSize.Min @@ -1017,10 +1018,10 @@ func estimateStringJoinCost(estimator checker.CostEstimator, target *checker.Ast traversalCost := targetSize.Add(fixedSizeEstimate(1)).MultiplyByCostFactor(stringCostFactor) // Result size: sum of element sizes + (n-1) * separator size. // Worst case estimate: use list size * max element size + list size * separator size. - maxResultSize := safeAdd(safeMul(targetSize.Max, (safeAdd(1, sepSize.Max))), sepSize.Max) + maxResultSize := cost.SafeAdd(cost.SafeMultiply(targetSize.Max, cost.SafeAdd(1, sepSize.Max)), sepSize.Max) resultSize := rangedSizeEstimate(0, maxResultSize) - cost := traversalCost.Add(resultSize.MultiplyByCostFactor(1)).Add(callCostEstimate) - return callEstimate(cost, &resultSize) + estimate := traversalCost.Add(resultSize.MultiplyByCostFactor(1)).Add(callCostEstimate) + return callEstimate(estimate, &resultSize) } // Runtime cost tracking functions for string extensions. @@ -1030,24 +1031,23 @@ func estimateStringJoinCost(estimator checker.CostEstimator, target *checker.Ast // trackStringCharAtCost tracks runtime cost for O(n) string operations. func trackStringCharAtCost(args []ref.Val, result ref.Val) *uint64 { - size := float64(actualSize(args[0])) * stringCostFactor - cost := safeAdd(callCost, uint64(math.Ceil(size)), 1) - return &cost + total := cost.SafeAdd(callCost, cost.SafeMultiplyByFactor(actualSize(args[0]), stringCostFactor), 1) + return &total } // trackStringTransformCost tracks runtime cost for O(n) string operations. func trackStringTransformCost(args []ref.Val, result ref.Val) *uint64 { - transformCost := math.Ceil(float64(actualSize(args[0])) * stringCostFactor) + transformCost := cost.SafeMultiplyByFactor(actualSize(args[0]), stringCostFactor) resultSize := actualSize(result) - cost := safeAdd(callCost, uint64(transformCost), resultSize) - return &cost + total := cost.SafeAdd(callCost, transformCost, resultSize) + return &total } // trackStringSearchCost tracks runtime cost for O(n*m) string search operations. func trackStringSearchCost(args []ref.Val, _ ref.Val) *uint64 { - searchCost := float64(actualSize(args[0])*actualSize(args[1])) * stringCostFactor - cost := safeAdd(uint64(math.Ceil(searchCost)), callCost) - return &cost + searchSize := cost.SafeMultiply(actualSize(args[0]), actualSize(args[1])) + total := cost.SafeAdd(cost.SafeMultiplyByFactor(searchSize, stringCostFactor), callCost) + return &total } // trackStringReplaceCost tracks runtime cost for string replace operations, @@ -1061,24 +1061,24 @@ func trackStringReplaceCost(args []ref.Val, result ref.Val) *uint64 { if needleSize == 0 { needleSize = 1 } - searchCost := uint64(math.Ceil(float64(targetSize*needleSize) * stringCostFactor)) - cost := safeAdd(callCost, searchCost, actualSize(result)) - return &cost + searchCost := cost.SafeMultiplyByFactor(cost.SafeMultiply(targetSize, needleSize), stringCostFactor) + total := cost.SafeAdd(callCost, searchCost, actualSize(result)) + return &total } // trackStringSplitCost tracks runtime cost for string split operations, // accounting for traversal and list allocation. func trackStringSplitCost(args []ref.Val, result ref.Val) *uint64 { - traversalCost := float64(safeAdd(actualSize(args[0]), 1)) * stringCostFactor + traversalCost := cost.SafeMultiplyByFactor(cost.SafeAdd(actualSize(args[0]), 1), stringCostFactor) resultSize := actualSize(result) - cost := safeAdd(callCost, uint64(math.Ceil(traversalCost)), resultSize, common.ListCreateBaseCost) - return &cost + total := cost.SafeAdd(callCost, traversalCost, resultSize, common.ListCreateBaseCost) + return &total } // trackStringJoinCost tracks runtime cost for string join operations, // accounting for traversal and the size of the result. func trackStringJoinCost(args []ref.Val, result ref.Val) *uint64 { - traversalCost := float64(safeAdd(actualSize(args[0]), 1)) * stringCostFactor - cost := safeAdd(callCost, uint64(math.Ceil(traversalCost)), actualSize(result)) - return &cost + traversalCost := cost.SafeMultiplyByFactor(cost.SafeAdd(actualSize(args[0]), 1), stringCostFactor) + total := cost.SafeAdd(callCost, traversalCost, actualSize(result)) + return &total } diff --git a/interpreter/BUILD.bazel b/interpreter/BUILD.bazel index 40ac2ba69..8ee046e5c 100644 --- a/interpreter/BUILD.bazel +++ b/interpreter/BUILD.bazel @@ -28,6 +28,7 @@ go_library( "//common:go_default_library", "//common/ast:go_default_library", "//common/containers:go_default_library", + "//common/cost:go_default_library", "//common/functions:go_default_library", "//common/operators:go_default_library", "//common/overloads:go_default_library", diff --git a/interpreter/runtimecost.go b/interpreter/runtimecost.go index f9c525111..308b62581 100644 --- a/interpreter/runtimecost.go +++ b/interpreter/runtimecost.go @@ -16,9 +16,9 @@ package interpreter import ( "errors" - "math" "github.com/google/cel-go/common" + "github.com/google/cel-go/common/cost" "github.com/google/cel-go/common/overloads" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" @@ -254,21 +254,21 @@ func (c *CostTracker) ActualCost() uint64 { } func (c *CostTracker) costCall(call InterpretableCall, args []ref.Val, result ref.Val) uint64 { - var cost uint64 + var total uint64 if len(c.overloadTrackers) != 0 { if tracker, found := c.overloadTrackers[call.OverloadID()]; found { callCost := tracker(args, result) if callCost != nil { - cost = safeAdd(cost, *callCost) - return cost + total = cost.SafeAdd(total, *callCost) + return total } } } if c.Estimator != nil { callCost := c.Estimator.CallCost(call.Function(), call.OverloadID(), args, result) if callCost != nil { - cost = safeAdd(cost, *callCost) - return cost + total = cost.SafeAdd(total, *callCost) + return total } } // if user didn't specify, the default way of calculating runtime cost would be used. @@ -276,13 +276,13 @@ func (c *CostTracker) costCall(call InterpretableCall, args []ref.Val, result re switch call.OverloadID() { // O(n) functions case overloads.StartsWithString, overloads.EndsWithString: - cost = safeAdd(cost, uint64(math.Ceil(float64(actualSize(args[1]))*common.StringTraversalCostFactor))) + total = cost.SafeAdd(total, cost.SafeMultiplyByFactor(actualSize(args[1]), common.StringTraversalCostFactor)) case overloads.StringToBytes, overloads.BytesToString, overloads.ExtQuoteString, overloads.ExtFormatString: - cost = safeAdd(cost, uint64(math.Ceil(float64(actualSize(args[0]))*common.StringTraversalCostFactor))) + total = cost.SafeAdd(total, cost.SafeMultiplyByFactor(actualSize(args[0]), common.StringTraversalCostFactor)) case overloads.InList: // If a list is composed entirely of constant values this is O(1), but we don't account for that here. // We just assume all list containment checks are O(n). - cost = safeAdd(cost, actualSize(args[1])) + total = cost.SafeAdd(total, actualSize(args[1])) // O(min(m, n)) functions case overloads.LessString, overloads.GreaterString, overloads.LessEqualsString, overloads.GreaterEqualsString, overloads.LessBytes, overloads.GreaterBytes, overloads.LessEqualsBytes, overloads.GreaterEqualsBytes, @@ -293,28 +293,29 @@ func (c *CostTracker) costCall(call InterpretableCall, args []ref.Val, result re lhsSize := actualSize(args[0]) rhsSize := actualSize(args[1]) minSize := min(rhsSize, lhsSize) - cost = safeAdd(cost, uint64(math.Ceil(float64(minSize)*common.StringTraversalCostFactor))) + total = cost.SafeAdd(total, cost.SafeMultiplyByFactor(minSize, common.StringTraversalCostFactor)) // O(m+n) functions case overloads.AddString, overloads.AddBytes: // In the worst case scenario, we would need to reallocate a new backing store and copy both operands over. - cost = safeAdd(cost, uint64(math.Ceil(float64(actualSize(args[0])+actualSize(args[1]))*common.StringTraversalCostFactor))) + argSize := cost.SafeAdd(actualSize(args[0]), actualSize(args[1])) + total = cost.SafeAdd(total, cost.SafeMultiplyByFactor(argSize, common.StringTraversalCostFactor)) // O(nm) functions case overloads.Matches, overloads.MatchesString: // https://swtch.com/~rsc/regexp/regexp1.html applies to RE2 implementation supported by CEL // Add one to string length for purposes of cost calculation to prevent product of string and regex to be 0 // in case where string is empty but regex is still expensive. - strCost := uint64(math.Ceil((1.0 + float64(actualSize(args[0]))) * common.StringTraversalCostFactor)) + strCost := cost.SafeMultiplyByFactor(cost.SafeAdd(1, actualSize(args[0])), common.StringTraversalCostFactor) // We don't know how many expressions are in the regex, just the string length (a huge // improvement here would be to somehow get a count the number of expressions in the regex or // how many states are in the regex state machine and use that to measure regex cost). // For now, we're making a guess that each expression in a regex is typically at least 4 chars // in length. - regexCost := uint64(math.Ceil(float64(actualSize(args[1])) * common.RegexStringLengthCostFactor)) - cost = safeAdd(cost, strCost*regexCost) + regexCost := cost.SafeMultiplyByFactor(actualSize(args[1]), common.RegexStringLengthCostFactor) + total = cost.SafeAdd(total, cost.SafeMultiply(strCost, regexCost)) case overloads.ContainsString: - strCost := uint64(math.Ceil(float64(actualSize(args[0])) * common.StringTraversalCostFactor)) - substrCost := uint64(math.Ceil(float64(actualSize(args[1])) * common.StringTraversalCostFactor)) - cost = safeAdd(cost, strCost*substrCost) + strCost := cost.SafeMultiplyByFactor(actualSize(args[0]), common.StringTraversalCostFactor) + substrCost := cost.SafeMultiplyByFactor(actualSize(args[1]), common.StringTraversalCostFactor) + total = cost.SafeAdd(total, cost.SafeMultiply(strCost, substrCost)) default: // The following operations are assumed to have O(1) complexity. @@ -324,10 +325,10 @@ func (c *CostTracker) costCall(call InterpretableCall, args []ref.Val, result re // - Computing the size of strings, byte sequences, lists and maps. // - Logical operations and all operators on fixed width scalars (comparisons, equality) // - Any functions that don't have a declared cost either here or in provided ActualCostEstimator. - cost = safeAdd(cost, 1) + total = cost.SafeAdd(total, 1) } - return cost + return total } // actualSize returns the size of the value for all traits.Sizer values, a fixed size for all proto-based @@ -395,21 +396,3 @@ argloop: } return result, true } - -func safeAdd(x, y uint64, rest ...uint64) uint64 { - if y > 0 && x > math.MaxUint64-y { - return math.MaxUint64 - } - next := x + y - if len(rest) == 0 { - return next - } - return safeAdd(next, rest[0], rest[1:]...) -} - -func safeMul(x, y uint64) uint64 { - if y != 0 && x > math.MaxUint64/y { - return math.MaxUint64 - } - return x * y -} From f513cf83d763c2964bd2f1fe0bbe2f0cdc00f4c6 Mon Sep 17 00:00:00 2001 From: Tristan Swadell Date: Tue, 18 Aug 2026 14:39:05 -0700 Subject: [PATCH 31/37] Fix data race for aggregate size computation (#1423) The aggregate size memoization on immutable lists and maps is now accessed atomically since values may be shared across concurrent evaluations, and sizes from computations aborted at the calculator's depth or traversal limits are no longer memoized, as they depend on where in the traversal the value was encountered. EstimateAggregateSize additionally distinguishes genuine uint32 saturation from computations aborted at the limits. --- common/types/list.go | 21 ++++-- common/types/map.go | 18 +++-- common/types/size_calc.go | 58 +++++++++++++++- common/types/size_calc_test.go | 119 +++++++++++++++++++++++++++++++++ 4 files changed, 203 insertions(+), 13 deletions(-) diff --git a/common/types/list.go b/common/types/list.go index 483f71261..d3b1d3ac8 100644 --- a/common/types/list.go +++ b/common/types/list.go @@ -18,6 +18,7 @@ import ( "fmt" "reflect" "strings" + "sync/atomic" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" @@ -109,8 +110,12 @@ func NewMutableList(adapter Adapter) traits.MutableLister { // The `Adapter` enables native type to CEL type conversions. type baseList struct { Adapter - value any - size int + value any + size int + // aggSize memoizes the aggregate size computed by the first completed sizing of this + // list. Accessed atomically since immutable lists may be shared across concurrent + // evaluations; zero means not yet computed. See the SizeCalculator documentation for + // the memoization contract. aggSize uint32 get func(int) any } @@ -266,14 +271,16 @@ func (l *baseList) Size() ref.Val { // AggregateSize implements the AggregateSizeVisitor interface method. func (l *baseList) AggregateSize(sizer AggregateSizer) uint32 { - if l.aggSize != 0 { - return l.aggSize + if sz := atomic.LoadUint32(&l.aggSize); sz != 0 { + return sz } total := uint32(1) for i := range l.size { total = safeAddUint32(total, sizer.AggregateSize(l.get(i))) } - l.aggSize = total + if cacheableAggregateSize(sizer) { + atomic.StoreUint32(&l.aggSize, total) + } return total } @@ -330,13 +337,13 @@ func (l *mutableList) Add(other ref.Val) ref.Val { case *mutableList: l.mutableValues = append(l.mutableValues, otherList.mutableValues...) l.size += len(otherList.mutableValues) - l.aggSize = 0 + atomic.StoreUint32(&l.aggSize, 0) case traits.Lister: for i := IntZero; i < otherList.Size().(Int); i++ { l.size++ l.mutableValues = append(l.mutableValues, otherList.Get(i)) } - l.aggSize = 0 + atomic.StoreUint32(&l.aggSize, 0) default: return MaybeNoSuchOverloadErr(otherList) } diff --git a/common/types/map.go b/common/types/map.go index dc502323f..8be49254b 100644 --- a/common/types/map.go +++ b/common/types/map.go @@ -19,6 +19,7 @@ import ( "reflect" "sort" "strings" + "sync/atomic" "unicode" "google.golang.org/protobuf/proto" @@ -142,7 +143,12 @@ type baseMap struct { // value is the native Go value upon which the map type operators. value any - size int + size int + + // aggSize memoizes the aggregate size computed by the first completed sizing of this + // map. Accessed atomically since immutable maps may be shared across concurrent + // evaluations; zero means not yet computed. See the SizeCalculator documentation for + // the memoization contract. aggSize uint32 } @@ -305,12 +311,14 @@ func (m *baseMap) Size() ref.Val { // AggregateSize implements the AggregateSizeVisitor interface method. func (m *baseMap) AggregateSize(sizer AggregateSizer) uint32 { - if m.aggSize != 0 { - return m.aggSize + if sz := atomic.LoadUint32(&m.aggSize); sz != 0 { + return sz } f := foldableAggregateSizer{sizer: sizer, total: 1} m.Fold(&f) - m.aggSize = f.total + if cacheableAggregateSize(sizer) { + atomic.StoreUint32(&m.aggSize, f.total) + } return f.total } @@ -392,7 +400,7 @@ func (m *mutableMap) Insert(k, v ref.Val) ref.Val { } m.mutableValues[k] = v m.size++ - m.aggSize = 0 + atomic.StoreUint32(&m.aggSize, 0) return m } diff --git a/common/types/size_calc.go b/common/types/size_calc.go index a5fdbae69..e3ded0263 100644 --- a/common/types/size_calc.go +++ b/common/types/size_calc.go @@ -66,6 +66,15 @@ func SizeCalculatorStringUnitLength(length int) SizeCalculatorOption { } // SizeCalculator calculates the recursive element size of values. +// +// Aggregate values may memoize their computed size on first calculation as an optimization +// for repeated sizing of shared structures. The memoized size reflects the configuration of +// the calculator which first sized the value; hosts requiring differently configured +// calculators, e.g. distinct depth or traversal limits, should not share value instances +// across them. Memoized totals are also a snapshot of the value's contents at first sizing: +// hosts which mutate data underlying a sized aggregate, e.g. a proto message held as a list +// element, will observe the total computed before the mutation. Sizes computed from +// calculations aborted at the depth or traversal limits are never memoized. type SizeCalculator struct { version int maxDepth int @@ -96,6 +105,7 @@ type sizeContext struct { calc *SizeCalculator depth int traversalCount *int + limitExceeded *bool } func (c sizeContext) childContext() sizeContext { @@ -106,21 +116,67 @@ func (c sizeContext) childContext() sizeContext { func (c sizeContext) visitNode() bool { *c.traversalCount++ if *c.traversalCount > c.calc.maxTraversal || c.depth > c.calc.maxDepth { + *c.limitExceeded = true return false } return true } +// aggregateSizeStatus exposes whether the in-flight size computation has exceeded the +// calculator's depth or traversal limits. +type aggregateSizeStatus interface { + aggregateSizeLimitExceeded() bool +} + +// aggregateSizeLimitExceeded implements the aggregateSizeStatus interface method. +func (c sizeContext) aggregateSizeLimitExceeded() bool { + return *c.limitExceeded +} + +// cacheableAggregateSize reports whether a size computed with the given sizer is safe to +// memoize on the value. Only totals from computations which verifiably stayed within the +// calculator's depth and traversal limits are stable properties of the value; totals from +// aborted computations depend on where in the traversal the value was encountered and would +// poison the memoized size. +func cacheableAggregateSize(sizer AggregateSizer) bool { + status, ok := sizer.(aggregateSizeStatus) + return ok && !status.aggregateSizeLimitExceeded() +} + +// AggregateSizeEstimate captures the outcome of an aggregate size computation. +// +// The Size saturates at math.MaxUint32 when the accumulated element count overflows uint32. +// LimitExceeded reports the computation was aborted because the value was too expensive to +// traverse (too deep, or too many nodes visited); in that case Size is also math.MaxUint32, +// but the value's true size may be smaller — the two conditions are distinguishable by the flag. +type AggregateSizeEstimate struct { + Size uint32 + LimitExceeded bool +} + // AggregateSize returns the size of the input value, if known. // Otherwise, a unit size of 1 is returned. +// +// When the calculator's depth or traversal limits are exceeded, the size saturates to +// math.MaxUint32. Use EstimateAggregateSize to distinguish limit-exceeded results from +// genuine uint32 saturation. func (s *SizeCalculator) AggregateSize(val any) uint32 { + return s.EstimateAggregateSize(val).Size +} + +// EstimateAggregateSize returns the aggregate size of the input value along with an indication +// of whether the computation was aborted due to the calculator's depth or traversal limits. +func (s *SizeCalculator) EstimateAggregateSize(val any) AggregateSizeEstimate { traversals := 0 + exceeded := false ctx := sizeContext{ calc: s, depth: 1, traversalCount: &traversals, + limitExceeded: &exceeded, } - return ctx.AggregateSize(val) + size := ctx.AggregateSize(val) + return AggregateSizeEstimate{Size: size, LimitExceeded: exceeded} } // stringSize converts a byte length to an element count where stringUnitLength bytes count diff --git a/common/types/size_calc_test.go b/common/types/size_calc_test.go index 9a65dfc46..ab1438d7a 100644 --- a/common/types/size_calc_test.go +++ b/common/types/size_calc_test.go @@ -18,6 +18,7 @@ import ( "fmt" "math" "reflect" + "sync" "testing" "time" @@ -843,3 +844,121 @@ func TestSizeCalculatorStringUnitLength(t *testing.T) { }) } } + +func TestEstimateAggregateSize(t *testing.T) { + adapter := DefaultTypeAdapter + + t.Run("within_limits", func(t *testing.T) { + calc := NewSizeCalculator() + est := calc.EstimateAggregateSize(NewRefValList(adapter, []ref.Val{Int(1), Int(2)})) + if est.Size != 3 || est.LimitExceeded { + t.Errorf("EstimateAggregateSize() got %+v, want {Size: 3, LimitExceeded: false}", est) + } + }) + + t.Run("traversal_limit_exceeded", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxTraversal(2)) + list := NewRefValList(adapter, []ref.Val{Int(1), Int(2), Int(3)}) + est := calc.EstimateAggregateSize(list) + if est.Size != math.MaxUint32 || !est.LimitExceeded { + t.Errorf("EstimateAggregateSize() got %+v, want {Size: MaxUint32, LimitExceeded: true}", est) + } + }) + + t.Run("depth_limit_exceeded", func(t *testing.T) { + calc := NewSizeCalculator(SizeCalculatorMaxDepth(1)) + list := NewRefValList(adapter, []ref.Val{NewRefValList(adapter, []ref.Val{Int(1)})}) + est := calc.EstimateAggregateSize(list) + if est.Size != math.MaxUint32 || !est.LimitExceeded { + t.Errorf("EstimateAggregateSize() got %+v, want {Size: MaxUint32, LimitExceeded: true}", est) + } + }) + + t.Run("saturation_without_limit", func(t *testing.T) { + // Two custom sizers each reporting MaxUint32 elements saturate the sum without + // tripping the depth or traversal limits. + calc := NewSizeCalculator() + val := struct{ A, B traits.Sizer }{ + A: customSizerVal(math.MaxUint32), + B: customSizerVal(math.MaxUint32), + } + est := calc.EstimateAggregateSize(val) + if est.Size != math.MaxUint32 || est.LimitExceeded { + t.Errorf("EstimateAggregateSize() got %+v, want {Size: MaxUint32, LimitExceeded: false}", est) + } + }) +} + +func TestAggregateSizeConcurrentAccess(t *testing.T) { + // Immutable lists and maps may be shared across concurrent evaluations; the aggregate + // size memoization must be race-free (validated under `go test -race`). + adapter := DefaultTypeAdapter + sharedList := NewRefValList(adapter, []ref.Val{Int(1), Int(2), Int(3)}) + sharedMap := NewRefValMap(adapter, map[ref.Val]ref.Val{String("k"): String("v")}) + var wg sync.WaitGroup + for i := 0; i < 8; i++ { + wg.Add(1) + go func() { + defer wg.Done() + calc := NewSizeCalculator() + if got := calc.AggregateSize(sharedList); got != 4 { + t.Errorf("AggregateSize(list) got %d, want 4", got) + } + if got := calc.AggregateSize(sharedMap); got != 3 { + t.Errorf("AggregateSize(map) got %d, want 3", got) + } + }() + } + wg.Wait() +} + +func TestAggregateSizeAbortedComputationNotMemoized(t *testing.T) { + // A sizing aborted at the calculator's limits depends on where in the traversal the + // value was encountered and must not be memoized: a later sizing within limits must + // return the true size with no limit-exceeded signal. + adapter := DefaultTypeAdapter + + t.Run("list", func(t *testing.T) { + shared := NewRefValList(adapter, []ref.Val{Int(1), Int(2), Int(3), Int(4), Int(5)}) + strict := NewSizeCalculator(SizeCalculatorMaxTraversal(2)) + if est := strict.EstimateAggregateSize(shared); !est.LimitExceeded { + t.Fatalf("strict EstimateAggregateSize() got %+v, want LimitExceeded", est) + } + generous := NewSizeCalculator() + est := generous.EstimateAggregateSize(shared) + if est.Size != 6 || est.LimitExceeded { + t.Errorf("generous EstimateAggregateSize() got %+v, want {Size: 6, LimitExceeded: false}", est) + } + }) + + t.Run("map", func(t *testing.T) { + shared := NewRefValMap(adapter, map[ref.Val]ref.Val{ + Int(1): Int(2), + Int(3): Int(4), + }) + strict := NewSizeCalculator(SizeCalculatorMaxTraversal(2)) + if est := strict.EstimateAggregateSize(shared); !est.LimitExceeded { + t.Fatalf("strict EstimateAggregateSize() got %+v, want LimitExceeded", est) + } + generous := NewSizeCalculator() + est := generous.EstimateAggregateSize(shared) + if est.Size != 5 || est.LimitExceeded { + t.Errorf("generous EstimateAggregateSize() got %+v, want {Size: 5, LimitExceeded: false}", est) + } + }) + + t.Run("completed_computation_is_memoized", func(t *testing.T) { + shared := NewRefValList(adapter, []ref.Val{Int(1), Int(2)}) + calc := NewSizeCalculator() + if got := calc.AggregateSize(shared); got != 3 { + t.Fatalf("AggregateSize() got %d, want 3", got) + } + // A subsequent sizing under a stricter budget serves the memoized total rather + // than recomputing (and aborting). + strict := NewSizeCalculator(SizeCalculatorMaxTraversal(2)) + est := strict.EstimateAggregateSize(shared) + if est.Size != 3 || est.LimitExceeded { + t.Errorf("strict EstimateAggregateSize() after memoization got %+v, want {Size: 3, LimitExceeded: false}", est) + } + }) +} From b02bd0a2ced3b32f53c2483394398869d554be04 Mon Sep 17 00:00:00 2001 From: dplotnikov Date: Tue, 18 Aug 2026 15:28:52 -0700 Subject: [PATCH 32/37] Fold by-category benchmark tests into parser_test.go --- parser/BUILD.bazel | 1 + parser/bench/BUILD.bazel | 41 ------- parser/bench/bench.go | 173 -------------------------- parser/bench/bench_test.go | 106 ---------------- parser/parser_test.go | 243 +++++++++++++++++++++++++++++++++++++ 5 files changed, 244 insertions(+), 320 deletions(-) delete mode 100644 parser/bench/BUILD.bazel delete mode 100644 parser/bench/bench.go delete mode 100644 parser/bench/bench_test.go diff --git a/parser/BUILD.bazel b/parser/BUILD.bazel index 97bc9bd43..4a102840f 100644 --- a/parser/BUILD.bazel +++ b/parser/BUILD.bazel @@ -48,6 +48,7 @@ go_test( deps = [ "//common/ast:go_default_library", "//common/debug:go_default_library", + "//common/operators:go_default_library", "//common/types:go_default_library", "//parser/gen:go_default_library", "//test:go_default_library", diff --git a/parser/bench/BUILD.bazel b/parser/bench/BUILD.bazel deleted file mode 100644 index e97556fd6..000000000 --- a/parser/bench/BUILD.bazel +++ /dev/null @@ -1,41 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -package( - licenses = ["notice"], # Apache 2.0 -) - -go_library( - name = "go_default_library", - srcs = [ - "bench.go", - ], - importpath = "github.com/google/cel-go/parser/bench", - visibility = ["//visibility:public"], - deps = [ - "//common:go_default_library", - "//common/ast:go_default_library", - "//common/operators:go_default_library", - "//common/types:go_default_library", - "//parser:go_default_library", - ], -) - -go_test( - name = "bench_test", - size = "small", - srcs = [ - "bench_test.go", - ], - embed = [ - ":go_default_library", - ], - deps = [ - "//common:go_default_library", - "//parser:go_default_library", - ], -) - -alias( - name = "go_default_test", - actual = ":bench_test", -) diff --git a/parser/bench/bench.go b/parser/bench/bench.go deleted file mode 100644 index 1d03fc961..000000000 --- a/parser/bench/bench.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2026 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 bench defines benchmark test cases and utilities for CEL parsers. -package bench - -import ( - "strings" - - "github.com/google/cel-go/common" - "github.com/google/cel-go/common/ast" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/parser" -) - -// ParseResult indicates whether a parse is expected to succeed or fail. -type ParseResult int - -const ( - // ParseResultSuccess indicates an expression that is expected to parse without error. - ParseResultSuccess ParseResult = iota - // ParseResultError indicates an expression that is expected to produce parse error(s). - ParseResultError -) - -// TestCase represents an expression to parse and the expected parse result. -type TestCase struct { - Expr string - Result ParseResult -} - -// ErrorCase returns a TestCase expecting a parse error. -func ErrorCase(expr string) TestCase { - return TestCase{ - Expr: expr, - Result: ParseResultError, - } -} - -// SuccessCase returns a TestCase expecting successful parsing. -func SuccessCase(expr string) TestCase { - return TestCase{ - Expr: expr, - Result: ParseResultSuccess, - } -} - -// Category represents a named group of test cases for benchmarking and verification. -type Category struct { - Name string - Cases []TestCase -} - -// OptMapMacro expands `m.optMap(v, f)` into a conditional comprehension. -var OptMapMacro = parser.NewReceiverMacro("optMap", 2, optMapExpander) - -func optMapExpander(meh parser.ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) { - varIdent := args[0] - varName := "" - switch varIdent.Kind() { - case ast.IdentKind: - varName = varIdent.AsIdent() - default: - return nil, meh.NewError(varIdent.ID(), "optMap() variable name must be a simple identifier") - } - mapExpr := args[1] - return meh.NewCall( - operators.Conditional, - meh.NewMemberCall("hasValue", target), - meh.NewCall("optional.of", - meh.NewComprehension( - meh.NewList(), - "#unused", - varName, - meh.NewMemberCall("value", meh.Copy(target)), - meh.NewLiteral(types.False), - meh.NewIdent(varName), - mapExpr, - ), - ), - meh.NewCall("optional.none"), - ), nil -} - -// GetCategories returns benchmark and correctness test cases organized by category. -func GetCategories() []Category { - return categories -} - -// GetTestCases returns benchmark and correctness test cases flattened across all categories. -func GetTestCases() []TestCase { - var allCases []TestCase - for _, cat := range categories { - allCases = append(allCases, cat.Cases...) - } - return allCases -} - -var categories = []Category{ - // Simple: common, representative CEL expressions covering basic syntax, operators, calls, and literals - { - Name: "Simple", - Cases: []TestCase{ - SuccessCase("x * 2 + y / 3"), - SuccessCase(`foo.bar.baz(1, 2, "abc")`), - SuccessCase(`a > 5 && b < 10 || c == "xyz"`), - SuccessCase("x ? y : z"), - SuccessCase(`{"foo": 1, "bar": [2, 3]}`), - SuccessCase("a[b]"), - SuccessCase("a.b.c"), - SuccessCase("a.`b-c`"), - SuccessCase("\"\\a\\b\\f\\n\\r\\t\\v'\\\"\\\\ Legal escapes \\u2764\""), - }, - }, - - // Complex: expressions with deep chaining, nesting, precedence, and complex structures - { - Name: "Complex", - Cases: []TestCase{ - SuccessCase("a" + strings.Repeat(" + a", 49)), - SuccessCase("a" + strings.Repeat(" || a", 49)), - SuccessCase("a" + strings.Repeat(".f", 49)), - SuccessCase(strings.Repeat("(", 20) + "a" + strings.Repeat(")", 20)), - SuccessCase(`SomeMessage{foo: 5, bar: "xyz"}`), - SuccessCase("1 + 2 * 3 - 1 / 2 == 6 % 1"), - SuccessCase("[] + [1, 2, 3] + [4]"), - }, - }, - - // Macros: standard and receiver comprehension macros, optional syntax traversal - { - Name: "Macros", - Cases: []TestCase{ - SuccessCase("has(m.f)"), - SuccessCase("[1, 2, 3].all(x, x > 0)"), - SuccessCase("m.map(v, v * 2)"), - SuccessCase("m.filter(v, v > 0)"), - SuccessCase("m.exists_one(v, v == 1)"), - SuccessCase("x.filter(y, y.exists(z, has(z.a)))"), - SuccessCase("a.?b[?0] && a[?c]"), - SuccessCase("m.optMap(v, v + 1)"), - }, - }, - - // Errors: representative syntax errors, invalid tokens, keywords, and unclosed delimiters - { - Name: "Errors", - Cases: []TestCase{ - ErrorCase("x * 2 + y /"), - ErrorCase(`foo.bar.baz(1, 2, "abc"`), - ErrorCase("a > 5 && && b < 10"), - ErrorCase(`{"foo": 1, "bar": [2, 3`), - ErrorCase("1 + $"), - ErrorCase("break"), - ErrorCase(`"\xFh"`), - ErrorCase("a" + strings.Repeat(" + a", 49) + " +"), - ErrorCase(strings.Repeat("(", 20) + "a"), - ErrorCase("f(*" + strings.Repeat(", *", 9) + ")"), - }, - }, -} diff --git a/parser/bench/bench_test.go b/parser/bench/bench_test.go deleted file mode 100644 index d016d0955..000000000 --- a/parser/bench/bench_test.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2026 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 bench - -import ( - "fmt" - "testing" - - "github.com/google/cel-go/common" - "github.com/google/cel-go/parser" -) - -func newBenchmarkParser(tb testing.TB) *parser.Parser { - tb.Helper() - p, err := parser.NewParser( - parser.Macros(append(parser.AllMacros, OptMapMacro)...), - parser.EnableOptionalSyntax(true), - parser.EnableIdentEscapeSyntax(true), - parser.MaxRecursionDepth(512), - ) - if err != nil { - tb.Fatalf("parser.NewParser() failed: %v", err) - } - return p -} - -func TestExpectedResult(t *testing.T) { - p := newBenchmarkParser(t) - for _, cat := range GetCategories() { - t.Run(cat.Name, func(t *testing.T) { - for i, tc := range cat.Cases { - t.Run(fmt.Sprintf("%d_%s", i, tc.Expr), func(t *testing.T) { - src := common.NewTextSource(tc.Expr) - _, errs := p.Parse(src) - hasErr := len(errs.GetErrors()) > 0 - switch tc.Result { - case ParseResultSuccess: - if hasErr { - t.Errorf("p.Parse(%q) failed unexpectedly: %v", tc.Expr, errs.ToDisplayString()) - } - case ParseResultError: - if !hasErr { - t.Errorf("p.Parse(%q) succeeded unexpectedly, wanted error", tc.Expr) - } - } - }) - } - }) - } -} - -// BenchmarkParse benchmarks parsing organized by workload categories. -func BenchmarkParse(b *testing.B) { - p := newBenchmarkParser(b) - for _, cat := range GetCategories() { - b.Run(cat.Name, func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - for _, tc := range cat.Cases { - src := common.NewTextSource(tc.Expr) - _, errs := p.Parse(src) - hasErr := len(errs.GetErrors()) > 0 - expectedErr := tc.Result == ParseResultError - if hasErr != expectedErr { - b.Fatalf("p.Parse(%q) got error: %v, expected error: %v", tc.Expr, hasErr, expectedErr) - } - } - } - }) - } -} - -// BenchmarkParseParallel benchmarks parsing concurrently across goroutines by category. -func BenchmarkParseParallel(b *testing.B) { - p := newBenchmarkParser(b) - for _, cat := range GetCategories() { - b.Run(cat.Name, func(b *testing.B) { - b.ResetTimer() - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - for _, tc := range cat.Cases { - src := common.NewTextSource(tc.Expr) - _, errs := p.Parse(src) - hasErr := len(errs.GetErrors()) > 0 - expectedErr := tc.Result == ParseResultError - if hasErr != expectedErr { - b.Fatalf("p.Parse(%q) got error: %v, expected error: %v", tc.Expr, hasErr, expectedErr) - } - } - } - }) - }) - } -} diff --git a/parser/parser_test.go b/parser/parser_test.go index 88527d813..4a453a197 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -24,6 +24,7 @@ import ( "github.com/google/cel-go/common" "github.com/google/cel-go/common/ast" "github.com/google/cel-go/common/debug" + "github.com/google/cel-go/common/operators" "github.com/google/cel-go/common/types" "github.com/google/cel-go/test" ) @@ -2393,6 +2394,248 @@ func BenchmarkParseParallel(b *testing.B) { }) } +type benchTestInfo struct { + // I contains the input expression to be parsed. + I string + + // E indicates whether an error is expected. + E bool +} + +type benchCategory struct { + name string + cases []benchTestInfo +} + +var benchCategories = []benchCategory{ + // Simple: common, representative CEL expressions covering basic syntax, operators, calls, and literals + { + name: "Simple", + cases: []benchTestInfo{ + { + I: "x * 2 + y / 3", + }, + { + I: `foo.bar.baz(1, 2, "abc")`, + }, + { + I: `a > 5 && b < 10 || c == "xyz"`, + }, + { + I: "x ? y : z", + }, + { + I: `{"foo": 1, "bar": [2, 3]}`, + }, + { + I: "a[b]", + }, + { + I: "a.b.c", + }, + { + I: "a.`b-c`", + }, + { + I: "\"\\a\\b\\f\\n\\r\\t\\v'\\\"\\\\ Legal escapes \\u2764\"", + }, + }, + }, + + // Complex: expressions with deep chaining, nesting, precedence, and complex structures + { + name: "Complex", + cases: []benchTestInfo{ + { + I: "a" + strings.Repeat(" + a", 49), + }, + { + I: "a" + strings.Repeat(" || a", 49), + }, + { + I: "a" + strings.Repeat(".f", 49), + }, + { + I: strings.Repeat("(", 20) + "a" + strings.Repeat(")", 20), + }, + { + I: `SomeMessage{foo: 5, bar: "xyz"}`, + }, + { + I: "1 + 2 * 3 - 1 / 2 == 6 % 1", + }, + { + I: "[] + [1, 2, 3] + [4]", + }, + }, + }, + + // Macros: standard and receiver comprehension macros, optional syntax traversal + { + name: "Macros", + cases: []benchTestInfo{ + { + I: "has(m.f)", + }, + { + I: "[1, 2, 3].all(x, x > 0)", + }, + { + I: "m.map(v, v * 2)", + }, + { + I: "m.filter(v, v > 0)", + }, + { + I: "m.exists_one(v, v == 1)", + }, + { + I: "x.filter(y, y.exists(z, has(z.a)))", + }, + { + I: "a.?b[?0] && a[?c]", + }, + { + I: "m.optMap(v, v + 1)", + }, + }, + }, + + // Errors: representative syntax errors, invalid tokens, keywords, and unclosed delimiters + { + name: "Errors", + cases: []benchTestInfo{ + { + I: "x * 2 + y /", + E: true, + }, + { + I: `foo.bar.baz(1, 2, "abc"`, + E: true, + }, + { + I: "a > 5 && && b < 10", + E: true, + }, + { + I: `{"foo": 1, "bar": [2, 3`, + E: true, + }, + { + I: "1 + $", + E: true, + }, + { + I: "break", + E: true, + }, + { + I: `"\xFh"`, + E: true, + }, + { + I: "a" + strings.Repeat(" + a", 49) + " +", + E: true, + }, + { + I: strings.Repeat("(", 20) + "a", + E: true, + }, + { + I: "f(*" + strings.Repeat(", *", 9) + ")", + E: true, + }, + }, + }, +} + +// BenchmarkByCategory benchmarks parsing organized by workload categories. +func BenchmarkByCategory(b *testing.B) { + p := newBenchmarkCategoryParser(b) + for _, cat := range benchCategories { + b.Run(cat.name, func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, tc := range cat.cases { + src := common.NewTextSource(tc.I) + _, errs := p.Parse(src) + hasErr := len(errs.GetErrors()) > 0 + if hasErr != tc.E { + b.Fatalf("p.Parse(%q) got error: %v, expected error: %v", tc.I, hasErr, tc.E) + } + } + } + }) + } +} + +// BenchmarkParallelByCategory benchmarks parsing concurrently across goroutines by category. +func BenchmarkParallelByCategory(b *testing.B) { + p := newBenchmarkCategoryParser(b) + for _, cat := range benchCategories { + b.Run(cat.name, func(b *testing.B) { + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + for _, tc := range cat.cases { + src := common.NewTextSource(tc.I) + _, errs := p.Parse(src) + hasErr := len(errs.GetErrors()) > 0 + if hasErr != tc.E { + b.Fatalf("p.Parse(%q) got error: %v, expected error: %v", tc.I, hasErr, tc.E) + } + } + } + }) + }) + } +} + +// optMapMacro expands `m.optMap(v, f)` into a conditional comprehension. +var optMapMacro = NewReceiverMacro("optMap", 2, optMapExpander) + +func optMapExpander(meh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) { + varIdent := args[0] + varName := "" + switch varIdent.Kind() { + case ast.IdentKind: + varName = varIdent.AsIdent() + default: + return nil, meh.NewError(varIdent.ID(), "optMap() variable name must be a simple identifier") + } + mapExpr := args[1] + return meh.NewCall( + operators.Conditional, + meh.NewMemberCall("hasValue", target), + meh.NewCall("optional.of", + meh.NewComprehension( + meh.NewList(), + "#unused", + varName, + meh.NewMemberCall("value", meh.Copy(target)), + meh.NewLiteral(types.False), + meh.NewIdent(varName), + mapExpr, + ), + ), + meh.NewCall("optional.none"), + ), nil +} + +func newBenchmarkCategoryParser(tb testing.TB) *Parser { + tb.Helper() + p, err := NewParser( + Macros(append(AllMacros, optMapMacro)...), + EnableOptionalSyntax(true), + EnableIdentEscapeSyntax(true), + MaxRecursionDepth(512), + ) + if err != nil { + tb.Fatalf("NewParser() failed: %v", err) + } + return p +} + func TestParseErrorData(t *testing.T) { p := newTestParser(t) src := common.NewTextSource(`a.?b`) From aadbbf98d7e37886a6bde42432ab9846a7fc2a29 Mon Sep 17 00:00:00 2001 From: dplotnikov Date: Wed, 19 Aug 2026 09:48:43 -0700 Subject: [PATCH 33/37] Skip policy conformance tests when run outside Bazel --- conformance/policy/policy_conformance_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/conformance/policy/policy_conformance_test.go b/conformance/policy/policy_conformance_test.go index 21966df04..c9a1d9966 100644 --- a/conformance/policy/policy_conformance_test.go +++ b/conformance/policy/policy_conformance_test.go @@ -17,7 +17,6 @@ package policy_conformance_test import ( "flag" "fmt" - "log" "os" "path/filepath" "strings" @@ -175,7 +174,7 @@ func k8sParserOpts() policy.ParserOption { func TestConformance(t *testing.T) { absTestdataDir, err := rlocation(testdataDir) if err != nil { - log.Fatalf("rlocation(%q) failed: %v", testdataDir, err) + t.Skipf("rlocation(%q) failed: %v", testdataDir, err) } testDirs := ([]string)(tests) From 78514224a5fd1d4a43e92c1c74567a2c8d9ac6a5 Mon Sep 17 00:00:00 2001 From: dplotnikov Date: Wed, 19 Aug 2026 09:55:06 -0700 Subject: [PATCH 34/37] Revert "Skip policy conformance tests when run outside Bazel" This reverts commit aadbbf98d7e37886a6bde42432ab9846a7fc2a29. --- conformance/policy/policy_conformance_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conformance/policy/policy_conformance_test.go b/conformance/policy/policy_conformance_test.go index c9a1d9966..21966df04 100644 --- a/conformance/policy/policy_conformance_test.go +++ b/conformance/policy/policy_conformance_test.go @@ -17,6 +17,7 @@ package policy_conformance_test import ( "flag" "fmt" + "log" "os" "path/filepath" "strings" @@ -174,7 +175,7 @@ func k8sParserOpts() policy.ParserOption { func TestConformance(t *testing.T) { absTestdataDir, err := rlocation(testdataDir) if err != nil { - t.Skipf("rlocation(%q) failed: %v", testdataDir, err) + log.Fatalf("rlocation(%q) failed: %v", testdataDir, err) } testDirs := ([]string)(tests) From 238dcd9bef9765614a1d981458b7fdb72ee707a2 Mon Sep 17 00:00:00 2001 From: Maria Ines Parnisari Date: Wed, 19 Aug 2026 14:07:02 -0700 Subject: [PATCH 35/37] Revert authzed rename for upstream sync --- README.md | 12 ++++---- cel/BUILD.bazel | 2 +- cel/async/BUILD.bazel | 2 +- cel/async/async.go | 10 +++---- cel/async/async_test.go | 10 +++---- cel/cel_example_test.go | 6 ++-- cel/cel_test.go | 26 ++++++++--------- cel/decls.go | 10 +++---- cel/decls_test.go | 18 ++++++------ cel/env.go | 26 ++++++++--------- cel/env_test.go | 16 +++++------ cel/fieldpaths.go | 4 +-- cel/folding.go | 12 ++++---- cel/folding_test.go | 12 ++++---- cel/inlining.go | 12 ++++---- cel/inlining_test.go | 4 +-- cel/io.go | 12 ++++---- cel/io_test.go | 12 ++++---- cel/library.go | 24 ++++++++-------- cel/macro.go | 8 +++--- cel/macro_test.go | 4 +-- cel/optimizer.go | 8 +++--- cel/optimizer_test.go | 8 +++--- cel/options.go | 22 +++++++-------- cel/program.go | 12 ++++---- cel/program_async_test.go | 14 +++++----- cel/prompt.go | 8 +++--- cel/prompt_test.go | 4 +-- cel/validator.go | 6 ++-- cel/validator_test.go | 14 +++++----- checker/BUILD.bazel | 2 +- checker/checker.go | 14 +++++----- checker/checker_test.go | 22 +++++++-------- checker/cost.go | 10 +++---- checker/cost_test.go | 16 +++++------ checker/decls/BUILD.bazel | 2 +- checker/env.go | 10 +++---- checker/env_test.go | 12 ++++---- checker/errors.go | 6 ++-- checker/format.go | 4 +-- checker/format_test.go | 4 +-- checker/mapping.go | 2 +- checker/printer.go | 4 +-- checker/scopes.go | 2 +- checker/types.go | 2 +- codelab/README.md | 4 +-- codelab/codelab.go | 8 +++--- codelab/go.mod | 6 ++-- codelab/solution/codelab.go | 8 +++--- common/BUILD.bazel | 2 +- common/ast/BUILD.bazel | 2 +- common/ast/ast.go | 6 ++-- common/ast/ast_test.go | 10 +++---- common/ast/conversion.go | 4 +-- common/ast/conversion_test.go | 16 +++++------ common/ast/expr.go | 2 +- common/ast/expr_test.go | 6 ++-- common/ast/factory.go | 2 +- common/ast/navigable.go | 4 +-- common/ast/navigable_test.go | 20 ++++++------- common/containers/BUILD.bazel | 2 +- common/containers/container.go | 2 +- common/containers/container_test.go | 2 +- common/debug/BUILD.bazel | 2 +- common/debug/debug.go | 6 ++-- common/decls/BUILD.bazel | 2 +- common/decls/decls.go | 12 ++++---- common/decls/decls_test.go | 14 +++++----- common/env/BUILD.bazel | 2 +- common/env/env.go | 4 +-- common/env/env_test.go | 10 +++---- common/functions/BUILD.bazel | 2 +- common/functions/functions.go | 2 +- common/operators/BUILD.bazel | 2 +- common/overloads/BUILD.bazel | 2 +- common/runes/BUILD.bazel | 2 +- common/source.go | 2 +- common/stdlib/BUILD.bazel | 2 +- common/stdlib/standard.go | 16 +++++------ common/types/BUILD.bazel | 2 +- common/types/bool.go | 2 +- common/types/bytes.go | 2 +- common/types/compare.go | 2 +- common/types/double.go | 2 +- common/types/double_test.go | 4 +-- common/types/duration.go | 4 +-- common/types/duration_test.go | 4 +-- common/types/err.go | 2 +- common/types/format.go | 4 +-- common/types/int.go | 2 +- common/types/int_test.go | 4 +-- common/types/iterator.go | 4 +-- common/types/json_list_test.go | 2 +- common/types/list.go | 4 +-- common/types/list_test.go | 4 +-- common/types/map.go | 6 ++-- common/types/map_test.go | 8 +++--- common/types/null.go | 2 +- common/types/null_test.go | 2 +- common/types/object.go | 4 +-- common/types/object_test.go | 4 +-- common/types/optional.go | 2 +- common/types/optional_test.go | 2 +- common/types/pb/BUILD.bazel | 2 +- common/types/pb/equal_test.go | 2 +- common/types/pb/file_test.go | 6 ++-- common/types/pb/pb_test.go | 4 +-- common/types/pb/type_test.go | 6 ++-- common/types/provider.go | 6 ++-- common/types/provider_test.go | 6 ++-- common/types/ref/BUILD.bazel | 2 +- common/types/string.go | 4 +-- common/types/string_test.go | 4 +-- common/types/timestamp.go | 4 +-- common/types/timestamp_test.go | 4 +-- common/types/traits/BUILD.bazel | 2 +- common/types/traits/comparer.go | 2 +- common/types/traits/container.go | 2 +- common/types/traits/field_tester.go | 2 +- common/types/traits/indexer.go | 2 +- common/types/traits/iterator.go | 2 +- common/types/traits/lister.go | 2 +- common/types/traits/mapper.go | 2 +- common/types/traits/matcher.go | 2 +- common/types/traits/math.go | 2 +- common/types/traits/receiver.go | 2 +- common/types/traits/sizer.go | 2 +- common/types/type_test.go | 2 +- common/types/types.go | 6 ++-- common/types/types_test.go | 4 +-- common/types/uint.go | 2 +- common/types/uint_test.go | 4 +-- common/types/unknown.go | 2 +- common/types/unknown_test.go | 2 +- common/types/util.go | 2 +- conformance/conformance_test.go | 12 ++++---- conformance/go.mod | 6 ++-- conformance/policy/policy_conformance_test.go | 12 ++++---- examples/README.md | 2 +- examples/example_cel_advanced_test.go | 2 +- examples/example_cel_collections_test.go | 2 +- examples/example_cel_compile_test.go | 4 +-- examples/example_cel_context_eval_test.go | 2 +- examples/example_cel_custom_functions_test.go | 6 ++-- examples/example_cel_custom_macros_test.go | 12 ++++---- examples/example_cel_execution_cost_test.go | 6 ++-- .../example_cel_logic_and_conditions_test.go | 2 +- examples/example_cel_native_structs_test.go | 4 +-- examples/example_cel_operators_test.go | 2 +- examples/example_cel_protocol_buffers_test.go | 4 +-- .../example_cel_strings_and_numbers_test.go | 4 +-- examples/example_cel_time_test.go | 2 +- .../example_cel_transforming_data_test.go | 2 +- examples/example_cel_type_conversions_test.go | 2 +- ext/BUILD.bazel | 2 +- ext/bindings.go | 12 ++++---- ext/bindings_test.go | 16 +++++------ ext/comprehensions.go | 14 +++++----- ext/comprehensions_test.go | 8 +++--- ext/costs.go | 12 ++++---- ext/encoders.go | 10 +++---- ext/encoders_test.go | 4 +-- ext/extension_option_factory.go | 4 +-- ext/extension_option_factory_test.go | 4 +-- ext/formatting.go | 12 ++++---- ext/formatting_test.go | 10 +++---- ext/formatting_v2.go | 10 +++---- ext/formatting_v2_test.go | 10 +++---- ext/guards.go | 6 ++-- ext/lists.go | 20 ++++++------- ext/lists_test.go | 8 +++--- ext/math.go | 14 +++++----- ext/math_test.go | 6 ++-- ext/native.go | 10 +++---- ext/native_test.go | 14 +++++----- ext/network.go | 12 ++++---- ext/network_test.go | 6 ++-- ext/protos.go | 4 +-- ext/protos_test.go | 12 ++++---- ext/regex.go | 12 ++++---- ext/regex_test.go | 4 +-- ext/sets.go | 16 +++++------ ext/sets_test.go | 10 +++---- ext/strings.go | 14 +++++----- ext/strings_test.go | 8 +++--- go.mod | 2 +- interpreter/BUILD.bazel | 2 +- interpreter/activation.go | 2 +- interpreter/activation_test.go | 4 +-- interpreter/async.go | 6 ++-- interpreter/async_test.go | 18 ++++++------ interpreter/attribute_patterns.go | 6 ++-- interpreter/attribute_patterns_test.go | 4 +-- interpreter/attributes.go | 8 +++--- interpreter/attributes_test.go | 18 ++++++------ interpreter/decorators.go | 8 +++--- interpreter/dispatcher.go | 2 +- interpreter/evalstate.go | 2 +- interpreter/frame.go | 6 ++-- interpreter/frame_test.go | 4 +-- interpreter/functions/BUILD.bazel | 2 +- interpreter/functions/functions.go | 2 +- interpreter/interpretable.go | 12 ++++---- interpreter/interpreter.go | 8 +++--- interpreter/interpreter_test.go | 28 +++++++++---------- interpreter/optimizations.go | 4 +-- interpreter/planner.go | 10 +++---- interpreter/prune.go | 12 ++++---- interpreter/prune_test.go | 20 ++++++------- interpreter/runtimecost.go | 10 +++---- interpreter/runtimecost_test.go | 18 ++++++------ parser/BUILD.bazel | 2 +- parser/errors.go | 2 +- parser/gen/BUILD.bazel | 2 +- parser/helper.go | 8 +++--- parser/helper_test.go | 4 +-- parser/input.go | 2 +- parser/macro.go | 10 +++---- parser/macro_test.go | 4 +-- parser/parser.go | 12 ++++---- parser/parser_test.go | 10 +++---- parser/unparser.go | 8 +++--- parser/unparser_test.go | 6 ++-- policy/BUILD.bazel | 2 +- policy/compiler.go | 14 +++++----- policy/compiler_test.go | 10 +++---- policy/composer.go | 10 +++---- policy/composer_test.go | 8 +++--- policy/config.go | 6 ++-- policy/config_test.go | 6 ++-- policy/go.mod | 10 +++---- policy/helper_test.go | 10 +++---- policy/parser.go | 6 ++-- policy/parser_test.go | 6 ++-- policy/source.go | 2 +- policy/test/cel_test_runner.go | 8 +++--- policy/test/k8s_cel_test_runner.go | 4 +-- repl/BUILD.bazel | 2 +- repl/commands.go | 4 +-- repl/evaluator.go | 16 +++++------ repl/evaluator_test.go | 6 ++-- repl/go.mod | 6 ++-- repl/main/BUILD.bazel | 4 +-- repl/main/main.go | 2 +- repl/parser/BUILD.bazel | 2 +- repl/typefmt.go | 8 +++--- repl/typefmt_test.go | 2 +- test/BUILD.bazel | 2 +- test/async.go | 4 +-- test/bench/BUILD.bazel | 2 +- test/bench/bench.go | 8 +++--- test/bench/bench_test.go | 2 +- test/expr.go | 2 +- test/proto2pb/BUILD.bazel | 4 +-- test/proto2pb/test_all_types.proto | 2 +- test/proto2pb/test_extensions.proto | 2 +- test/proto3pb/BUILD.bazel | 4 +-- test/proto3pb/test_all_types.proto | 2 +- test/proto3pb/test_import.proto | 2 +- tools/celtest/BUILD.bazel | 2 +- tools/celtest/test_coverage_reporter.go | 6 ++-- tools/celtest/test_coverage_reporter_test.go | 6 ++-- tools/celtest/test_runner.go | 20 ++++++------- tools/celtest/test_runner_test.go | 14 +++++----- tools/compiler/BUILD.bazel | 2 +- tools/compiler/compiler.go | 12 ++++---- tools/compiler/compiler_test.go | 8 +++--- tools/go.mod | 8 +++--- tools/go.sum | 4 +-- 269 files changed, 883 insertions(+), 883 deletions(-) diff --git a/README.md b/README.md index 361eea71a..6b71ff776 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # Common Expression Language -[![Go Report Card](https://goreportcard.com/badge/github.com/authzed/cel-go)](https://goreportcard.com/report/github.com/authzed/cel-go) -[![GoDoc](https://godoc.org/github.com/authzed/cel-go?status.svg)][6] +[![Go Report Card](https://goreportcard.com/badge/github.com/google/cel-go)](https://goreportcard.com/report/github.com/google/cel-go) +[![GoDoc](https://godoc.org/github.com/google/cel-go?status.svg)][6] > [!WARNING] > **On June 16, 2026, this repository will move to > github.com/cel-expr/cel-go!** > > Please update your links and dependencies. See the [pinned -> issue](https://github.com/authzed/cel-go/issues/1329) for details. +> issue](https://github.com/google/cel-go/issues/1329) for details. The Common Expression Language (CEL) is a non-Turing complete language designed for simplicity, speed, safety, and portability. CEL's C-like [syntax][1] looks @@ -64,7 +64,7 @@ Let's expose `name` and `group` variables to CEL using the `cel.Variable` environment option: ```go -import "github.com/authzed/cel-go/cel" +import "github.com/google/cel-go/cel" env, err := cel.NewEnv( cel.Variable("name", cel.StringType), @@ -288,6 +288,6 @@ Released under the [Apache License](LICENSE). [1]: https://github.com/google/cel-spec [2]: https://groups.google.com/forum/#!forum/cel-go-discuss [3]: https://github.com/google/cel-cpp -[4]: https://github.com/authzed/cel-go/issues +[4]: https://github.com/google/cel-go/issues [5]: https://bazel.build -[6]: https://godoc.org/github.com/authzed/cel-go +[6]: https://godoc.org/github.com/google/cel-go diff --git a/cel/BUILD.bazel b/cel/BUILD.bazel index 4fd6b2070..62a56036a 100644 --- a/cel/BUILD.bazel +++ b/cel/BUILD.bazel @@ -23,7 +23,7 @@ go_library( "validator.go", ], embedsrcs = ["templates/authoring.tmpl"], - importpath = "github.com/authzed/cel-go/cel", + importpath = "github.com/google/cel-go/cel", visibility = ["//visibility:public"], deps = [ "//cel/async:go_default_library", diff --git a/cel/async/BUILD.bazel b/cel/async/BUILD.bazel index 34fa169eb..85b28bcdb 100644 --- a/cel/async/BUILD.bazel +++ b/cel/async/BUILD.bazel @@ -9,7 +9,7 @@ go_library( srcs = [ "async.go", ], - importpath = "github.com/authzed/cel-go/cel/async", + importpath = "github.com/google/cel-go/cel/async", visibility = ["//visibility:public"], deps = [ "//common/decls:go_default_library", diff --git a/cel/async/async.go b/cel/async/async.go index f76b9d37a..a011114bd 100644 --- a/cel/async/async.go +++ b/cel/async/async.go @@ -21,11 +21,11 @@ import ( "errors" "time" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/interpreter" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/interpreter" ) // Call describes a pending or completed asynchronous function call. diff --git a/cel/async/async_test.go b/cel/async/async_test.go index 2ea197801..71fe50de6 100644 --- a/cel/async/async_test.go +++ b/cel/async/async_test.go @@ -22,11 +22,11 @@ import ( "testing" "time" - "github.com/authzed/cel-go/cel/async" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/cel/async" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) type retryableTestErr struct{} diff --git a/cel/cel_example_test.go b/cel/cel_example_test.go index b4d116e57..f4c11b598 100644 --- a/cel/cel_example_test.go +++ b/cel/cel_example_test.go @@ -19,9 +19,9 @@ import ( "fmt" "log" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) func Example() { diff --git a/cel/cel_test.go b/cel/cel_test.go index 95b59bdcb..5aa0cbf7b 100644 --- a/cel/cel_test.go +++ b/cel/cel_test.go @@ -32,17 +32,17 @@ import ( "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoreflect" - "github.com/authzed/cel-go/checker" - celast "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" - "github.com/authzed/cel-go/interpreter" - "github.com/authzed/cel-go/parser" - "github.com/authzed/cel-go/test" + "github.com/google/cel-go/checker" + celast "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/env" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/interpreter" + "github.com/google/cel-go/parser" + "github.com/google/cel-go/test" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" descpb "google.golang.org/protobuf/types/descriptorpb" @@ -51,8 +51,8 @@ import ( timestamppb "google.golang.org/protobuf/types/known/timestamppb" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" - proto2pb "github.com/authzed/cel-go/test/proto2pb" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto2pb "github.com/google/cel-go/test/proto2pb" + proto3pb "github.com/google/cel-go/test/proto3pb" ) func Test_ExampleWithBuiltins(t *testing.T) { diff --git a/cel/decls.go b/cel/decls.go index 64e488d54..c7c23fd51 100644 --- a/cel/decls.go +++ b/cel/decls.go @@ -17,11 +17,11 @@ package cel import ( "fmt" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" celpb "cel.dev/expr" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" diff --git a/cel/decls_test.go b/cel/decls_test.go index f3244b915..9024d74db 100644 --- a/cel/decls_test.go +++ b/cel/decls_test.go @@ -21,15 +21,15 @@ import ( "strings" "testing" - chkdecls "github.com/authzed/cel-go/checker/decls" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/stdlib" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + chkdecls "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/stdlib" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/env.go b/cel/env.go index b738fd141..784790ba2 100644 --- a/cel/env.go +++ b/cel/env.go @@ -22,19 +22,19 @@ import ( "strings" "sync" - "github.com/authzed/cel-go/checker" - chkdecls "github.com/authzed/cel-go/checker/decls" - "github.com/authzed/cel-go/common" - celast "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/stdlib" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/interpreter" - "github.com/authzed/cel-go/parser" + "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/env" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/stdlib" + "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" "google.golang.org/protobuf/reflect/protoreflect" diff --git a/cel/env_test.go b/cel/env_test.go index 6d7b5b836..eb4c20850 100644 --- a/cel/env_test.go +++ b/cel/env_test.go @@ -23,17 +23,17 @@ import ( "sync" "testing" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/env" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" "google.golang.org/protobuf/proto" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/fieldpaths.go b/cel/fieldpaths.go index 57d02473b..570fce3a4 100644 --- a/cel/fieldpaths.go +++ b/cel/fieldpaths.go @@ -4,8 +4,8 @@ import ( "slices" "strings" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/types" ) // fieldPath represents a selection path to a field from a variable in a CEL environment. diff --git a/cel/folding.go b/cel/folding.go index 17a7203d6..5525f0805 100644 --- a/cel/folding.go +++ b/cel/folding.go @@ -19,12 +19,12 @@ import ( "errors" "fmt" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" ) // ConstantFoldingOption defines a functional option for configuring constant folding. diff --git a/cel/folding_test.go b/cel/folding_test.go index da4cfc622..56dd789d4 100644 --- a/cel/folding_test.go +++ b/cel/folding_test.go @@ -25,13 +25,13 @@ import ( "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/interpreter" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/interpreter" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/inlining.go b/cel/inlining.go index ffd15d4aa..d9a5e89a5 100644 --- a/cel/inlining.go +++ b/cel/inlining.go @@ -15,12 +15,12 @@ package cel import ( - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/traits" ) // InlineVariable holds a variable name to be matched and an AST representing diff --git a/cel/inlining_test.go b/cel/inlining_test.go index 06d678b01..d9ad88b7a 100644 --- a/cel/inlining_test.go +++ b/cel/inlining_test.go @@ -17,9 +17,9 @@ package cel_test import ( "testing" - "github.com/authzed/cel-go/cel" + "github.com/google/cel-go/cel" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" ) func TestInliningOptimizerNoopShadow(t *testing.T) { diff --git a/cel/io.go b/cel/io.go index f5a970715..c991c95c3 100644 --- a/cel/io.go +++ b/cel/io.go @@ -21,12 +21,12 @@ import ( "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/parser" celpb "cel.dev/expr" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" diff --git a/cel/io_test.go b/cel/io_test.go index b1560cc71..f0ef20a81 100644 --- a/cel/io_test.go +++ b/cel/io_test.go @@ -22,13 +22,13 @@ import ( "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/checker/decls" - celast "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/checker/decls" + celast "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/library.go b/cel/library.go index 06b589e2b..332eb3f17 100644 --- a/cel/library.go +++ b/cel/library.go @@ -18,18 +18,18 @@ import ( "fmt" "math" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/stdlib" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" - "github.com/authzed/cel-go/interpreter" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/env" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/stdlib" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/interpreter" + "github.com/google/cel-go/parser" ) const ( diff --git a/cel/macro.go b/cel/macro.go index 76a945cef..3d3c5be1b 100644 --- a/cel/macro.go +++ b/cel/macro.go @@ -17,10 +17,10 @@ package cel import ( "fmt" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/macro_test.go b/cel/macro_test.go index 950693afb..f6400b020 100644 --- a/cel/macro_test.go +++ b/cel/macro_test.go @@ -17,8 +17,8 @@ package cel import ( "testing" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" ) func TestGlobalVarArgMacro(t *testing.T) { diff --git a/cel/optimizer.go b/cel/optimizer.go index acb931d02..6e260a93c 100644 --- a/cel/optimizer.go +++ b/cel/optimizer.go @@ -18,10 +18,10 @@ import ( "fmt" "sort" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // StaticOptimizer contains a sequence of ASTOptimizer instances which will be applied in order. diff --git a/cel/optimizer_test.go b/cel/optimizer_test.go index c616dd7dc..04212d537 100644 --- a/cel/optimizer_test.go +++ b/cel/optimizer_test.go @@ -19,14 +19,14 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/ext" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/ext" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/options.go b/cel/options.go index 65e4da3a8..540ad38ba 100644 --- a/cel/options.go +++ b/cel/options.go @@ -24,17 +24,17 @@ import ( "google.golang.org/protobuf/reflect/protoregistry" "google.golang.org/protobuf/types/dynamicpb" - "github.com/authzed/cel-go/cel/async" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/pb" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/interpreter" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/cel/async" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/env" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/pb" + "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" descpb "google.golang.org/protobuf/types/descriptorpb" diff --git a/cel/program.go b/cel/program.go index 5b1958fc2..3a7589a71 100644 --- a/cel/program.go +++ b/cel/program.go @@ -20,12 +20,12 @@ import ( "fmt" "time" - "github.com/authzed/cel-go/cel/async" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/interpreter" + "github.com/google/cel-go/cel/async" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/interpreter" ) // Program is an evaluable view of an Ast. diff --git a/cel/program_async_test.go b/cel/program_async_test.go index 15f298b3a..60750047d 100644 --- a/cel/program_async_test.go +++ b/cel/program_async_test.go @@ -24,13 +24,13 @@ import ( "testing" "time" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/cel/async" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/ext" - "github.com/authzed/cel-go/interpreter" - "github.com/authzed/cel-go/test" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/cel/async" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/ext" + "github.com/google/cel-go/interpreter" + "github.com/google/cel-go/test" ) func TestConcurrentEval(t *testing.T) { diff --git a/cel/prompt.go b/cel/prompt.go index 05bd26e40..f59934827 100644 --- a/cel/prompt.go +++ b/cel/prompt.go @@ -20,10 +20,10 @@ import ( "strings" "text/template" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" ) //go:embed templates/authoring.tmpl diff --git a/cel/prompt_test.go b/cel/prompt_test.go index 8e958d27e..068211cb7 100644 --- a/cel/prompt_test.go +++ b/cel/prompt_test.go @@ -20,8 +20,8 @@ import ( "sync" "testing" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/env" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/env" "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/proto" diff --git a/cel/validator.go b/cel/validator.go index 130a65ded..cb7f4c29e 100644 --- a/cel/validator.go +++ b/cel/validator.go @@ -20,9 +20,9 @@ import ( "reflect" "regexp" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/common/overloads" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/env" + "github.com/google/cel-go/common/overloads" ) const ( diff --git a/cel/validator_test.go b/cel/validator_test.go index e6465bfca..606a5b6d9 100644 --- a/cel/validator_test.go +++ b/cel/validator_test.go @@ -18,13 +18,13 @@ import ( "reflect" "testing" - celenv "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" - "github.com/authzed/cel-go/test" + celenv "github.com/google/cel-go/common/env" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/test" ) func TestValidateDurationLiterals(t *testing.T) { diff --git a/checker/BUILD.bazel b/checker/BUILD.bazel index 145c3e715..678b412a9 100644 --- a/checker/BUILD.bazel +++ b/checker/BUILD.bazel @@ -18,7 +18,7 @@ go_library( "scopes.go", "types.go", ], - importpath = "github.com/authzed/cel-go/checker", + importpath = "github.com/google/cel-go/checker", visibility = ["//visibility:public"], deps = [ "//checker/decls:go_default_library", diff --git a/checker/checker.go b/checker/checker.go index b209703f3..42d27a428 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -22,13 +22,13 @@ import ( "slices" "strings" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common" + "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/operators" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) type checker struct { diff --git a/checker/checker_test.go b/checker/checker_test.go index 7e520afe1..3e86ec577 100644 --- a/checker/checker_test.go +++ b/checker/checker_test.go @@ -20,18 +20,18 @@ import ( "testing" "time" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/debug" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/stdlib" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/parser" - "github.com/authzed/cel-go/test" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/debug" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/stdlib" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/parser" + "github.com/google/cel-go/test" - proto2pb "github.com/authzed/cel-go/test/proto2pb" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto2pb "github.com/google/cel-go/test/proto2pb" + proto3pb "github.com/google/cel-go/test/proto3pb" ) func testCases(t testing.TB) []testInfo { diff --git a/checker/cost.go b/checker/cost.go index f1aebf298..3d7dd7ec4 100644 --- a/checker/cost.go +++ b/checker/cost.go @@ -17,11 +17,11 @@ package checker import ( "math" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/parser" ) // WARNING: Any changes to cost calculations in this file require a corresponding change in interpreter/runtimecost.go diff --git a/checker/cost_test.go b/checker/cost_test.go index 9cc2bcbbc..5ee5a266f 100644 --- a/checker/cost_test.go +++ b/checker/cost_test.go @@ -19,15 +19,15 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/stdlib" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/stdlib" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/parser" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" ) func TestCost(t *testing.T) { diff --git a/checker/decls/BUILD.bazel b/checker/decls/BUILD.bazel index 2ee0e4dfb..a6b0be292 100644 --- a/checker/decls/BUILD.bazel +++ b/checker/decls/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "decls.go", ], - importpath = "github.com/authzed/cel-go/checker/decls", + importpath = "github.com/google/cel-go/checker/decls", deps = [ "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", "@org_golang_google_protobuf//types/known/emptypb:go_default_library", diff --git a/checker/env.go b/checker/env.go index 9d3f5e3b5..477918c48 100644 --- a/checker/env.go +++ b/checker/env.go @@ -18,11 +18,11 @@ import ( "fmt" "strings" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/parser" ) type aggregateLiteralElementType int diff --git a/checker/env_test.go b/checker/env_test.go index 8048d68ef..2ec7f13fa 100644 --- a/checker/env_test.go +++ b/checker/env_test.go @@ -18,12 +18,12 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/stdlib" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/stdlib" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/parser" ) func TestOverlappingMacro(t *testing.T) { diff --git a/checker/errors.go b/checker/errors.go index 399d1dd92..3535440ba 100644 --- a/checker/errors.go +++ b/checker/errors.go @@ -15,9 +15,9 @@ package checker import ( - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" ) // typeErrors is a specialization of Errors. diff --git a/checker/format.go b/checker/format.go index 65655a41b..95842905e 100644 --- a/checker/format.go +++ b/checker/format.go @@ -18,8 +18,8 @@ import ( "fmt" "strings" - chkdecls "github.com/authzed/cel-go/checker/decls" - "github.com/authzed/cel-go/common/types" + chkdecls "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common/types" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/checker/format_test.go b/checker/format_test.go index 6351d61a4..c68b01c2f 100644 --- a/checker/format_test.go +++ b/checker/format_test.go @@ -17,8 +17,8 @@ package checker import ( "testing" - "github.com/authzed/cel-go/checker/decls" - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common/types" ) func TestFormatType(t *testing.T) { diff --git a/checker/mapping.go b/checker/mapping.go index d5b3bdf26..8163a908a 100644 --- a/checker/mapping.go +++ b/checker/mapping.go @@ -15,7 +15,7 @@ package checker import ( - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/common/types" ) type mapping struct { diff --git a/checker/printer.go b/checker/printer.go index deadde3bc..7a3984f02 100644 --- a/checker/printer.go +++ b/checker/printer.go @@ -17,8 +17,8 @@ package checker import ( "sort" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/debug" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/debug" ) type semanticAdorner struct { diff --git a/checker/scopes.go b/checker/scopes.go index d91b8c331..9ae9832e1 100644 --- a/checker/scopes.go +++ b/checker/scopes.go @@ -17,7 +17,7 @@ package checker import ( "strings" - "github.com/authzed/cel-go/common/decls" + "github.com/google/cel-go/common/decls" ) // Scopes represents nested Decl sets where the Scopes value contains a Groups containing all diff --git a/checker/types.go b/checker/types.go index e94d8c73f..4c65b2737 100644 --- a/checker/types.go +++ b/checker/types.go @@ -15,7 +15,7 @@ package checker import ( - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/common/types" ) // isDyn returns true if the input t is either type DYN or a well-known ANY message. diff --git a/codelab/README.md b/codelab/README.md index 5f6caa072..bf985c71b 100644 --- a/codelab/README.md +++ b/codelab/README.md @@ -3,6 +3,6 @@ Find the codelab instructions [here](https://codelabs.developers.google.com/codelabs/cel-go/#0). It requires some knowledge of GoLang and Protobuf. -If you get stuck, check out the [solutions](https://github.com/authzed/cel-go/blob/master/codelab/solution/codelab.go). +If you get stuck, check out the [solutions](https://github.com/google/cel-go/blob/master/codelab/solution/codelab.go). -If you find a bug or want to make an improvement, PRs and issues are welcome. Please follow the [contributing guidelines](https://github.com/authzed/cel-go/blob/master/CONTRIBUTING.md). +If you find a bug or want to make an improvement, PRs and issues are welcome. Please follow the [contributing guidelines](https://github.com/google/cel-go/blob/master/CONTRIBUTING.md). diff --git a/codelab/codelab.go b/codelab/codelab.go index 131f65ed6..f533e7003 100644 --- a/codelab/codelab.go +++ b/codelab/codelab.go @@ -24,10 +24,10 @@ import ( "strings" "time" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" "github.com/golang/glog" "google.golang.org/protobuf/encoding/protojson" diff --git a/codelab/go.mod b/codelab/go.mod index fe627fead..58d84bb8b 100644 --- a/codelab/go.mod +++ b/codelab/go.mod @@ -1,4 +1,4 @@ -module github.com/authzed/cel-go/codelab +module github.com/google/cel-go/codelab go 1.22.0 @@ -6,7 +6,7 @@ toolchain go1.22.5 require ( github.com/golang/glog v1.2.4 - github.com/authzed/cel-go v0.21.0 + github.com/google/cel-go v0.21.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 google.golang.org/protobuf v1.34.2 ) @@ -19,4 +19,4 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect ) -replace github.com/authzed/cel-go => ../. +replace github.com/google/cel-go => ../. diff --git a/codelab/solution/codelab.go b/codelab/solution/codelab.go index d1edf25c0..b1bfb95d9 100644 --- a/codelab/solution/codelab.go +++ b/codelab/solution/codelab.go @@ -24,10 +24,10 @@ import ( "strings" "time" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" "github.com/golang/glog" "google.golang.org/protobuf/encoding/protojson" diff --git a/common/BUILD.bazel b/common/BUILD.bazel index b1a5b82ab..1b1b7914d 100644 --- a/common/BUILD.bazel +++ b/common/BUILD.bazel @@ -15,7 +15,7 @@ go_library( "location.go", "source.go", ], - importpath = "github.com/authzed/cel-go/common", + importpath = "github.com/google/cel-go/common", deps = [ "//common/runes:go_default_library", "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", diff --git a/common/ast/BUILD.bazel b/common/ast/BUILD.bazel index 94ca875f9..9824f57a9 100644 --- a/common/ast/BUILD.bazel +++ b/common/ast/BUILD.bazel @@ -14,7 +14,7 @@ go_library( "factory.go", "navigable.go", ], - importpath = "github.com/authzed/cel-go/common/ast", + importpath = "github.com/google/cel-go/common/ast", deps = [ "//common:go_default_library", "//common/types:go_default_library", diff --git a/common/ast/ast.go b/common/ast/ast.go index e5f8db8df..c8f8f8a02 100644 --- a/common/ast/ast.go +++ b/common/ast/ast.go @@ -18,9 +18,9 @@ package ast import ( "slices" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // AST contains a protobuf expression and source info along with CEL-native type and reference information. diff --git a/common/ast/ast_test.go b/common/ast/ast_test.go index 3fd1d39a7..ee63171a6 100644 --- a/common/ast/ast_test.go +++ b/common/ast/ast_test.go @@ -20,11 +20,11 @@ import ( "reflect" "testing" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" ) diff --git a/common/ast/conversion.go b/common/ast/conversion.go index c0fad20ad..380f8c118 100644 --- a/common/ast/conversion.go +++ b/common/ast/conversion.go @@ -19,8 +19,8 @@ import ( "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" celpb "cel.dev/expr" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" diff --git a/common/ast/conversion_test.go b/common/ast/conversion_test.go index 02091ae97..ecd50830b 100644 --- a/common/ast/conversion_test.go +++ b/common/ast/conversion_test.go @@ -23,14 +23,14 @@ import ( "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" - chkdecls "github.com/authzed/cel-go/checker/decls" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/parser" + chkdecls "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/ast/expr.go b/common/ast/expr.go index 7c4a588d9..9f55cb3b9 100644 --- a/common/ast/expr.go +++ b/common/ast/expr.go @@ -15,7 +15,7 @@ package ast import ( - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) // ExprKind represents the expression node kind. diff --git a/common/ast/expr_test.go b/common/ast/expr_test.go index c550cf828..d32bf3fee 100644 --- a/common/ast/expr_test.go +++ b/common/ast/expr_test.go @@ -19,9 +19,9 @@ import ( "reflect" "testing" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" ) func TestSetKindCase(t *testing.T) { diff --git a/common/ast/factory.go b/common/ast/factory.go index 140ac8be5..d4dcde4d9 100644 --- a/common/ast/factory.go +++ b/common/ast/factory.go @@ -14,7 +14,7 @@ package ast -import "github.com/authzed/cel-go/common/types/ref" +import "github.com/google/cel-go/common/types/ref" // ExprFactory interfaces defines a set of methods necessary for building native expression values. type ExprFactory interface { diff --git a/common/ast/navigable.go b/common/ast/navigable.go index 091c6a6f5..364edfa3a 100644 --- a/common/ast/navigable.go +++ b/common/ast/navigable.go @@ -15,8 +15,8 @@ package ast import ( - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // NavigableExpr represents the base navigable expression value with methods to inspect the diff --git a/common/ast/navigable_test.go b/common/ast/navigable_test.go index d6ea22c28..4a378546f 100644 --- a/common/ast/navigable_test.go +++ b/common/ast/navigable_test.go @@ -18,17 +18,17 @@ import ( "reflect" "testing" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/stdlib" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common" + "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/operators" + "github.com/google/cel-go/common/stdlib" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/parser" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" ) func TestNavigateAST(t *testing.T) { diff --git a/common/containers/BUILD.bazel b/common/containers/BUILD.bazel index 8e4a4c538..81197f064 100644 --- a/common/containers/BUILD.bazel +++ b/common/containers/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "container.go", ], - importpath = "github.com/authzed/cel-go/common/containers", + importpath = "github.com/google/cel-go/common/containers", deps = [ "//common/ast:go_default_library", ], diff --git a/common/containers/container.go b/common/containers/container.go index 2eab44b4a..fcfcdfc3f 100644 --- a/common/containers/container.go +++ b/common/containers/container.go @@ -21,7 +21,7 @@ import ( "strings" "unicode" - "github.com/authzed/cel-go/common/ast" + "github.com/google/cel-go/common/ast" ) var ( diff --git a/common/containers/container_test.go b/common/containers/container_test.go index 8bb6ffb36..2ccdfd6ee 100644 --- a/common/containers/container_test.go +++ b/common/containers/container_test.go @@ -19,7 +19,7 @@ import ( "reflect" "testing" - "github.com/authzed/cel-go/common/ast" + "github.com/google/cel-go/common/ast" ) func TestContainers_ResolveCandidateNames(t *testing.T) { diff --git a/common/debug/BUILD.bazel b/common/debug/BUILD.bazel index 24d8d40d0..724ed3404 100644 --- a/common/debug/BUILD.bazel +++ b/common/debug/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "debug.go", ], - importpath = "github.com/authzed/cel-go/common/debug", + importpath = "github.com/google/cel-go/common/debug", deps = [ "//common:go_default_library", "//common/ast:go_default_library", diff --git a/common/debug/debug.go b/common/debug/debug.go index c195e2d51..fbc847f0c 100644 --- a/common/debug/debug.go +++ b/common/debug/debug.go @@ -22,9 +22,9 @@ import ( "strconv" "strings" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // Adorner returns debug metadata that will be tacked on to the string diff --git a/common/decls/BUILD.bazel b/common/decls/BUILD.bazel index 7d87692ab..bd3f9ae70 100644 --- a/common/decls/BUILD.bazel +++ b/common/decls/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "decls.go", ], - importpath = "github.com/authzed/cel-go/common/decls", + importpath = "github.com/google/cel-go/common/decls", deps = [ "//checker/decls:go_default_library", "//common:go_default_library", diff --git a/common/decls/decls.go b/common/decls/decls.go index 56c7ea8c4..51cb689e5 100644 --- a/common/decls/decls.go +++ b/common/decls/decls.go @@ -20,12 +20,12 @@ import ( "fmt" "strings" - chkdecls "github.com/authzed/cel-go/checker/decls" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + chkdecls "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/decls/decls_test.go b/common/decls/decls_test.go index 113580766..ca26eb690 100644 --- a/common/decls/decls_test.go +++ b/common/decls/decls_test.go @@ -23,13 +23,13 @@ import ( "google.golang.org/protobuf/proto" - chkdecls "github.com/authzed/cel-go/checker/decls" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + chkdecls "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/env/BUILD.bazel b/common/env/BUILD.bazel index 81e70fbc1..261da924d 100644 --- a/common/env/BUILD.bazel +++ b/common/env/BUILD.bazel @@ -25,7 +25,7 @@ go_library( "env.go", "io.go", ], - importpath = "github.com/authzed/cel-go/common/env", + importpath = "github.com/google/cel-go/common/env", deps = [ "//common:go_default_library", "//common/decls:go_default_library", diff --git a/common/env/env.go b/common/env/env.go index bc6e06ba5..936036ed2 100644 --- a/common/env/env.go +++ b/common/env/env.go @@ -22,8 +22,8 @@ import ( "strconv" "strings" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/types" ) // NewConfig creates an instance of a YAML serializable CEL environment configuration. diff --git a/common/env/env_test.go b/common/env/env_test.go index 6ee20367a..9ee4ce6f6 100644 --- a/common/env/env_test.go +++ b/common/env/env_test.go @@ -25,11 +25,11 @@ import ( "go.yaml.in/yaml/v3" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" ) func TestConfig(t *testing.T) { diff --git a/common/functions/BUILD.bazel b/common/functions/BUILD.bazel index 92ca48bbc..3cc27d60c 100644 --- a/common/functions/BUILD.bazel +++ b/common/functions/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "functions.go", ], - importpath = "github.com/authzed/cel-go/common/functions", + importpath = "github.com/google/cel-go/common/functions", deps = [ "//common/types/ref:go_default_library", ], diff --git a/common/functions/functions.go b/common/functions/functions.go index 09703d331..0c00781d9 100644 --- a/common/functions/functions.go +++ b/common/functions/functions.go @@ -18,7 +18,7 @@ package functions import ( "context" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) // Overload defines a named overload of a function, indicating an operand trait diff --git a/common/operators/BUILD.bazel b/common/operators/BUILD.bazel index 869c82e79..b5b67f062 100644 --- a/common/operators/BUILD.bazel +++ b/common/operators/BUILD.bazel @@ -10,5 +10,5 @@ go_library( srcs = [ "operators.go", ], - importpath = "github.com/authzed/cel-go/common/operators", + importpath = "github.com/google/cel-go/common/operators", ) diff --git a/common/overloads/BUILD.bazel b/common/overloads/BUILD.bazel index 3a1f47fda..e46e2f483 100644 --- a/common/overloads/BUILD.bazel +++ b/common/overloads/BUILD.bazel @@ -10,5 +10,5 @@ go_library( srcs = [ "overloads.go", ], - importpath = "github.com/authzed/cel-go/common/overloads", + importpath = "github.com/google/cel-go/common/overloads", ) diff --git a/common/runes/BUILD.bazel b/common/runes/BUILD.bazel index 658969ffe..bb30242cf 100644 --- a/common/runes/BUILD.bazel +++ b/common/runes/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "buffer.go", ], - importpath = "github.com/authzed/cel-go/common/runes", + importpath = "github.com/google/cel-go/common/runes", ) go_test( diff --git a/common/source.go b/common/source.go index bc1413c95..9187e9b5c 100644 --- a/common/source.go +++ b/common/source.go @@ -15,7 +15,7 @@ package common import ( - "github.com/authzed/cel-go/common/runes" + "github.com/google/cel-go/common/runes" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/stdlib/BUILD.bazel b/common/stdlib/BUILD.bazel index 23f19a8af..124dbea81 100644 --- a/common/stdlib/BUILD.bazel +++ b/common/stdlib/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "standard.go", ], - importpath = "github.com/authzed/cel-go/common/stdlib", + importpath = "github.com/google/cel-go/common/stdlib", deps = [ "//common:go_default_library", "//common/decls:go_default_library", diff --git a/common/stdlib/standard.go b/common/stdlib/standard.go index e2ea809ce..d2313bef1 100644 --- a/common/stdlib/standard.go +++ b/common/stdlib/standard.go @@ -21,14 +21,14 @@ import ( "strings" "time" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" ) var ( diff --git a/common/types/BUILD.bazel b/common/types/BUILD.bazel index 89ac60e37..37d4df495 100644 --- a/common/types/BUILD.bazel +++ b/common/types/BUILD.bazel @@ -33,7 +33,7 @@ go_library( "unknown.go", "util.go", ], - importpath = "github.com/authzed/cel-go/common/types", + importpath = "github.com/google/cel-go/common/types", deps = [ "//checker/decls:go_default_library", "//common/overloads:go_default_library", diff --git a/common/types/bool.go b/common/types/bool.go index d987fef07..5f1e4573e 100644 --- a/common/types/bool.go +++ b/common/types/bool.go @@ -20,7 +20,7 @@ import ( "strconv" "strings" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/bytes.go b/common/types/bytes.go index 5671625c1..2eefb5d7f 100644 --- a/common/types/bytes.go +++ b/common/types/bytes.go @@ -22,7 +22,7 @@ import ( "strings" "unicode/utf8" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/compare.go b/common/types/compare.go index 8a9b05bf5..e19682618 100644 --- a/common/types/compare.go +++ b/common/types/compare.go @@ -17,7 +17,7 @@ package types import ( "math" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) func compareDoubleInt(d Double, i Int) Int { diff --git a/common/types/double.go b/common/types/double.go index 07a956fdd..02abfee2d 100644 --- a/common/types/double.go +++ b/common/types/double.go @@ -21,7 +21,7 @@ import ( "strconv" "strings" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/double_test.go b/common/types/double_test.go index 7f3002a50..47e875f29 100644 --- a/common/types/double_test.go +++ b/common/types/double_test.go @@ -23,8 +23,8 @@ import ( "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/duration.go b/common/types/duration.go index a7bbe07f8..220714773 100644 --- a/common/types/duration.go +++ b/common/types/duration.go @@ -21,8 +21,8 @@ import ( "strings" "time" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" dpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/duration_test.go b/common/types/duration_test.go index 7694ec54c..f0e4509e8 100644 --- a/common/types/duration_test.go +++ b/common/types/duration_test.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" dpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/err.go b/common/types/err.go index dd99b1b75..3216ff1c4 100644 --- a/common/types/err.go +++ b/common/types/err.go @@ -19,7 +19,7 @@ import ( "fmt" "reflect" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) // Error interface which allows types types.Err values to be treated as error values. diff --git a/common/types/format.go b/common/types/format.go index 89c2dacaa..174a2bd04 100644 --- a/common/types/format.go +++ b/common/types/format.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" ) type formattable interface { diff --git a/common/types/int.go b/common/types/int.go index 6b99d1fb4..60d5a7160 100644 --- a/common/types/int.go +++ b/common/types/int.go @@ -22,7 +22,7 @@ import ( "strings" "time" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/int_test.go b/common/types/int_test.go index 60c4d7b86..560c3dfb2 100644 --- a/common/types/int_test.go +++ b/common/types/int_test.go @@ -24,8 +24,8 @@ import ( "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/iterator.go b/common/types/iterator.go index 0d7913b15..98e9147b6 100644 --- a/common/types/iterator.go +++ b/common/types/iterator.go @@ -18,8 +18,8 @@ import ( "fmt" "reflect" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" ) var ( diff --git a/common/types/json_list_test.go b/common/types/json_list_test.go index d3d59e7b9..0d8cff093 100644 --- a/common/types/json_list_test.go +++ b/common/types/json_list_test.go @@ -19,7 +19,7 @@ import ( "reflect" "testing" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/types/traits" "google.golang.org/protobuf/proto" anypb "google.golang.org/protobuf/types/known/anypb" diff --git a/common/types/list.go b/common/types/list.go index b6452c295..028770ed6 100644 --- a/common/types/list.go +++ b/common/types/list.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/list_test.go b/common/types/list_test.go index 8f8168fbe..ca134b716 100644 --- a/common/types/list_test.go +++ b/common/types/list_test.go @@ -24,8 +24,8 @@ import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" dpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/map.go b/common/types/map.go index 22207458a..e4d6f7657 100644 --- a/common/types/map.go +++ b/common/types/map.go @@ -24,9 +24,9 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "github.com/authzed/cel-go/common/types/pb" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/types/pb" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/map_test.go b/common/types/map_test.go index e33673797..81989120d 100644 --- a/common/types/map_test.go +++ b/common/types/map_test.go @@ -26,11 +26,11 @@ import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/common/types/pb" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/types/pb" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" tpb "google.golang.org/protobuf/types/known/timestamppb" diff --git a/common/types/null.go b/common/types/null.go index 835cc0618..671e1ee5c 100644 --- a/common/types/null.go +++ b/common/types/null.go @@ -21,7 +21,7 @@ import ( "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/null_test.go b/common/types/null_test.go index 33a82b761..97ec24c10 100644 --- a/common/types/null_test.go +++ b/common/types/null_test.go @@ -22,7 +22,7 @@ import ( "google.golang.org/protobuf/proto" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" dynamicpb "google.golang.org/protobuf/types/dynamicpb" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/object.go b/common/types/object.go index d6dcac796..bb2a09e87 100644 --- a/common/types/object.go +++ b/common/types/object.go @@ -24,8 +24,8 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "github.com/authzed/cel-go/common/types/pb" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/pb" + "github.com/google/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/object_test.go b/common/types/object_test.go index 18def0120..b2e2207ea 100644 --- a/common/types/object_test.go +++ b/common/types/object_test.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" anypb "google.golang.org/protobuf/types/known/anypb" diff --git a/common/types/optional.go b/common/types/optional.go index 7c0418110..0d861823d 100644 --- a/common/types/optional.go +++ b/common/types/optional.go @@ -20,7 +20,7 @@ import ( "reflect" "strings" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) var ( diff --git a/common/types/optional_test.go b/common/types/optional_test.go index faf1c2e6c..89f28d7e7 100644 --- a/common/types/optional_test.go +++ b/common/types/optional_test.go @@ -19,7 +19,7 @@ import ( "reflect" "testing" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) func TestOptionalOptionalOf(t *testing.T) { diff --git a/common/types/pb/BUILD.bazel b/common/types/pb/BUILD.bazel index 0c79e718a..e2b9d37b5 100644 --- a/common/types/pb/BUILD.bazel +++ b/common/types/pb/BUILD.bazel @@ -15,7 +15,7 @@ go_library( "pb.go", "type.go", ], - importpath = "github.com/authzed/cel-go/common/types/pb", + importpath = "github.com/google/cel-go/common/types/pb", deps = [ "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", "@org_golang_google_protobuf//encoding/protowire:go_default_library", diff --git a/common/types/pb/equal_test.go b/common/types/pb/equal_test.go index 0f7ac3138..83254e4f6 100644 --- a/common/types/pb/equal_test.go +++ b/common/types/pb/equal_test.go @@ -20,7 +20,7 @@ import ( "google.golang.org/protobuf/proto" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" anypb "google.golang.org/protobuf/types/known/anypb" ) diff --git a/common/types/pb/file_test.go b/common/types/pb/file_test.go index dde847fa8..d0c4da607 100644 --- a/common/types/pb/file_test.go +++ b/common/types/pb/file_test.go @@ -21,10 +21,10 @@ import ( "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoreflect" - "github.com/authzed/cel-go/checker/decls" + "github.com/google/cel-go/checker/decls" - proto2pb "github.com/authzed/cel-go/test/proto2pb" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto2pb "github.com/google/cel-go/test/proto2pb" + proto3pb "github.com/google/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" descpb "google.golang.org/protobuf/types/descriptorpb" ) diff --git a/common/types/pb/pb_test.go b/common/types/pb/pb_test.go index 45260f203..aef1c02b4 100644 --- a/common/types/pb/pb_test.go +++ b/common/types/pb/pb_test.go @@ -21,8 +21,8 @@ import ( "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoreflect" - proto2pb "github.com/authzed/cel-go/test/proto2pb" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto2pb "github.com/google/cel-go/test/proto2pb" + proto3pb "github.com/google/cel-go/test/proto3pb" descpb "google.golang.org/protobuf/types/descriptorpb" dynamicpb "google.golang.org/protobuf/types/dynamicpb" durationpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/pb/type_test.go b/common/types/pb/type_test.go index 630e06286..fed1a3d0d 100644 --- a/common/types/pb/type_test.go +++ b/common/types/pb/type_test.go @@ -19,11 +19,11 @@ import ( "testing" "time" - "github.com/authzed/cel-go/checker/decls" + "github.com/google/cel-go/checker/decls" "google.golang.org/protobuf/proto" - proto2pb "github.com/authzed/cel-go/test/proto2pb" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto2pb "github.com/google/cel-go/test/proto2pb" + proto3pb "github.com/google/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" dynamicpb "google.golang.org/protobuf/types/dynamicpb" anypb "google.golang.org/protobuf/types/known/anypb" diff --git a/common/types/provider.go b/common/types/provider.go index c76c1fefd..1bb2c11ed 100644 --- a/common/types/provider.go +++ b/common/types/provider.go @@ -22,9 +22,9 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "github.com/authzed/cel-go/common/types/pb" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/types/pb" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" anypb "google.golang.org/protobuf/types/known/anypb" diff --git a/common/types/provider_test.go b/common/types/provider_test.go index 3f8fa206b..552ac1836 100644 --- a/common/types/provider_test.go +++ b/common/types/provider_test.go @@ -23,11 +23,11 @@ import ( "testing" "time" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" "google.golang.org/protobuf/proto" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" anypb "google.golang.org/protobuf/types/known/anypb" dpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/ref/BUILD.bazel b/common/types/ref/BUILD.bazel index 65ccb68f3..79330c332 100644 --- a/common/types/ref/BUILD.bazel +++ b/common/types/ref/BUILD.bazel @@ -11,7 +11,7 @@ go_library( "provider.go", "reference.go", ], - importpath = "github.com/authzed/cel-go/common/types/ref", + importpath = "github.com/google/cel-go/common/types/ref", deps = [ "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", "@org_golang_google_protobuf//proto:go_default_library", diff --git a/common/types/string.go b/common/types/string.go index 0d73db5eb..1335903a7 100644 --- a/common/types/string.go +++ b/common/types/string.go @@ -22,8 +22,8 @@ import ( "strings" "time" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/string_test.go b/common/types/string_test.go index 1914e4e11..daf8fdc54 100644 --- a/common/types/string_test.go +++ b/common/types/string_test.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/timestamp.go b/common/types/timestamp.go index c830851eb..62a020d97 100644 --- a/common/types/timestamp.go +++ b/common/types/timestamp.go @@ -23,8 +23,8 @@ import ( "time" "unicode" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/timestamp_test.go b/common/types/timestamp_test.go index 615f94f17..7661c878a 100644 --- a/common/types/timestamp_test.go +++ b/common/types/timestamp_test.go @@ -21,8 +21,8 @@ import ( "testing" "time" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types/ref" "google.golang.org/protobuf/proto" diff --git a/common/types/traits/BUILD.bazel b/common/types/traits/BUILD.bazel index 5d054f56c..b19eb8301 100644 --- a/common/types/traits/BUILD.bazel +++ b/common/types/traits/BUILD.bazel @@ -22,7 +22,7 @@ go_library( "traits.go", "zeroer.go", ], - importpath = "github.com/authzed/cel-go/common/types/traits", + importpath = "github.com/google/cel-go/common/types/traits", deps = [ "//common/types/ref:go_default_library", ], diff --git a/common/types/traits/comparer.go b/common/types/traits/comparer.go index 4888da175..b531d9ae2 100644 --- a/common/types/traits/comparer.go +++ b/common/types/traits/comparer.go @@ -15,7 +15,7 @@ package traits import ( - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) // Comparer interface for ordering comparisons between values in order to diff --git a/common/types/traits/container.go b/common/types/traits/container.go index fd6c607bf..cf5c621ae 100644 --- a/common/types/traits/container.go +++ b/common/types/traits/container.go @@ -14,7 +14,7 @@ package traits -import "github.com/authzed/cel-go/common/types/ref" +import "github.com/google/cel-go/common/types/ref" // Container interface which permits containment tests such as 'a in b'. type Container interface { diff --git a/common/types/traits/field_tester.go b/common/types/traits/field_tester.go index 865ea6a07..816a95652 100644 --- a/common/types/traits/field_tester.go +++ b/common/types/traits/field_tester.go @@ -15,7 +15,7 @@ package traits import ( - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) // FieldTester indicates if a defined field on an object type is set to a diff --git a/common/types/traits/indexer.go b/common/types/traits/indexer.go index ce8a13af2..662c6836c 100644 --- a/common/types/traits/indexer.go +++ b/common/types/traits/indexer.go @@ -15,7 +15,7 @@ package traits import ( - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) // Indexer permits random access of elements by index 'a[b()]'. diff --git a/common/types/traits/iterator.go b/common/types/traits/iterator.go index d7b42aa77..91c10f08f 100644 --- a/common/types/traits/iterator.go +++ b/common/types/traits/iterator.go @@ -15,7 +15,7 @@ package traits import ( - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) // Iterable aggregate types permit traversal over their elements. diff --git a/common/types/traits/lister.go b/common/types/traits/lister.go index e9a17f116..e54781a60 100644 --- a/common/types/traits/lister.go +++ b/common/types/traits/lister.go @@ -14,7 +14,7 @@ package traits -import "github.com/authzed/cel-go/common/types/ref" +import "github.com/google/cel-go/common/types/ref" // Lister interface which aggregates the traits of a list. type Lister interface { diff --git a/common/types/traits/mapper.go b/common/types/traits/mapper.go index f7ba9e05f..d13333f3f 100644 --- a/common/types/traits/mapper.go +++ b/common/types/traits/mapper.go @@ -14,7 +14,7 @@ package traits -import "github.com/authzed/cel-go/common/types/ref" +import "github.com/google/cel-go/common/types/ref" // Mapper interface which aggregates the traits of a maps. type Mapper interface { diff --git a/common/types/traits/matcher.go b/common/types/traits/matcher.go index 5b7aae7a6..085dc94ff 100644 --- a/common/types/traits/matcher.go +++ b/common/types/traits/matcher.go @@ -14,7 +14,7 @@ package traits -import "github.com/authzed/cel-go/common/types/ref" +import "github.com/google/cel-go/common/types/ref" // Matcher interface for supporting 'matches()' overloads. type Matcher interface { diff --git a/common/types/traits/math.go b/common/types/traits/math.go index 4062ff90a..86d5b9137 100644 --- a/common/types/traits/math.go +++ b/common/types/traits/math.go @@ -14,7 +14,7 @@ package traits -import "github.com/authzed/cel-go/common/types/ref" +import "github.com/google/cel-go/common/types/ref" // Adder interface to support '+' operator overloads. type Adder interface { diff --git a/common/types/traits/receiver.go b/common/types/traits/receiver.go index ad3e2e985..8f41db45e 100644 --- a/common/types/traits/receiver.go +++ b/common/types/traits/receiver.go @@ -14,7 +14,7 @@ package traits -import "github.com/authzed/cel-go/common/types/ref" +import "github.com/google/cel-go/common/types/ref" // Receiver interface for routing instance method calls within a value. type Receiver interface { diff --git a/common/types/traits/sizer.go b/common/types/traits/sizer.go index 85583d6b6..b80d25137 100644 --- a/common/types/traits/sizer.go +++ b/common/types/traits/sizer.go @@ -15,7 +15,7 @@ package traits import ( - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) // Sizer interface for supporting 'size()' overloads. diff --git a/common/types/type_test.go b/common/types/type_test.go index 8e90b033f..a63d17057 100644 --- a/common/types/type_test.go +++ b/common/types/type_test.go @@ -17,7 +17,7 @@ package types import ( "testing" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) func TestType_ConvertToType(t *testing.T) { diff --git a/common/types/types.go b/common/types/types.go index 417376b45..78c77a9b5 100644 --- a/common/types/types.go +++ b/common/types/types.go @@ -21,9 +21,9 @@ import ( "google.golang.org/protobuf/proto" - chkdecls "github.com/authzed/cel-go/checker/decls" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + chkdecls "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" celpb "cel.dev/expr" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" diff --git a/common/types/types_test.go b/common/types/types_test.go index 3d69e7224..c91d7148a 100644 --- a/common/types/types_test.go +++ b/common/types/types_test.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/proto" - chkdecls "github.com/authzed/cel-go/checker/decls" - "github.com/authzed/cel-go/common/types/traits" + chkdecls "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/types/uint.go b/common/types/uint.go index 1676de780..91d5369da 100644 --- a/common/types/uint.go +++ b/common/types/uint.go @@ -21,7 +21,7 @@ import ( "strconv" "strings" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/uint_test.go b/common/types/uint_test.go index 926c84f38..2484fbf36 100644 --- a/common/types/uint_test.go +++ b/common/types/uint_test.go @@ -23,8 +23,8 @@ import ( "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/unknown.go b/common/types/unknown.go index 9ee22ebdf..f43aff18e 100644 --- a/common/types/unknown.go +++ b/common/types/unknown.go @@ -23,7 +23,7 @@ import ( "strings" "unicode" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) var ( diff --git a/common/types/unknown_test.go b/common/types/unknown_test.go index 4ae841ed7..593708950 100644 --- a/common/types/unknown_test.go +++ b/common/types/unknown_test.go @@ -21,7 +21,7 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) func TestIsUnknown(t *testing.T) { diff --git a/common/types/util.go b/common/types/util.go index 5dd0f9e92..71662eee3 100644 --- a/common/types/util.go +++ b/common/types/util.go @@ -15,7 +15,7 @@ package types import ( - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) // IsUnknownOrError returns whether the input element ref.Val is an ErrType or UnknownType. diff --git a/conformance/conformance_test.go b/conformance/conformance_test.go index 1f7c0b43f..417310287 100644 --- a/conformance/conformance_test.go +++ b/conformance/conformance_test.go @@ -11,12 +11,12 @@ import ( "github.com/bazelbuild/rules_go/go/runfiles" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/ext" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/ext" "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/encoding/prototext" diff --git a/conformance/go.mod b/conformance/go.mod index 15bd36b6a..f5e6a2cc6 100644 --- a/conformance/go.mod +++ b/conformance/go.mod @@ -1,11 +1,11 @@ -module github.com/authzed/cel-go/conformance +module github.com/google/cel-go/conformance go 1.23.0 require ( cel.dev/expr v0.25.1 github.com/bazelbuild/rules_go v0.49.0 - github.com/authzed/cel-go v0.26.1 + github.com/google/cel-go v0.26.1 github.com/google/go-cmp v0.7.0 google.golang.org/protobuf v1.36.10 ) @@ -19,4 +19,4 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect ) -replace github.com/authzed/cel-go => ./.. +replace github.com/google/cel-go => ./.. diff --git a/conformance/policy/policy_conformance_test.go b/conformance/policy/policy_conformance_test.go index 2cc1563c6..d718f1778 100644 --- a/conformance/policy/policy_conformance_test.go +++ b/conformance/policy/policy_conformance_test.go @@ -24,12 +24,12 @@ import ( "testing" "github.com/bazelbuild/rules_go/go/runfiles" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/policy" - "github.com/authzed/cel-go/tools/celtest" - "github.com/authzed/cel-go/tools/compiler" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/policy" + "github.com/google/cel-go/tools/celtest" + "github.com/google/cel-go/tools/compiler" _ "cel.dev/expr/conformance/proto3" ) diff --git a/examples/README.md b/examples/README.md index 2e12245f5..4e739f660 100644 --- a/examples/README.md +++ b/examples/README.md @@ -15,7 +15,7 @@ import ( "fmt" "log" - "github.com/authzed/cel-go/cel" + "github.com/google/cel-go/cel" ) func main() { diff --git a/examples/example_cel_advanced_test.go b/examples/example_cel_advanced_test.go index b537ce4ae..a04a9ded5 100644 --- a/examples/example_cel_advanced_test.go +++ b/examples/example_cel_advanced_test.go @@ -17,7 +17,7 @@ package examples import ( "fmt" - "github.com/authzed/cel-go/cel" + "github.com/google/cel-go/cel" ) // Example_cel_CommonErrors showcases handling common runtime errors (division by zero, index out of bounds, missing key) diff --git a/examples/example_cel_collections_test.go b/examples/example_cel_collections_test.go index a3422033f..93c2110b6 100644 --- a/examples/example_cel_collections_test.go +++ b/examples/example_cel_collections_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "github.com/authzed/cel-go/cel" + "github.com/google/cel-go/cel" ) // Example_cel_Collections showcases membership, indexing, search (exists), all, and filter diff --git a/examples/example_cel_compile_test.go b/examples/example_cel_compile_test.go index fbe170944..7a7f3a42c 100644 --- a/examples/example_cel_compile_test.go +++ b/examples/example_cel_compile_test.go @@ -18,8 +18,8 @@ import ( "fmt" "log" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/ext" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/ext" ) // Example_cel_Compile showcases compiling a CEL expression with variable declarations diff --git a/examples/example_cel_context_eval_test.go b/examples/example_cel_context_eval_test.go index e4804036d..18f51d712 100644 --- a/examples/example_cel_context_eval_test.go +++ b/examples/example_cel_context_eval_test.go @@ -19,7 +19,7 @@ import ( "fmt" "log" - "github.com/authzed/cel-go/cel" + "github.com/google/cel-go/cel" ) // Example_cel_ContextEval showcases evaluation cancellation and timeout using ContextEval diff --git a/examples/example_cel_custom_functions_test.go b/examples/example_cel_custom_functions_test.go index 20628b88d..0664ff0d3 100644 --- a/examples/example_cel_custom_functions_test.go +++ b/examples/example_cel_custom_functions_test.go @@ -18,9 +18,9 @@ import ( "fmt" "log" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // Example_cel_Overload showcases defining custom global functions with cel.Overload diff --git a/examples/example_cel_custom_macros_test.go b/examples/example_cel_custom_macros_test.go index 30e07be1f..b970415f4 100644 --- a/examples/example_cel_custom_macros_test.go +++ b/examples/example_cel_custom_macros_test.go @@ -18,12 +18,12 @@ import ( "fmt" "log" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/parser" ) // Example_cel_CustomMacros showcases defining custom AST transformation macros diff --git a/examples/example_cel_execution_cost_test.go b/examples/example_cel_execution_cost_test.go index 4e80dba7b..69e6411b5 100644 --- a/examples/example_cel_execution_cost_test.go +++ b/examples/example_cel_execution_cost_test.go @@ -19,9 +19,9 @@ import ( "log" "strings" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/types/ref" ) type exampleCostEstimator struct { diff --git a/examples/example_cel_logic_and_conditions_test.go b/examples/example_cel_logic_and_conditions_test.go index 40e3b7a19..ed3d9792c 100644 --- a/examples/example_cel_logic_and_conditions_test.go +++ b/examples/example_cel_logic_and_conditions_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "github.com/authzed/cel-go/cel" + "github.com/google/cel-go/cel" ) // Example_cel_LogicAndConditions showcases logical operators, conditional (ternary) operator, and evaluation diff --git a/examples/example_cel_native_structs_test.go b/examples/example_cel_native_structs_test.go index 00750d264..076a254db 100644 --- a/examples/example_cel_native_structs_test.go +++ b/examples/example_cel_native_structs_test.go @@ -19,8 +19,8 @@ import ( "log" "reflect" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/ext" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/ext" ) type User struct { diff --git a/examples/example_cel_operators_test.go b/examples/example_cel_operators_test.go index e6625da92..895079af3 100644 --- a/examples/example_cel_operators_test.go +++ b/examples/example_cel_operators_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "github.com/authzed/cel-go/cel" + "github.com/google/cel-go/cel" ) // Example_cel_Arithmetic showcases negation, basic operations, modulo, and precedence diff --git a/examples/example_cel_protocol_buffers_test.go b/examples/example_cel_protocol_buffers_test.go index ce622757e..26a46bc6c 100644 --- a/examples/example_cel_protocol_buffers_test.go +++ b/examples/example_cel_protocol_buffers_test.go @@ -18,8 +18,8 @@ import ( "fmt" "log" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/test/proto3pb" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/test/proto3pb" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/wrapperspb" ) diff --git a/examples/example_cel_strings_and_numbers_test.go b/examples/example_cel_strings_and_numbers_test.go index 3c253ba85..8be7cc820 100644 --- a/examples/example_cel_strings_and_numbers_test.go +++ b/examples/example_cel_strings_and_numbers_test.go @@ -18,8 +18,8 @@ import ( "fmt" "log" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/ext" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/ext" ) // Example_cel_StringsAndNumbers showcases string functions, concatenation, and numeric comparisons diff --git a/examples/example_cel_time_test.go b/examples/example_cel_time_test.go index 62395cda5..448e6a977 100644 --- a/examples/example_cel_time_test.go +++ b/examples/example_cel_time_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "github.com/authzed/cel-go/cel" + "github.com/google/cel-go/cel" ) // Example_cel_TimestampsAndDurations showcases timestamps, durations, arithmetic, and field access diff --git a/examples/example_cel_transforming_data_test.go b/examples/example_cel_transforming_data_test.go index a0432f17d..594582cf8 100644 --- a/examples/example_cel_transforming_data_test.go +++ b/examples/example_cel_transforming_data_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "github.com/authzed/cel-go/cel" + "github.com/google/cel-go/cel" ) // Example_cel_TransformingData showcases building maps, transforming lists with map(), diff --git a/examples/example_cel_type_conversions_test.go b/examples/example_cel_type_conversions_test.go index 95c252f6b..1681e6c0a 100644 --- a/examples/example_cel_type_conversions_test.go +++ b/examples/example_cel_type_conversions_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "github.com/authzed/cel-go/cel" + "github.com/google/cel-go/cel" ) // Example_cel_TypeConversions showcases type casting functions (int, uint, double, string, bytes, dyn) diff --git a/ext/BUILD.bazel b/ext/BUILD.bazel index 3d8631351..f362fd97b 100644 --- a/ext/BUILD.bazel +++ b/ext/BUILD.bazel @@ -24,7 +24,7 @@ go_library( "sets.go", "strings.go", ], - importpath = "github.com/authzed/cel-go/ext", + importpath = "github.com/google/cel-go/ext", visibility = ["//visibility:public"], deps = [ "//cel:go_default_library", diff --git a/ext/bindings.go b/ext/bindings.go index e0eefa1c1..89766d60a 100644 --- a/ext/bindings.go +++ b/ext/bindings.go @@ -22,12 +22,12 @@ import ( "strings" "sync" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" - "github.com/authzed/cel-go/interpreter" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/interpreter" ) // Bindings returns a cel.EnvOption to configure support for local variable diff --git a/ext/bindings_test.go b/ext/bindings_test.go index 5d8490edc..4999cc416 100644 --- a/ext/bindings_test.go +++ b/ext/bindings_test.go @@ -20,14 +20,14 @@ import ( "sync" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/interpreter" - "github.com/authzed/cel-go/test" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "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/test" ) var bindingTests = []struct { diff --git a/ext/comprehensions.go b/ext/comprehensions.go index 6fab5c98e..adb22912b 100644 --- a/ext/comprehensions.go +++ b/ext/comprehensions.go @@ -18,13 +18,13 @@ import ( "fmt" "math" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/parser" ) const ( diff --git a/ext/comprehensions_test.go b/ext/comprehensions_test.go index c7ba2f86f..986547d97 100644 --- a/ext/comprehensions_test.go +++ b/ext/comprehensions_test.go @@ -19,10 +19,10 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/interpreter" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/interpreter" ) func TestTwoVarComprehensions(t *testing.T) { diff --git a/ext/costs.go b/ext/costs.go index bfec2cb9e..d2cf7c757 100644 --- a/ext/costs.go +++ b/ext/costs.go @@ -17,12 +17,12 @@ package ext import ( "math" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" ) var ( diff --git a/ext/encoders.go b/ext/encoders.go index 302e9aefe..97fc932a5 100644 --- a/ext/encoders.go +++ b/ext/encoders.go @@ -20,11 +20,11 @@ import ( "fmt" "math" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/interpreter" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/interpreter" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/structpb" ) diff --git a/ext/encoders_test.go b/ext/encoders_test.go index 51dd9f7e0..dde2200da 100644 --- a/ext/encoders_test.go +++ b/ext/encoders_test.go @@ -20,8 +20,8 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" ) func TestEncoders(t *testing.T) { diff --git a/ext/extension_option_factory.go b/ext/extension_option_factory.go index 5b1a64f8c..e68cf5bc7 100644 --- a/ext/extension_option_factory.go +++ b/ext/extension_option_factory.go @@ -17,8 +17,8 @@ package ext import ( "fmt" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/env" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/env" ) // ExtensionOptionFactory converts an ExtensionConfig value to a CEL environment option. diff --git a/ext/extension_option_factory_test.go b/ext/extension_option_factory_test.go index e6a66ba55..603aedf53 100644 --- a/ext/extension_option_factory_test.go +++ b/ext/extension_option_factory_test.go @@ -18,8 +18,8 @@ import ( "fmt" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/env" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/env" ) func TestExtensionOptionFactoryInvalidExtension(t *testing.T) { diff --git a/ext/formatting.go b/ext/formatting.go index 5409086f6..35fb17048 100644 --- a/ext/formatting.go +++ b/ext/formatting.go @@ -26,12 +26,12 @@ import ( "golang.org/x/text/language" "golang.org/x/text/message" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" ) type clauseImpl func(ref.Val, string) (string, error) diff --git a/ext/formatting_test.go b/ext/formatting_test.go index 4cc3ac1b2..e77e3a936 100644 --- a/ext/formatting_test.go +++ b/ext/formatting_test.go @@ -24,12 +24,12 @@ import ( "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" ) func TestStringFormat(t *testing.T) { diff --git a/ext/formatting_v2.go b/ext/formatting_v2.go index 3972998c2..f923cc7e1 100644 --- a/ext/formatting_v2.go +++ b/ext/formatting_v2.go @@ -24,11 +24,11 @@ import ( "time" "unicode" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" ) type clauseImplV2 func(ref.Val) (string, error) diff --git a/ext/formatting_v2_test.go b/ext/formatting_v2_test.go index 9f2edbc67..a183dba38 100644 --- a/ext/formatting_v2_test.go +++ b/ext/formatting_v2_test.go @@ -24,12 +24,12 @@ import ( "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" ) func TestStringsWithExtensionV2(t *testing.T) { diff --git a/ext/guards.go b/ext/guards.go index 4606a64ce..1461c0416 100644 --- a/ext/guards.go +++ b/ext/guards.go @@ -15,9 +15,9 @@ package ext import ( - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // function invocation guards for common call signatures within extension functions. diff --git a/ext/lists.go b/ext/lists.go index af1e3adf0..3d0e67642 100644 --- a/ext/lists.go +++ b/ext/lists.go @@ -19,16 +19,16 @@ import ( "math" "sort" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" - "github.com/authzed/cel-go/interpreter" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "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/common/types/traits" + "github.com/google/cel-go/interpreter" + "github.com/google/cel-go/parser" ) var comparableTypes = []*cel.Type{ diff --git a/ext/lists_test.go b/ext/lists_test.go index ec7817f4f..bf01801cb 100644 --- a/ext/lists_test.go +++ b/ext/lists_test.go @@ -19,11 +19,11 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/types" - proto2pb "github.com/authzed/cel-go/test/proto2pb" + proto2pb "github.com/google/cel-go/test/proto2pb" ) func TestLists(t *testing.T) { diff --git a/ext/math.go b/ext/math.go index fb1859adc..e67b205de 100644 --- a/ext/math.go +++ b/ext/math.go @@ -19,13 +19,13 @@ import ( "math" "strings" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" - "github.com/authzed/cel-go/interpreter" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/interpreter" ) // Math returns a cel.EnvOption to configure namespaced math helper macros and diff --git a/ext/math_test.go b/ext/math_test.go index 413be8e8c..8b47cae64 100644 --- a/ext/math_test.go +++ b/ext/math_test.go @@ -19,9 +19,9 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/types" ) func TestMath(t *testing.T) { diff --git a/ext/native.go b/ext/native.go index 81f3e9279..d9f5fab0d 100644 --- a/ext/native.go +++ b/ext/native.go @@ -25,11 +25,11 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/pb" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/pb" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" structpb "google.golang.org/protobuf/types/known/structpb" ) diff --git a/ext/native_test.go b/ext/native_test.go index 3a37194aa..75c024d1e 100644 --- a/ext/native_test.go +++ b/ext/native_test.go @@ -26,16 +26,16 @@ import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/pb" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" - "github.com/authzed/cel-go/test" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/pb" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/test" structpb "google.golang.org/protobuf/types/known/structpb" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" ) func TestNativeTypes(t *testing.T) { diff --git a/ext/network.go b/ext/network.go index e1177d70b..bca065707 100644 --- a/ext/network.go +++ b/ext/network.go @@ -20,12 +20,12 @@ import ( "net/netip" "reflect" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/interpreter" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/interpreter" ) const ( diff --git a/ext/network_test.go b/ext/network_test.go index 28dad5429..592c07494 100644 --- a/ext/network_test.go +++ b/ext/network_test.go @@ -19,9 +19,9 @@ import ( "reflect" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/types" ) func TestNetwork_Success(t *testing.T) { diff --git a/ext/protos.go b/ext/protos.go index a425dcf7c..b09db25b0 100644 --- a/ext/protos.go +++ b/ext/protos.go @@ -17,8 +17,8 @@ package ext import ( "math" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/ast" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/ast" ) // Protos returns a cel.EnvOption to configure extended macros and functions for diff --git a/ext/protos_test.go b/ext/protos_test.go index 6c5aede00..739dddf3d 100644 --- a/ext/protos_test.go +++ b/ext/protos_test.go @@ -20,13 +20,13 @@ import ( "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" - "github.com/authzed/cel-go/test" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/test" - proto2pb "github.com/authzed/cel-go/test/proto2pb" + proto2pb "github.com/google/cel-go/test/proto2pb" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" ) diff --git a/ext/regex.go b/ext/regex.go index fb0c2a70a..bd222f170 100644 --- a/ext/regex.go +++ b/ext/regex.go @@ -22,12 +22,12 @@ import ( "strconv" "strings" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/interpreter" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/interpreter" ) const ( diff --git a/ext/regex_test.go b/ext/regex_test.go index cd0722bd2..24c5582ea 100644 --- a/ext/regex_test.go +++ b/ext/regex_test.go @@ -19,8 +19,8 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" ) func TestRegex(t *testing.T) { diff --git a/ext/sets.go b/ext/sets.go index 79e256b22..63c019ad9 100644 --- a/ext/sets.go +++ b/ext/sets.go @@ -15,14 +15,14 @@ package ext import ( - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" - "github.com/authzed/cel-go/interpreter" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/interpreter" ) // Sets returns a cel.EnvOption to configure namespaced set relationship diff --git a/ext/sets_test.go b/ext/sets_test.go index 92182ade8..4ea409b67 100644 --- a/ext/sets_test.go +++ b/ext/sets_test.go @@ -20,12 +20,12 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" ) func TestSets(t *testing.T) { diff --git a/ext/strings.go b/ext/strings.go index 5e5b433fe..1f7732f2f 100644 --- a/ext/strings.go +++ b/ext/strings.go @@ -27,13 +27,13 @@ import ( "golang.org/x/text/language" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" - "github.com/authzed/cel-go/interpreter" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/interpreter" ) const ( diff --git a/ext/strings_test.go b/ext/strings_test.go index 23e07e465..276a3efd5 100644 --- a/ext/strings_test.go +++ b/ext/strings_test.go @@ -21,10 +21,10 @@ import ( "time" "unicode/utf8" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // TODO: move these tests to a conformance test. diff --git a/go.mod b/go.mod index b5226a335..ad223de71 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/authzed/cel-go +module github.com/google/cel-go go 1.23.0 diff --git a/interpreter/BUILD.bazel b/interpreter/BUILD.bazel index 2a349f0bf..40ac2ba69 100644 --- a/interpreter/BUILD.bazel +++ b/interpreter/BUILD.bazel @@ -23,7 +23,7 @@ go_library( "prune.go", "runtimecost.go", ], - importpath = "github.com/authzed/cel-go/interpreter", + importpath = "github.com/google/cel-go/interpreter", deps = [ "//common:go_default_library", "//common/ast:go_default_library", diff --git a/interpreter/activation.go b/interpreter/activation.go index 6d4bae3b7..bc9296ed4 100644 --- a/interpreter/activation.go +++ b/interpreter/activation.go @@ -18,7 +18,7 @@ import ( "errors" "fmt" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) // Activation used to resolve identifiers by name and references by id. diff --git a/interpreter/activation_test.go b/interpreter/activation_test.go index 28aa3a857..b0fed2102 100644 --- a/interpreter/activation_test.go +++ b/interpreter/activation_test.go @@ -18,8 +18,8 @@ import ( "testing" "time" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) func TestActivation(t *testing.T) { diff --git a/interpreter/async.go b/interpreter/async.go index fff5da9a6..4e391196b 100644 --- a/interpreter/async.go +++ b/interpreter/async.go @@ -23,9 +23,9 @@ import ( "sync" "sync/atomic" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // Async extension function support. diff --git a/interpreter/async_test.go b/interpreter/async_test.go index 0f16d88e8..242a93310 100644 --- a/interpreter/async_test.go +++ b/interpreter/async_test.go @@ -24,15 +24,15 @@ import ( "testing" "time" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/parser" ) // asyncReturning returns an AsyncOp that immediately produces the given value, while counting the diff --git a/interpreter/attribute_patterns.go b/interpreter/attribute_patterns.go index fa00e37ed..bbaca5226 100644 --- a/interpreter/attribute_patterns.go +++ b/interpreter/attribute_patterns.go @@ -18,9 +18,9 @@ import ( "fmt" "strings" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // AttributePattern represents a top-level variable with an optional set of qualifier patterns. diff --git a/interpreter/attribute_patterns_test.go b/interpreter/attribute_patterns_test.go index 0863589ed..9fbbc7d9d 100644 --- a/interpreter/attribute_patterns_test.go +++ b/interpreter/attribute_patterns_test.go @@ -18,8 +18,8 @@ import ( "fmt" "testing" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/types" ) // attr describes a simplified format for specifying common Attribute and Qualifier values for diff --git a/interpreter/attributes.go b/interpreter/attributes.go index 721a1df05..26d8eb0f3 100644 --- a/interpreter/attributes.go +++ b/interpreter/attributes.go @@ -18,10 +18,10 @@ import ( "fmt" "strings" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" ) // AttributeFactory provides methods creating Attribute and Qualifier values. diff --git a/interpreter/attributes_test.go b/interpreter/attributes_test.go index 7e8cd5d3c..1dea98d9a 100644 --- a/interpreter/attributes_test.go +++ b/interpreter/attributes_test.go @@ -20,19 +20,19 @@ import ( "reflect" "testing" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/stdlib" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/stdlib" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" anypb "google.golang.org/protobuf/types/known/anypb" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" ) func TestAttributesAbsoluteAttr(t *testing.T) { diff --git a/interpreter/decorators.go b/interpreter/decorators.go index 9e55f05f0..9c973664a 100644 --- a/interpreter/decorators.go +++ b/interpreter/decorators.go @@ -15,10 +15,10 @@ package interpreter import ( - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" ) // InterpretableDecorator is a functional interface for decorating or replacing diff --git a/interpreter/dispatcher.go b/interpreter/dispatcher.go index b2a1eca89..8f0bdb7b8 100644 --- a/interpreter/dispatcher.go +++ b/interpreter/dispatcher.go @@ -17,7 +17,7 @@ package interpreter import ( "fmt" - "github.com/authzed/cel-go/common/functions" + "github.com/google/cel-go/common/functions" ) // Dispatcher resolves function calls to their appropriate overload. diff --git a/interpreter/evalstate.go b/interpreter/evalstate.go index d0b8094e2..4bdd1fdc7 100644 --- a/interpreter/evalstate.go +++ b/interpreter/evalstate.go @@ -15,7 +15,7 @@ package interpreter import ( - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/ref" ) // EvalState tracks the values associated with expression ids during execution. diff --git a/interpreter/frame.go b/interpreter/frame.go index a5b0c526c..20ab313c8 100644 --- a/interpreter/frame.go +++ b/interpreter/frame.go @@ -21,9 +21,9 @@ import ( "sync" "sync/atomic" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // evalContext contains the stateful information needed for a single evaluation. diff --git a/interpreter/frame_test.go b/interpreter/frame_test.go index fe4daf7b9..fb9d629cc 100644 --- a/interpreter/frame_test.go +++ b/interpreter/frame_test.go @@ -18,8 +18,8 @@ import ( "context" "testing" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) func TestFrameCheckInterrupt(t *testing.T) { diff --git a/interpreter/functions/BUILD.bazel b/interpreter/functions/BUILD.bazel index 0393b9251..4a80c3ea0 100644 --- a/interpreter/functions/BUILD.bazel +++ b/interpreter/functions/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "functions.go", ], - importpath = "github.com/authzed/cel-go/interpreter/functions", + importpath = "github.com/google/cel-go/interpreter/functions", deps = [ "//common/functions:go_default_library", ], diff --git a/interpreter/functions/functions.go b/interpreter/functions/functions.go index 331f39e9f..21ffb6924 100644 --- a/interpreter/functions/functions.go +++ b/interpreter/functions/functions.go @@ -16,7 +16,7 @@ // interpreter and as declared within the checker#StandardDeclarations. package functions -import fn "github.com/authzed/cel-go/common/functions" +import fn "github.com/google/cel-go/common/functions" // Overload defines a named overload of a function, indicating an operand trait // which must be present on the first argument to the overload as well as one diff --git a/interpreter/interpretable.go b/interpreter/interpretable.go index 7e92984b5..906c4f805 100644 --- a/interpreter/interpretable.go +++ b/interpreter/interpretable.go @@ -18,12 +18,12 @@ import ( "fmt" "sync" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" ) // Interpretable evaluates an Activation and produces a value. diff --git a/interpreter/interpreter.go b/interpreter/interpreter.go index 92aa9d0cd..ef13ab922 100644 --- a/interpreter/interpreter.go +++ b/interpreter/interpreter.go @@ -20,10 +20,10 @@ package interpreter import ( "errors" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // PlannerOption configures the program plan options during interpretable setup. diff --git a/interpreter/interpreter_test.go b/interpreter/interpreter_test.go index 90e95f0f1..bcb0f5758 100644 --- a/interpreter/interpreter_test.go +++ b/interpreter/interpreter_test.go @@ -25,26 +25,26 @@ import ( "testing" "time" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/stdlib" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common" + "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/functions" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/stdlib" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" structpb "google.golang.org/protobuf/types/known/structpb" tpb "google.golang.org/protobuf/types/known/timestamppb" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" - proto2pb "github.com/authzed/cel-go/test/proto2pb" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto2pb "github.com/google/cel-go/test/proto2pb" + proto3pb "github.com/google/cel-go/test/proto3pb" ) type testCase struct { diff --git a/interpreter/optimizations.go b/interpreter/optimizations.go index 5a90513b6..2fc87e693 100644 --- a/interpreter/optimizations.go +++ b/interpreter/optimizations.go @@ -17,8 +17,8 @@ package interpreter import ( "regexp" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // MatchesRegexOptimization optimizes the 'matches' standard library function by compiling the regex pattern and diff --git a/interpreter/planner.go b/interpreter/planner.go index d9ccad4a3..396a9803f 100644 --- a/interpreter/planner.go +++ b/interpreter/planner.go @@ -18,11 +18,11 @@ import ( "fmt" "strings" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" ) // newPlanner creates an interpretablePlanner which references a Dispatcher, TypeProvider, diff --git a/interpreter/prune.go b/interpreter/prune.go index ea659757b..1662c1c1b 100644 --- a/interpreter/prune.go +++ b/interpreter/prune.go @@ -15,12 +15,12 @@ package interpreter import ( - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" ) type astPruner struct { diff --git a/interpreter/prune_test.go b/interpreter/prune_test.go index ad6f3954a..3d12abc5a 100644 --- a/interpreter/prune_test.go +++ b/interpreter/prune_test.go @@ -17,17 +17,17 @@ package interpreter import ( "testing" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" - "github.com/authzed/cel-go/parser" - "github.com/authzed/cel-go/test" + "github.com/google/cel-go/common" + "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/common/types/traits" + "github.com/google/cel-go/parser" + "github.com/google/cel-go/test" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" ) type testInfo struct { diff --git a/interpreter/runtimecost.go b/interpreter/runtimecost.go index e9a7b4a70..81e4ef63c 100644 --- a/interpreter/runtimecost.go +++ b/interpreter/runtimecost.go @@ -18,11 +18,11 @@ import ( "errors" "math" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/common/types/traits" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" ) // WARNING: Any changes to cost calculations in this file require a corresponding change in checker/cost.go diff --git a/interpreter/runtimecost_test.go b/interpreter/runtimecost_test.go index a672e1e20..597d73ef9 100644 --- a/interpreter/runtimecost_test.go +++ b/interpreter/runtimecost_test.go @@ -23,16 +23,16 @@ import ( "testing" "time" - "github.com/authzed/cel-go/checker" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/overloads" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/parser" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" ) func TestTrackCostAdvanced(t *testing.T) { diff --git a/parser/BUILD.bazel b/parser/BUILD.bazel index 5726d2b5e..97bc9bd43 100644 --- a/parser/BUILD.bazel +++ b/parser/BUILD.bazel @@ -16,7 +16,7 @@ go_library( "unescape.go", "unparser.go", ], - importpath = "github.com/authzed/cel-go/parser", + importpath = "github.com/google/cel-go/parser", visibility = ["//visibility:public"], deps = [ "//common:go_default_library", diff --git a/parser/errors.go b/parser/errors.go index 013714a31..c3cec01a8 100644 --- a/parser/errors.go +++ b/parser/errors.go @@ -15,7 +15,7 @@ package parser import ( - "github.com/authzed/cel-go/common" + "github.com/google/cel-go/common" ) // parseErrors is a specialization of Errors. diff --git a/parser/gen/BUILD.bazel b/parser/gen/BUILD.bazel index e7f9d9fc4..3efed87b7 100644 --- a/parser/gen/BUILD.bazel +++ b/parser/gen/BUILD.bazel @@ -19,7 +19,7 @@ go_library( "CEL.tokens", "CELLexer.tokens", ], - importpath = "github.com/authzed/cel-go/parser/gen", + importpath = "github.com/google/cel-go/parser/gen", deps = [ "@com_github_antlr4_go_antlr_v4//:go_default_library", ], diff --git a/parser/helper.go b/parser/helper.go index bb2efed7a..84bef80d5 100644 --- a/parser/helper.go +++ b/parser/helper.go @@ -19,10 +19,10 @@ import ( antlr "github.com/antlr4-go/antlr/v4" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) type parserHelper struct { diff --git a/parser/helper_test.go b/parser/helper_test.go index c7f983f34..d6602bc7b 100644 --- a/parser/helper_test.go +++ b/parser/helper_test.go @@ -17,8 +17,8 @@ package parser import ( "testing" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" "google.golang.org/protobuf/proto" ) diff --git a/parser/input.go b/parser/input.go index f9349a1e4..44792455d 100644 --- a/parser/input.go +++ b/parser/input.go @@ -17,7 +17,7 @@ package parser import ( antlr "github.com/antlr4-go/antlr/v4" - "github.com/authzed/cel-go/common/runes" + "github.com/google/cel-go/common/runes" ) type charStream struct { diff --git a/parser/macro.go b/parser/macro.go index 3e1b5775b..1ef43c4b5 100644 --- a/parser/macro.go +++ b/parser/macro.go @@ -17,11 +17,11 @@ package parser import ( "fmt" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // MacroOpt defines a functional option for configuring macro behavior. diff --git a/parser/macro_test.go b/parser/macro_test.go index 5c6c17e74..1d056f644 100644 --- a/parser/macro_test.go +++ b/parser/macro_test.go @@ -17,8 +17,8 @@ package parser import ( "testing" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" ) func TestReceiverVarArgMacro(t *testing.T) { diff --git a/parser/parser.go b/parser/parser.go index 2fb7fbadf..338233543 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -25,12 +25,12 @@ import ( antlr "github.com/antlr4-go/antlr/v4" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/runes" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/parser/gen" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/runes" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/parser/gen" ) // Parser encapsulates the context necessary to perform parsing for different expressions. diff --git a/parser/parser_test.go b/parser/parser_test.go index 56c1fb36f..88527d813 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -21,11 +21,11 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/debug" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/test" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/debug" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/test" ) var testCases = []testInfo{ diff --git a/parser/unparser.go b/parser/unparser.go index 6722601c3..d503a450e 100644 --- a/parser/unparser.go +++ b/parser/unparser.go @@ -21,10 +21,10 @@ import ( "strconv" "strings" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // Unparse takes an input expression and source position information and generates a human-readable diff --git a/parser/unparser_test.go b/parser/unparser_test.go index 8eab160dc..7862e5ca0 100644 --- a/parser/unparser_test.go +++ b/parser/unparser_test.go @@ -19,9 +19,9 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/operators" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" "google.golang.org/protobuf/proto" diff --git a/policy/BUILD.bazel b/policy/BUILD.bazel index 1f273238d..d443c6d7c 100644 --- a/policy/BUILD.bazel +++ b/policy/BUILD.bazel @@ -37,7 +37,7 @@ go_library( "test_tag_handler_k8s.go", "yaml.go", ], - importpath = "github.com/authzed/cel-go/policy", + importpath = "github.com/google/cel-go/policy", deps = [ "//cel:go_default_library", "//common:go_default_library", diff --git a/policy/compiler.go b/policy/compiler.go index 5d7507ac6..2ca84e0a2 100644 --- a/policy/compiler.go +++ b/policy/compiler.go @@ -19,13 +19,13 @@ package policy import ( "fmt" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/containers" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common" + "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" ) // CompiledRule represents the variables and match blocks associated with a rule block. diff --git a/policy/compiler_test.go b/policy/compiler_test.go index fdb29aedc..6f9bc4f3b 100644 --- a/policy/compiler_test.go +++ b/policy/compiler_test.go @@ -23,11 +23,11 @@ import ( "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/ext" - "github.com/authzed/cel-go/interpreter" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/ext" + "github.com/google/cel-go/interpreter" "github.com/google/go-cmp/cmp" ) diff --git a/policy/composer.go b/policy/composer.go index 5e993957f..ef392184f 100644 --- a/policy/composer.go +++ b/policy/composer.go @@ -20,11 +20,11 @@ import ( "slices" "strings" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/types" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" ) // ComposerOption is a functional option used to configure a RuleComposer diff --git a/policy/composer_test.go b/policy/composer_test.go index 8a158fdf2..5cd601c2c 100644 --- a/policy/composer_test.go +++ b/policy/composer_test.go @@ -4,10 +4,10 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/debug" - "github.com/authzed/cel-go/ext" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/debug" + "github.com/google/cel-go/ext" ) func TestCompose_SourceInfo(t *testing.T) { diff --git a/policy/config.go b/policy/config.go index 43f466aa5..02243922b 100644 --- a/policy/config.go +++ b/policy/config.go @@ -15,9 +15,9 @@ package policy import ( - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/ext" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/env" + "github.com/google/cel-go/ext" ) // FromConfig configures a CEL policy environment from a config file. diff --git a/policy/config_test.go b/policy/config_test.go index 40bce9775..77fcce274 100644 --- a/policy/config_test.go +++ b/policy/config_test.go @@ -17,12 +17,12 @@ package policy import ( "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/env" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/env" "go.yaml.in/yaml/v3" - proto3pb "github.com/authzed/cel-go/test/proto3pb" + proto3pb "github.com/google/cel-go/test/proto3pb" ) func TestConfig(t *testing.T) { diff --git a/policy/go.mod b/policy/go.mod index 68c9404da..6fe07141a 100644 --- a/policy/go.mod +++ b/policy/go.mod @@ -1,10 +1,10 @@ -module github.com/authzed/cel-go/policy +module github.com/google/cel-go/policy go 1.23.0 require ( - github.com/authzed/cel-go v0.26.1 - github.com/authzed/cel-go/tools v0.0.0-20251023215754-a36d461be521 + github.com/google/cel-go v0.26.1 + github.com/google/cel-go/tools v0.0.0-20251023215754-a36d461be521 github.com/google/go-cmp v0.7.0 go.yaml.in/yaml/v3 v3.0.4 google.golang.org/protobuf v1.36.10 @@ -19,6 +19,6 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf // indirect ) -replace github.com/authzed/cel-go => ../. +replace github.com/google/cel-go => ../. -replace github.com/authzed/cel-go/tools => ../tools/. +replace github.com/google/cel-go/tools => ../tools/. diff --git a/policy/helper_test.go b/policy/helper_test.go index 5c815d0ae..fbb62b55a 100644 --- a/policy/helper_test.go +++ b/policy/helper_test.go @@ -19,11 +19,11 @@ import ( "os" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/test" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/env" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/test" "go.yaml.in/yaml/v3" diff --git a/policy/parser.go b/policy/parser.go index 7682ddafe..a42c7a3b9 100644 --- a/policy/parser.go +++ b/policy/parser.go @@ -20,9 +20,9 @@ import ( "go.yaml.in/yaml/v3" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/ast" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" ) type semanticType int diff --git a/policy/parser_test.go b/policy/parser_test.go index 804df04f5..f407a8600 100644 --- a/policy/parser_test.go +++ b/policy/parser_test.go @@ -18,9 +18,9 @@ import ( "fmt" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/ext" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/ext" "github.com/google/go-cmp/cmp" "go.yaml.in/yaml/v3" ) diff --git a/policy/source.go b/policy/source.go index c11ddb2a8..41abd19bd 100644 --- a/policy/source.go +++ b/policy/source.go @@ -15,7 +15,7 @@ package policy import ( - "github.com/authzed/cel-go/common" + "github.com/google/cel-go/common" ) // ByteSource converts a byte sequence and location description to a model.Source. diff --git a/policy/test/cel_test_runner.go b/policy/test/cel_test_runner.go index a653a08a1..95269e13c 100644 --- a/policy/test/cel_test_runner.go +++ b/policy/test/cel_test_runner.go @@ -18,10 +18,10 @@ import ( "os" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/tools/celtest" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/tools/celtest" ) // TestCEL triggers the celtest test runner with a list of custom options which are used to set up diff --git a/policy/test/k8s_cel_test_runner.go b/policy/test/k8s_cel_test_runner.go index 3829246e2..26e6ad114 100644 --- a/policy/test/k8s_cel_test_runner.go +++ b/policy/test/k8s_cel_test_runner.go @@ -18,8 +18,8 @@ import ( "os" "testing" - "github.com/authzed/cel-go/policy" - "github.com/authzed/cel-go/tools/celtest" + "github.com/google/cel-go/policy" + "github.com/google/cel-go/tools/celtest" ) // TestK8sCEL triggers compilation and test execution of a k8s policy which diff --git a/repl/BUILD.bazel b/repl/BUILD.bazel index efa0f6f60..2f6e627ea 100644 --- a/repl/BUILD.bazel +++ b/repl/BUILD.bazel @@ -26,7 +26,7 @@ go_library( "evaluator.go", "typefmt.go", ], - importpath = "github.com/authzed/cel-go/repl", + importpath = "github.com/google/cel-go/repl", deps = [ "//cel:go_default_library", "//checker:go_default_library", diff --git a/repl/commands.go b/repl/commands.go index 0ef82da33..bad89eb7c 100644 --- a/repl/commands.go +++ b/repl/commands.go @@ -21,8 +21,8 @@ import ( antlr "github.com/antlr4-go/antlr/v4" - "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/repl/parser" + "github.com/google/cel-go/common/env" + "github.com/google/cel-go/repl/parser" ) var ( diff --git a/repl/evaluator.go b/repl/evaluator.go index ff0b9cdff..ecfd72264 100644 --- a/repl/evaluator.go +++ b/repl/evaluator.go @@ -23,14 +23,14 @@ import ( "sort" "strings" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/env" - envlib "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/common/functions" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/ext" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/decls" + "github.com/google/cel-go/common/env" + envlib "github.com/google/cel-go/common/env" + "github.com/google/cel-go/common/functions" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/ext" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" diff --git a/repl/evaluator_test.go b/repl/evaluator_test.go index 55ad190b5..d18f015e2 100644 --- a/repl/evaluator_test.go +++ b/repl/evaluator_test.go @@ -18,11 +18,11 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/env" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/env" "github.com/google/go-cmp/cmp" - proto2pb "github.com/authzed/cel-go/test/proto2pb" + proto2pb "github.com/google/cel-go/test/proto2pb" ) var testTextDescriptorFile string = "testdata/attribute_context_fds.textproto" diff --git a/repl/go.mod b/repl/go.mod index 603668e29..400aa810d 100644 --- a/repl/go.mod +++ b/repl/go.mod @@ -1,4 +1,4 @@ -module github.com/authzed/cel-go/repl +module github.com/google/cel-go/repl go 1.23.0 @@ -6,7 +6,7 @@ require ( cel.dev/expr v0.25.1 github.com/antlr4-go/antlr/v4 v4.13.1 github.com/chzyer/readline v1.5.1 - github.com/authzed/cel-go v0.26.1 + github.com/google/cel-go v0.26.1 github.com/google/go-cmp v0.7.0 go.yaml.in/yaml/v3 v3.0.4 google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 @@ -20,6 +20,6 @@ require ( golang.org/x/text v0.22.0 // indirect ) -replace github.com/authzed/cel-go => ../. +replace github.com/google/cel-go => ../. replace cel.dev/expr => ../../cel-spec diff --git a/repl/main/BUILD.bazel b/repl/main/BUILD.bazel index 52cfbabb0..8b935c48c 100644 --- a/repl/main/BUILD.bazel +++ b/repl/main/BUILD.bazel @@ -21,14 +21,14 @@ package( go_binary( name = "main", embed = [":go_default_library"], - importpath = "github.com/authzed/cel-go/repl/main", + importpath = "github.com/google/cel-go/repl/main", visibility = ["//visibility:public"], ) go_library( name = "go_default_library", srcs = ["main.go"], - importpath = "github.com/authzed/cel-go/repl/main", + importpath = "github.com/google/cel-go/repl/main", visibility = ["//visibility:private"], deps = [ "//repl:go_default_library", diff --git a/repl/main/main.go b/repl/main/main.go index 277a880a2..bfc00242f 100644 --- a/repl/main/main.go +++ b/repl/main/main.go @@ -45,7 +45,7 @@ import ( "os" "path/filepath" - "github.com/authzed/cel-go/repl" + "github.com/google/cel-go/repl" "github.com/chzyer/readline" ) diff --git a/repl/parser/BUILD.bazel b/repl/parser/BUILD.bazel index b0e932e5f..4d6f12cb2 100644 --- a/repl/parser/BUILD.bazel +++ b/repl/parser/BUILD.bazel @@ -23,7 +23,7 @@ go_library( name = "go_default_library", srcs = glob(["*.go"], exclude=["*_test.go"]), data = glob(["*.tokens"]), - importpath = "github.com/authzed/cel-go/repl/parser", + importpath = "github.com/google/cel-go/repl/parser", deps = [ "@com_github_antlr4_go_antlr_v4//:go_default_library", ], diff --git a/repl/typefmt.go b/repl/typefmt.go index 9fb760d8a..a877f57d0 100644 --- a/repl/typefmt.go +++ b/repl/typefmt.go @@ -20,10 +20,10 @@ import ( antlr "github.com/antlr4-go/antlr/v4" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/repl/parser" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/env" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/repl/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/repl/typefmt_test.go b/repl/typefmt_test.go index 55580235d..a9c9df9ec 100644 --- a/repl/typefmt_test.go +++ b/repl/typefmt_test.go @@ -17,7 +17,7 @@ package repl import ( "testing" - "github.com/authzed/cel-go/cel" + "github.com/google/cel-go/cel" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/test/BUILD.bazel b/test/BUILD.bazel index 3ae35ea31..597010414 100644 --- a/test/BUILD.bazel +++ b/test/BUILD.bazel @@ -23,7 +23,7 @@ go_library( "expr.go", "suite.go", ], - importpath = "github.com/authzed/cel-go/test", + importpath = "github.com/google/cel-go/test", deps = [ "//common/operators:go_default_library", "//common/types:go_default_library", diff --git a/test/async.go b/test/async.go index 7f08d5c21..c550e940c 100644 --- a/test/async.go +++ b/test/async.go @@ -18,8 +18,8 @@ import ( "context" "time" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" ) // FakeRPC returns a blocking async function which simulates an RPC that succeeds after a short diff --git a/test/bench/BUILD.bazel b/test/bench/BUILD.bazel index 7091227db..5f856dc74 100644 --- a/test/bench/BUILD.bazel +++ b/test/bench/BUILD.bazel @@ -9,7 +9,7 @@ go_library( srcs = [ "bench.go", ], - importpath = "github.com/authzed/cel-go/test/bench", + importpath = "github.com/google/cel-go/test/bench", deps = [ "//cel:go_default_library", "//ext:go_default_library", diff --git a/test/bench/bench.go b/test/bench/bench.go index cd7992f36..6725faaf7 100644 --- a/test/bench/bench.go +++ b/test/bench/bench.go @@ -19,10 +19,10 @@ import ( "fmt" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/ext" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/ext" ) // Case represents a human-readable expression and an expected output given an input diff --git a/test/bench/bench_test.go b/test/bench/bench_test.go index b1834ebf1..4cfc1e85c 100644 --- a/test/bench/bench_test.go +++ b/test/bench/bench_test.go @@ -17,7 +17,7 @@ package bench import ( "testing" - "github.com/authzed/cel-go/cel" + "github.com/google/cel-go/cel" ) func BenchmarkReferenceCases(b *testing.B) { diff --git a/test/expr.go b/test/expr.go index 548c8b943..39adcee60 100644 --- a/test/expr.go +++ b/test/expr.go @@ -15,7 +15,7 @@ package test import ( - "github.com/authzed/cel-go/common/operators" + "github.com/google/cel-go/common/operators" "google.golang.org/protobuf/proto" diff --git a/test/proto2pb/BUILD.bazel b/test/proto2pb/BUILD.bazel index 4bd712d99..c7233c8f1 100644 --- a/test/proto2pb/BUILD.bazel +++ b/test/proto2pb/BUILD.bazel @@ -23,7 +23,7 @@ go_library( "test_all_types.pb.go", "test_extensions.pb.go", ], - importpath = "github.com/authzed/cel-go/test/proto2pb", + importpath = "github.com/google/cel-go/test/proto2pb", deps = [ "@org_golang_google_protobuf//proto:go_default_library", "@org_golang_google_protobuf//reflect/protoreflect:go_default_library", @@ -59,7 +59,7 @@ proto_library( go_proto_library( name = "test_all_types_go_proto", - importpath = "github.com/authzed/cel-go/test/proto2pb", + importpath = "github.com/google/cel-go/test/proto2pb", protos = [ ":test_all_types_proto", ":test_extensions_proto", diff --git a/test/proto2pb/test_all_types.proto b/test/proto2pb/test_all_types.proto index 4e72647bb..a77cc6d8b 100644 --- a/test/proto2pb/test_all_types.proto +++ b/test/proto2pb/test_all_types.proto @@ -3,7 +3,7 @@ syntax = "proto2"; package google.expr.proto2.test; -option go_package = "github.com/authzed/cel-go/test/proto2pb"; +option go_package = "github.com/google/cel-go/test/proto2pb"; import "google/protobuf/any.proto"; import "google/protobuf/duration.proto"; diff --git a/test/proto2pb/test_extensions.proto b/test/proto2pb/test_extensions.proto index 3e2e5ca77..f98ca34a4 100644 --- a/test/proto2pb/test_extensions.proto +++ b/test/proto2pb/test_extensions.proto @@ -2,7 +2,7 @@ syntax = "proto2"; package google.expr.proto2.test; -option go_package = "github.com/authzed/cel-go/test/proto2pb"; +option go_package = "github.com/google/cel-go/test/proto2pb"; import "google/protobuf/wrappers.proto"; import "test/proto2pb/test_all_types.proto"; diff --git a/test/proto3pb/BUILD.bazel b/test/proto3pb/BUILD.bazel index b33ceed4d..7b3449686 100644 --- a/test/proto3pb/BUILD.bazel +++ b/test/proto3pb/BUILD.bazel @@ -24,7 +24,7 @@ go_library( "test_all_types.pb.go", "test_import.pb.go", ], - importpath = "github.com/authzed/cel-go/test/proto3pb", + importpath = "github.com/google/cel-go/test/proto3pb", deps = [ "@org_golang_google_protobuf//proto:go_default_library", "@org_golang_google_protobuf//types/known/anypb:go_default_library", @@ -57,7 +57,7 @@ proto_library( go_proto_library( name = "test_all_types_go_proto", - importpath = "github.com/authzed/cel-go/test/proto3pb", + importpath = "github.com/google/cel-go/test/proto3pb", protos = [ ":test_all_types_proto", ":test_import_proto", diff --git a/test/proto3pb/test_all_types.proto b/test/proto3pb/test_all_types.proto index cef88cda9..f6e8f2c1e 100644 --- a/test/proto3pb/test_all_types.proto +++ b/test/proto3pb/test_all_types.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package google.expr.proto3.test; -option go_package = "github.com/authzed/cel-go/test/proto3pb"; +option go_package = "github.com/google/cel-go/test/proto3pb"; import "google/protobuf/any.proto"; import "google/protobuf/duration.proto"; diff --git a/test/proto3pb/test_import.proto b/test/proto3pb/test_import.proto index 31203de19..f508fc5d0 100644 --- a/test/proto3pb/test_import.proto +++ b/test/proto3pb/test_import.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package google.expr.proto3.test; -option go_package = "github.com/authzed/cel-go/test/proto3pb"; +option go_package = "github.com/google/cel-go/test/proto3pb"; enum ImportedGlobalEnum { IMPORT_FOO = 0; diff --git a/tools/celtest/BUILD.bazel b/tools/celtest/BUILD.bazel index 271c49821..8d5fd5a99 100644 --- a/tools/celtest/BUILD.bazel +++ b/tools/celtest/BUILD.bazel @@ -26,7 +26,7 @@ go_library( "test_coverage_reporter.go", "test_runner.go", ], - importpath = "github.com/authzed/cel-go/tools/celtest", + importpath = "github.com/google/cel-go/tools/celtest", deps = [ "//cel:go_default_library", "//common/ast:go_default_library", diff --git a/tools/celtest/test_coverage_reporter.go b/tools/celtest/test_coverage_reporter.go index ff2229ca2..1147e84c6 100644 --- a/tools/celtest/test_coverage_reporter.go +++ b/tools/celtest/test_coverage_reporter.go @@ -20,9 +20,9 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/parser" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/parser" ) // reportCoverage reports the coverage information for the provided programs. diff --git a/tools/celtest/test_coverage_reporter_test.go b/tools/celtest/test_coverage_reporter_test.go index 3cbac23ea..ae7b0c701 100644 --- a/tools/celtest/test_coverage_reporter_test.go +++ b/tools/celtest/test_coverage_reporter_test.go @@ -18,9 +18,9 @@ package celtest import ( "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/tools/compiler" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/tools/compiler" ) func TestCoverageStats(t *testing.T) { diff --git a/tools/celtest/test_runner.go b/tools/celtest/test_runner.go index 202205367..5f1e997d6 100644 --- a/tools/celtest/test_runner.go +++ b/tools/celtest/test_runner.go @@ -25,16 +25,16 @@ import ( "strings" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/ast" - "github.com/authzed/cel-go/common/debug" - "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/common/operators" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/interpreter" - "github.com/authzed/cel-go/test" - "github.com/authzed/cel-go/tools/compiler" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/debug" + "github.com/google/cel-go/common/env" + "github.com/google/cel-go/common/operators" + "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/test" + "github.com/google/cel-go/tools/compiler" "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/encoding/prototext" diff --git a/tools/celtest/test_runner_test.go b/tools/celtest/test_runner_test.go index 3b788eb68..deb6699a7 100644 --- a/tools/celtest/test_runner_test.go +++ b/tools/celtest/test_runner_test.go @@ -18,13 +18,13 @@ package celtest import ( "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/decls" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/common/types/ref" - "github.com/authzed/cel-go/policy" - "github.com/authzed/cel-go/test" - "github.com/authzed/cel-go/tools/compiler" + "github.com/google/cel-go/cel" + "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/policy" + "github.com/google/cel-go/test" + "github.com/google/cel-go/tools/compiler" "go.yaml.in/yaml/v3" diff --git a/tools/compiler/BUILD.bazel b/tools/compiler/BUILD.bazel index 6e16f3ca0..de7d0cc6e 100644 --- a/tools/compiler/BUILD.bazel +++ b/tools/compiler/BUILD.bazel @@ -24,7 +24,7 @@ go_library( srcs = [ "compiler.go", ], - importpath = "github.com/authzed/cel-go/tools/compiler", + importpath = "github.com/google/cel-go/tools/compiler", deps = [ "//cel:go_default_library", "//common:go_default_library", diff --git a/tools/compiler/compiler.go b/tools/compiler/compiler.go index 84c3110b6..4d4f69709 100644 --- a/tools/compiler/compiler.go +++ b/tools/compiler/compiler.go @@ -24,12 +24,12 @@ import ( "go.yaml.in/yaml/v3" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common" - "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/common/types" - "github.com/authzed/cel-go/ext" - "github.com/authzed/cel-go/policy" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/env" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/ext" + "github.com/google/cel-go/policy" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" diff --git a/tools/compiler/compiler_test.go b/tools/compiler/compiler_test.go index e9be8e5b3..d528eed56 100644 --- a/tools/compiler/compiler_test.go +++ b/tools/compiler/compiler_test.go @@ -18,10 +18,10 @@ import ( "reflect" "testing" - "github.com/authzed/cel-go/cel" - "github.com/authzed/cel-go/common/env" - "github.com/authzed/cel-go/ext" - "github.com/authzed/cel-go/policy" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/env" + "github.com/google/cel-go/ext" + "github.com/google/cel-go/policy" celpb "cel.dev/expr" configpb "cel.dev/expr/conformance" diff --git a/tools/go.mod b/tools/go.mod index 6f8ea594f..dc978e3aa 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -1,11 +1,11 @@ -module github.com/authzed/cel-go/tools +module github.com/google/cel-go/tools go 1.23.0 require ( cel.dev/expr v0.25.1 - github.com/authzed/cel-go v0.22.0 - github.com/authzed/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1 + github.com/google/cel-go v0.22.0 + github.com/google/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1 github.com/google/go-cmp v0.7.0 go.yaml.in/yaml/v3 v3.0.4 google.golang.org/genproto/googleapis/api v0.0.0-20250311190419-81fb87f6b8bf @@ -21,4 +21,4 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/authzed/cel-go => ../. +replace github.com/google/cel-go => ../. diff --git a/tools/go.sum b/tools/go.sum index a6b85e3cc..2d091fe5a 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -5,8 +5,8 @@ github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmO github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/authzed/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1 h1:jT/04RYwo++S9tvHggXWuAqvnc2Pi0BTHYsZYVOoMOs= -github.com/authzed/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1/go.mod h1:dgvqy3CzFx17CBMkL0s1hd0r1+rEQOo85tDpr0g6Dp4= +github.com/google/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1 h1:jT/04RYwo++S9tvHggXWuAqvnc2Pi0BTHYsZYVOoMOs= +github.com/google/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1/go.mod h1:dgvqy3CzFx17CBMkL0s1hd0r1+rEQOo85tDpr0g6Dp4= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= From 3ce516be4cd4c5656aab2f2345d99cc9faa40fc0 Mon Sep 17 00:00:00 2001 From: Maria Ines Parnisari Date: Wed, 19 Aug 2026 14:07:41 -0700 Subject: [PATCH 36/37] Re-apply authzed rename --- README.md | 8 +++--- cel/BUILD.bazel | 2 +- cel/async/BUILD.bazel | 2 +- cel/async/async.go | 10 +++---- cel/async/async_test.go | 10 +++---- cel/cel_example_test.go | 6 ++-- cel/cel_test.go | 26 ++++++++--------- cel/decls.go | 10 +++---- cel/decls_test.go | 18 ++++++------ cel/env.go | 26 ++++++++--------- cel/env_test.go | 16 +++++------ cel/fieldpaths.go | 4 +-- cel/folding.go | 12 ++++---- cel/folding_test.go | 12 ++++---- cel/inlining.go | 12 ++++---- cel/inlining_test.go | 4 +-- cel/io.go | 12 ++++---- cel/io_test.go | 12 ++++---- cel/library.go | 24 ++++++++-------- cel/macro.go | 8 +++--- cel/macro_test.go | 4 +-- cel/optimizer.go | 8 +++--- cel/optimizer_test.go | 8 +++--- cel/options.go | 22 +++++++-------- cel/program.go | 14 +++++----- cel/program_async_test.go | 14 +++++----- cel/prompt.go | 8 +++--- cel/prompt_test.go | 4 +-- cel/validator.go | 8 +++--- cel/validator_test.go | 14 +++++----- checker/BUILD.bazel | 2 +- checker/checker.go | 14 +++++----- checker/checker_test.go | 22 +++++++-------- checker/cost.go | 12 ++++---- checker/cost_test.go | 16 +++++------ checker/decls/BUILD.bazel | 2 +- checker/env.go | 10 +++---- checker/env_test.go | 12 ++++---- checker/errors.go | 6 ++-- checker/format.go | 4 +-- checker/format_test.go | 4 +-- checker/mapping.go | 2 +- checker/printer.go | 4 +-- checker/scopes.go | 2 +- checker/types.go | 2 +- codelab/README.md | 4 +-- codelab/codelab.go | 8 +++--- codelab/go.mod | 6 ++-- codelab/solution/codelab.go | 8 +++--- common/BUILD.bazel | 2 +- common/ast/BUILD.bazel | 2 +- common/ast/ast.go | 6 ++-- common/ast/ast_test.go | 10 +++---- common/ast/conversion.go | 4 +-- common/ast/conversion_test.go | 16 +++++------ common/ast/expr.go | 2 +- common/ast/expr_test.go | 6 ++-- common/ast/factory.go | 2 +- common/ast/navigable.go | 4 +-- common/ast/navigable_test.go | 20 ++++++------- common/containers/BUILD.bazel | 2 +- common/containers/container.go | 2 +- common/containers/container_test.go | 2 +- common/cost/BUILD.bazel | 2 +- common/debug/BUILD.bazel | 2 +- common/debug/debug.go | 6 ++-- common/decls/BUILD.bazel | 2 +- common/decls/decls.go | 12 ++++---- common/decls/decls_test.go | 14 +++++----- common/env/BUILD.bazel | 2 +- common/env/env.go | 4 +-- common/env/env_test.go | 10 +++---- common/functions/BUILD.bazel | 2 +- common/functions/functions.go | 2 +- common/operators/BUILD.bazel | 2 +- common/overloads/BUILD.bazel | 2 +- common/runes/BUILD.bazel | 2 +- common/source.go | 2 +- common/stdlib/BUILD.bazel | 2 +- common/stdlib/standard.go | 16 +++++------ common/types/BUILD.bazel | 2 +- common/types/bool.go | 2 +- common/types/bytes.go | 2 +- common/types/compare.go | 2 +- common/types/double.go | 2 +- common/types/double_test.go | 4 +-- common/types/duration.go | 4 +-- common/types/duration_test.go | 4 +-- common/types/err.go | 2 +- common/types/format.go | 4 +-- common/types/int.go | 2 +- common/types/int_test.go | 4 +-- common/types/iterator.go | 4 +-- common/types/json_list_test.go | 2 +- common/types/list.go | 4 +-- common/types/list_test.go | 4 +-- common/types/map.go | 6 ++-- common/types/map_test.go | 8 +++--- common/types/native.go | 4 +-- common/types/native_test.go | 16 +++++------ common/types/null.go | 2 +- common/types/null_test.go | 2 +- common/types/object.go | 4 +-- common/types/object_test.go | 4 +-- common/types/optional.go | 2 +- common/types/optional_test.go | 2 +- common/types/pb/BUILD.bazel | 2 +- common/types/pb/equal_test.go | 2 +- common/types/pb/file_test.go | 6 ++-- common/types/pb/pb_test.go | 4 +-- common/types/pb/type_test.go | 6 ++-- common/types/provider.go | 6 ++-- common/types/provider_test.go | 6 ++-- common/types/ref/BUILD.bazel | 2 +- common/types/size_calc.go | 4 +-- common/types/size_calc_test.go | 6 ++-- common/types/string.go | 4 +-- common/types/string_test.go | 4 +-- common/types/struct.go | 2 +- common/types/timestamp.go | 4 +-- common/types/timestamp_test.go | 4 +-- common/types/traits/BUILD.bazel | 2 +- common/types/traits/comparer.go | 2 +- common/types/traits/container.go | 2 +- common/types/traits/field_tester.go | 2 +- common/types/traits/indexer.go | 2 +- common/types/traits/iterator.go | 2 +- common/types/traits/lister.go | 2 +- common/types/traits/mapper.go | 2 +- common/types/traits/matcher.go | 2 +- common/types/traits/math.go | 2 +- common/types/traits/receiver.go | 2 +- common/types/traits/sizer.go | 2 +- common/types/type_test.go | 2 +- common/types/types.go | 6 ++-- common/types/types_test.go | 4 +-- common/types/uint.go | 2 +- common/types/uint_test.go | 4 +-- common/types/unknown.go | 2 +- common/types/unknown_test.go | 2 +- common/types/util.go | 2 +- conformance/conformance_test.go | 12 ++++---- conformance/go.mod | 14 +++++----- conformance/policy/policy_conformance_test.go | 12 ++++---- examples/README.md | 2 +- examples/example_cel_advanced_test.go | 2 +- examples/example_cel_collections_test.go | 2 +- examples/example_cel_compile_test.go | 4 +-- examples/example_cel_context_eval_test.go | 2 +- examples/example_cel_custom_functions_test.go | 6 ++-- examples/example_cel_custom_macros_test.go | 12 ++++---- examples/example_cel_execution_cost_test.go | 6 ++-- .../example_cel_logic_and_conditions_test.go | 2 +- examples/example_cel_native_structs_test.go | 4 +-- examples/example_cel_operators_test.go | 2 +- examples/example_cel_protocol_buffers_test.go | 4 +-- .../example_cel_strings_and_numbers_test.go | 4 +-- examples/example_cel_time_test.go | 2 +- .../example_cel_transforming_data_test.go | 2 +- examples/example_cel_type_conversions_test.go | 2 +- ext/BUILD.bazel | 2 +- ext/bindings.go | 12 ++++---- ext/bindings_test.go | 16 +++++------ ext/comprehensions.go | 14 +++++----- ext/comprehensions_test.go | 8 +++--- ext/costs.go | 12 ++++---- ext/encoders.go | 12 ++++---- ext/encoders_test.go | 4 +-- ext/extension_option_factory.go | 4 +-- ext/extension_option_factory_test.go | 4 +-- ext/formatting.go | 12 ++++---- ext/formatting_test.go | 10 +++---- ext/formatting_v2.go | 10 +++---- ext/formatting_v2_test.go | 10 +++---- ext/guards.go | 6 ++-- ext/lists.go | 22 +++++++-------- ext/lists_test.go | 8 +++--- ext/math.go | 16 +++++------ ext/math_test.go | 6 ++-- ext/native.go | 4 +-- ext/native_test.go | 14 +++++----- ext/network.go | 14 +++++----- ext/network_test.go | 6 ++-- ext/protos.go | 4 +-- ext/protos_test.go | 12 ++++---- ext/regex.go | 14 +++++----- ext/regex_test.go | 4 +-- ext/security/go.mod | 6 ++-- ext/security/hmac/BUILD.bazel | 2 +- ext/security/hmac/hmac.go | 6 ++-- ext/security/hmac/hmac_test.go | 6 ++-- ext/security/jwt/BUILD.bazel | 2 +- ext/security/jwt/jwt.go | 6 ++-- ext/security/jwt/jwt_test.go | 8 +++--- ext/sets.go | 18 ++++++------ ext/sets_test.go | 10 +++---- ext/strings.go | 16 +++++------ ext/strings_test.go | 8 +++--- go.mod | 2 +- interpreter/BUILD.bazel | 2 +- interpreter/activation.go | 2 +- interpreter/activation_test.go | 4 +-- interpreter/async.go | 6 ++-- interpreter/async_test.go | 18 ++++++------ interpreter/attribute_patterns.go | 6 ++-- interpreter/attribute_patterns_test.go | 4 +-- interpreter/attributes.go | 8 +++--- interpreter/attributes_test.go | 18 ++++++------ interpreter/decorators.go | 8 +++--- interpreter/dispatcher.go | 2 +- interpreter/evalstate.go | 2 +- interpreter/frame.go | 6 ++-- interpreter/frame_test.go | 4 +-- interpreter/functions/BUILD.bazel | 2 +- interpreter/functions/functions.go | 2 +- interpreter/interpretable.go | 12 ++++---- interpreter/interpreter.go | 8 +++--- interpreter/interpreter_test.go | 28 +++++++++---------- interpreter/optimizations.go | 4 +-- interpreter/planner.go | 12 ++++---- interpreter/prune.go | 12 ++++---- interpreter/prune_test.go | 20 ++++++------- interpreter/runtimecost.go | 12 ++++---- interpreter/runtimecost_test.go | 18 ++++++------ parser/BUILD.bazel | 2 +- parser/errors.go | 2 +- parser/gen/BUILD.bazel | 2 +- parser/helper.go | 8 +++--- parser/helper_test.go | 4 +-- parser/input.go | 2 +- parser/macro.go | 10 +++---- parser/macro_test.go | 4 +-- parser/parser.go | 12 ++++---- parser/parser_test.go | 12 ++++---- parser/unparser.go | 8 +++--- parser/unparser_test.go | 6 ++-- policy/BUILD.bazel | 2 +- policy/compiler.go | 14 +++++----- policy/compiler_test.go | 10 +++---- policy/composer.go | 10 +++---- policy/composer_test.go | 10 +++---- policy/config.go | 6 ++-- policy/config_test.go | 6 ++-- policy/go.mod | 10 +++---- policy/helper_test.go | 12 ++++---- policy/parser.go | 6 ++-- policy/parser_test.go | 6 ++-- policy/source.go | 2 +- policy/test/cel_test_runner.go | 8 +++--- policy/test/k8s_cel_test_runner.go | 4 +-- repl/BUILD.bazel | 2 +- repl/commands.go | 4 +-- repl/evaluator.go | 16 +++++------ repl/evaluator_test.go | 6 ++-- repl/go.mod | 6 ++-- repl/main/BUILD.bazel | 4 +-- repl/main/main.go | 2 +- repl/parser/BUILD.bazel | 2 +- repl/typefmt.go | 8 +++--- repl/typefmt_test.go | 2 +- test/BUILD.bazel | 2 +- test/async.go | 4 +-- test/bench/BUILD.bazel | 2 +- test/bench/bench.go | 8 +++--- test/bench/bench_test.go | 2 +- test/expr.go | 2 +- test/proto2pb/BUILD.bazel | 4 +-- test/proto2pb/test_all_types.proto | 2 +- test/proto2pb/test_extensions.proto | 2 +- test/proto3pb/BUILD.bazel | 4 +-- test/proto3pb/test_all_types.proto | 2 +- test/proto3pb/test_import.proto | 2 +- tools/celtest/BUILD.bazel | 2 +- tools/celtest/test_coverage_reporter.go | 6 ++-- tools/celtest/test_coverage_reporter_test.go | 6 ++-- tools/celtest/test_runner.go | 20 ++++++------- tools/celtest/test_runner_test.go | 14 +++++----- tools/compiler/BUILD.bazel | 2 +- tools/compiler/compiler.go | 12 ++++---- tools/compiler/compiler_test.go | 8 +++--- tools/go.mod | 10 +++---- 281 files changed, 931 insertions(+), 931 deletions(-) diff --git a/README.md b/README.md index 1a4a99281..81a9673f3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Common Expression Language -[![Go Report Card](https://goreportcard.com/badge/cel.dev/cel-go)](https://goreportcard.com/report/cel.dev/cel-go) -[![GoDoc](https://pkg.go.dev/badge/cel.dev/cel-go.svg)][6] +[![Go Report Card](https://goreportcard.com/badge/github.com/authzed/cel-go)](https://goreportcard.com/report/github.com/authzed/cel-go) +[![GoDoc](https://pkg.go.dev/badge/github.com/authzed/cel-go.svg)][6] > [!WARNING] > **On June 16, 2026, this repository will move to @@ -64,7 +64,7 @@ Let's expose `name` and `group` variables to CEL using the `cel.Variable` environment option: ```go -import "cel.dev/cel-go/cel" +import "github.com/authzed/cel-go/cel" env, err := cel.NewEnv( cel.Variable("name", cel.StringType), @@ -290,4 +290,4 @@ Released under the [Apache License](LICENSE). [3]: https://github.com/cel-expr/cel-cpp [4]: https://github.com/cel-expr/cel-go/issues [5]: https://bazel.build -[6]: https://pkg.go.dev/cel.dev/cel-go +[6]: https://pkg.go.dev/github.com/authzed/cel-go diff --git a/cel/BUILD.bazel b/cel/BUILD.bazel index c42612e19..4fd6b2070 100644 --- a/cel/BUILD.bazel +++ b/cel/BUILD.bazel @@ -23,7 +23,7 @@ go_library( "validator.go", ], embedsrcs = ["templates/authoring.tmpl"], - importpath = "cel.dev/cel-go/cel", + importpath = "github.com/authzed/cel-go/cel", visibility = ["//visibility:public"], deps = [ "//cel/async:go_default_library", diff --git a/cel/async/BUILD.bazel b/cel/async/BUILD.bazel index e77dfaec5..34fa169eb 100644 --- a/cel/async/BUILD.bazel +++ b/cel/async/BUILD.bazel @@ -9,7 +9,7 @@ go_library( srcs = [ "async.go", ], - importpath = "cel.dev/cel-go/cel/async", + importpath = "github.com/authzed/cel-go/cel/async", visibility = ["//visibility:public"], deps = [ "//common/decls:go_default_library", diff --git a/cel/async/async.go b/cel/async/async.go index b8621a784..f76b9d37a 100644 --- a/cel/async/async.go +++ b/cel/async/async.go @@ -21,11 +21,11 @@ import ( "errors" "time" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/interpreter" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/interpreter" ) // Call describes a pending or completed asynchronous function call. diff --git a/cel/async/async_test.go b/cel/async/async_test.go index 9c5ba9962..2ea197801 100644 --- a/cel/async/async_test.go +++ b/cel/async/async_test.go @@ -22,11 +22,11 @@ import ( "testing" "time" - "cel.dev/cel-go/cel/async" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/cel/async" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) type retryableTestErr struct{} diff --git a/cel/cel_example_test.go b/cel/cel_example_test.go index 5c6b7603a..b4d116e57 100644 --- a/cel/cel_example_test.go +++ b/cel/cel_example_test.go @@ -19,9 +19,9 @@ import ( "fmt" "log" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) func Example() { diff --git a/cel/cel_test.go b/cel/cel_test.go index f78a43ba5..7c82cb9e5 100644 --- a/cel/cel_test.go +++ b/cel/cel_test.go @@ -32,17 +32,17 @@ import ( "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoreflect" - "cel.dev/cel-go/checker" - celast "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/env" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/interpreter" - "cel.dev/cel-go/parser" - "cel.dev/cel-go/test" + "github.com/authzed/cel-go/checker" + celast "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/interpreter" + "github.com/authzed/cel-go/parser" + "github.com/authzed/cel-go/test" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" descpb "google.golang.org/protobuf/types/descriptorpb" @@ -51,8 +51,8 @@ import ( timestamppb "google.golang.org/protobuf/types/known/timestamppb" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" - proto2pb "cel.dev/cel-go/test/proto2pb" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto2pb "github.com/authzed/cel-go/test/proto2pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) func Test_ExampleWithBuiltins(t *testing.T) { diff --git a/cel/decls.go b/cel/decls.go index 1cc3f7b50..b19c25102 100644 --- a/cel/decls.go +++ b/cel/decls.go @@ -17,11 +17,11 @@ package cel import ( "fmt" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" celpb "cel.dev/expr" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" diff --git a/cel/decls_test.go b/cel/decls_test.go index bade15c7b..f3244b915 100644 --- a/cel/decls_test.go +++ b/cel/decls_test.go @@ -21,15 +21,15 @@ import ( "strings" "testing" - chkdecls "cel.dev/cel-go/checker/decls" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/stdlib" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + chkdecls "github.com/authzed/cel-go/checker/decls" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/stdlib" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/env.go b/cel/env.go index 772b0da48..54743c85f 100644 --- a/cel/env.go +++ b/cel/env.go @@ -23,19 +23,19 @@ import ( "strings" "sync" - "cel.dev/cel-go/checker" - chkdecls "cel.dev/cel-go/checker/decls" - "cel.dev/cel-go/common" - celast "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/env" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/stdlib" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/interpreter" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/checker" + chkdecls "github.com/authzed/cel-go/checker/decls" + "github.com/authzed/cel-go/common" + celast "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/stdlib" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/interpreter" + "github.com/authzed/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "google.golang.org/protobuf/reflect/protoreflect" diff --git a/cel/env_test.go b/cel/env_test.go index 95124c15b..8e23f1dd3 100644 --- a/cel/env_test.go +++ b/cel/env_test.go @@ -23,17 +23,17 @@ import ( "sync" "testing" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/env" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" "google.golang.org/protobuf/proto" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/fieldpaths.go b/cel/fieldpaths.go index b63c2ba2a..57d02473b 100644 --- a/cel/fieldpaths.go +++ b/cel/fieldpaths.go @@ -4,8 +4,8 @@ import ( "slices" "strings" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/types" ) // fieldPath represents a selection path to a field from a variable in a CEL environment. diff --git a/cel/folding.go b/cel/folding.go index e45dc6093..53d7d9855 100644 --- a/cel/folding.go +++ b/cel/folding.go @@ -19,12 +19,12 @@ import ( "errors" "fmt" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" ) // ConstantFoldingOption defines a functional option for configuring constant folding. diff --git a/cel/folding_test.go b/cel/folding_test.go index b894681e2..0ed60e7c4 100644 --- a/cel/folding_test.go +++ b/cel/folding_test.go @@ -25,13 +25,13 @@ import ( "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/interpreter" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/interpreter" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/inlining.go b/cel/inlining.go index 3ade2204d..ffd15d4aa 100644 --- a/cel/inlining.go +++ b/cel/inlining.go @@ -15,12 +15,12 @@ package cel import ( - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/traits" ) // InlineVariable holds a variable name to be matched and an AST representing diff --git a/cel/inlining_test.go b/cel/inlining_test.go index ed1b9270d..06d678b01 100644 --- a/cel/inlining_test.go +++ b/cel/inlining_test.go @@ -17,9 +17,9 @@ package cel_test import ( "testing" - "cel.dev/cel-go/cel" + "github.com/authzed/cel-go/cel" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) func TestInliningOptimizerNoopShadow(t *testing.T) { diff --git a/cel/io.go b/cel/io.go index 31aa304fe..f5a970715 100644 --- a/cel/io.go +++ b/cel/io.go @@ -21,12 +21,12 @@ import ( "google.golang.org/protobuf/proto" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/parser" celpb "cel.dev/expr" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" diff --git a/cel/io_test.go b/cel/io_test.go index 41da494aa..b1560cc71 100644 --- a/cel/io_test.go +++ b/cel/io_test.go @@ -22,13 +22,13 @@ import ( "google.golang.org/protobuf/proto" - "cel.dev/cel-go/checker/decls" - celast "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/checker/decls" + celast "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/library.go b/cel/library.go index eb0c9562b..02b9f2768 100644 --- a/cel/library.go +++ b/cel/library.go @@ -18,18 +18,18 @@ import ( "fmt" "math" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/env" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/stdlib" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/interpreter" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/stdlib" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/interpreter" + "github.com/authzed/cel-go/parser" ) const ( diff --git a/cel/macro.go b/cel/macro.go index aed4a8da1..76a945cef 100644 --- a/cel/macro.go +++ b/cel/macro.go @@ -17,10 +17,10 @@ package cel import ( "fmt" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/macro_test.go b/cel/macro_test.go index 85c7ab694..950693afb 100644 --- a/cel/macro_test.go +++ b/cel/macro_test.go @@ -17,8 +17,8 @@ package cel import ( "testing" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" ) func TestGlobalVarArgMacro(t *testing.T) { diff --git a/cel/optimizer.go b/cel/optimizer.go index 5c2846558..acb931d02 100644 --- a/cel/optimizer.go +++ b/cel/optimizer.go @@ -18,10 +18,10 @@ import ( "fmt" "sort" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // StaticOptimizer contains a sequence of ASTOptimizer instances which will be applied in order. diff --git a/cel/optimizer_test.go b/cel/optimizer_test.go index b1776a7ae..c616dd7dc 100644 --- a/cel/optimizer_test.go +++ b/cel/optimizer_test.go @@ -19,14 +19,14 @@ import ( "strings" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/ext" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/ext" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/cel/options.go b/cel/options.go index 21cdb3615..62529ce2d 100644 --- a/cel/options.go +++ b/cel/options.go @@ -24,17 +24,17 @@ import ( "google.golang.org/protobuf/reflect/protoregistry" "google.golang.org/protobuf/types/dynamicpb" - "cel.dev/cel-go/cel/async" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/env" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/pb" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/interpreter" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/cel/async" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/pb" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/interpreter" + "github.com/authzed/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" descpb "google.golang.org/protobuf/types/descriptorpb" diff --git a/cel/program.go b/cel/program.go index b6df752ee..9ec616bf8 100644 --- a/cel/program.go +++ b/cel/program.go @@ -20,13 +20,13 @@ import ( "fmt" "time" - "cel.dev/cel-go/cel/async" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/interpreter" + "github.com/authzed/cel-go/cel/async" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/interpreter" ) // Program is an evaluable view of an Ast. diff --git a/cel/program_async_test.go b/cel/program_async_test.go index 3a7a3ec4a..de6de7cb5 100644 --- a/cel/program_async_test.go +++ b/cel/program_async_test.go @@ -24,13 +24,13 @@ import ( "testing" "time" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/cel/async" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/ext" - "cel.dev/cel-go/interpreter" - "cel.dev/cel-go/test" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/cel/async" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/ext" + "github.com/authzed/cel-go/interpreter" + "github.com/authzed/cel-go/test" ) func TestConcurrentEval(t *testing.T) { diff --git a/cel/prompt.go b/cel/prompt.go index e5e2acde7..05bd26e40 100644 --- a/cel/prompt.go +++ b/cel/prompt.go @@ -20,10 +20,10 @@ import ( "strings" "text/template" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" ) //go:embed templates/authoring.tmpl diff --git a/cel/prompt_test.go b/cel/prompt_test.go index b206e056d..8e958d27e 100644 --- a/cel/prompt_test.go +++ b/cel/prompt_test.go @@ -20,8 +20,8 @@ import ( "sync" "testing" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/env" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/env" "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/proto" diff --git a/cel/validator.go b/cel/validator.go index 17a999f92..817e37c91 100644 --- a/cel/validator.go +++ b/cel/validator.go @@ -20,10 +20,10 @@ import ( "reflect" "regexp" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/env" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" ) const ( diff --git a/cel/validator_test.go b/cel/validator_test.go index fb31c94c0..048bb7224 100644 --- a/cel/validator_test.go +++ b/cel/validator_test.go @@ -18,13 +18,13 @@ import ( "reflect" "testing" - celenv "cel.dev/cel-go/common/env" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/test" + celenv "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/test" ) func TestValidateDurationLiterals(t *testing.T) { diff --git a/checker/BUILD.bazel b/checker/BUILD.bazel index 7c224828f..1d7f822ed 100644 --- a/checker/BUILD.bazel +++ b/checker/BUILD.bazel @@ -18,7 +18,7 @@ go_library( "scopes.go", "types.go", ], - importpath = "cel.dev/cel-go/checker", + importpath = "github.com/authzed/cel-go/checker", visibility = ["//visibility:public"], deps = [ "//checker/decls:go_default_library", diff --git a/checker/checker.go b/checker/checker.go index 1cffd3e5f..b209703f3 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -22,13 +22,13 @@ import ( "slices" "strings" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) type checker struct { diff --git a/checker/checker_test.go b/checker/checker_test.go index 47da296d3..953ba035e 100644 --- a/checker/checker_test.go +++ b/checker/checker_test.go @@ -20,18 +20,18 @@ import ( "testing" "time" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/debug" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/stdlib" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/parser" - "cel.dev/cel-go/test" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/debug" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/stdlib" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/parser" + "github.com/authzed/cel-go/test" - proto2pb "cel.dev/cel-go/test/proto2pb" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto2pb "github.com/authzed/cel-go/test/proto2pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) func testCases(t testing.TB) []testInfo { diff --git a/checker/cost.go b/checker/cost.go index 33b498051..069e89b73 100644 --- a/checker/cost.go +++ b/checker/cost.go @@ -17,12 +17,12 @@ package checker import ( "math" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/cost" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/cost" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/parser" ) // WARNING: Any changes to cost calculations in this file require a corresponding change in interpreter/runtimecost.go diff --git a/checker/cost_test.go b/checker/cost_test.go index 3a7ef835b..1a343ee4b 100644 --- a/checker/cost_test.go +++ b/checker/cost_test.go @@ -19,15 +19,15 @@ import ( "strings" "testing" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/stdlib" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/stdlib" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/parser" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) func TestCost(t *testing.T) { diff --git a/checker/decls/BUILD.bazel b/checker/decls/BUILD.bazel index fe5380166..2ee0e4dfb 100644 --- a/checker/decls/BUILD.bazel +++ b/checker/decls/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "decls.go", ], - importpath = "cel.dev/cel-go/checker/decls", + importpath = "github.com/authzed/cel-go/checker/decls", deps = [ "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", "@org_golang_google_protobuf//types/known/emptypb:go_default_library", diff --git a/checker/env.go b/checker/env.go index 1dd2f0ff7..6d726c1d6 100644 --- a/checker/env.go +++ b/checker/env.go @@ -18,11 +18,11 @@ import ( "fmt" "strings" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/parser" ) type aggregateLiteralElementType int diff --git a/checker/env_test.go b/checker/env_test.go index f8a10f29b..b6f035154 100644 --- a/checker/env_test.go +++ b/checker/env_test.go @@ -18,12 +18,12 @@ import ( "strings" "testing" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/stdlib" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/stdlib" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/parser" ) func TestOverlappingMacro(t *testing.T) { diff --git a/checker/errors.go b/checker/errors.go index dce384a80..399d1dd92 100644 --- a/checker/errors.go +++ b/checker/errors.go @@ -15,9 +15,9 @@ package checker import ( - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/types" ) // typeErrors is a specialization of Errors. diff --git a/checker/format.go b/checker/format.go index 17b1a4d24..65655a41b 100644 --- a/checker/format.go +++ b/checker/format.go @@ -18,8 +18,8 @@ import ( "fmt" "strings" - chkdecls "cel.dev/cel-go/checker/decls" - "cel.dev/cel-go/common/types" + chkdecls "github.com/authzed/cel-go/checker/decls" + "github.com/authzed/cel-go/common/types" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/checker/format_test.go b/checker/format_test.go index 68afc51fc..6351d61a4 100644 --- a/checker/format_test.go +++ b/checker/format_test.go @@ -17,8 +17,8 @@ package checker import ( "testing" - "cel.dev/cel-go/checker/decls" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/checker/decls" + "github.com/authzed/cel-go/common/types" ) func TestFormatType(t *testing.T) { diff --git a/checker/mapping.go b/checker/mapping.go index 7018d8199..d5b3bdf26 100644 --- a/checker/mapping.go +++ b/checker/mapping.go @@ -15,7 +15,7 @@ package checker import ( - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/common/types" ) type mapping struct { diff --git a/checker/printer.go b/checker/printer.go index c90711e4c..deadde3bc 100644 --- a/checker/printer.go +++ b/checker/printer.go @@ -17,8 +17,8 @@ package checker import ( "sort" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/debug" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/debug" ) type semanticAdorner struct { diff --git a/checker/scopes.go b/checker/scopes.go index 88b664af6..afa5dbe4e 100644 --- a/checker/scopes.go +++ b/checker/scopes.go @@ -17,7 +17,7 @@ package checker import ( "strings" - "cel.dev/cel-go/common/decls" + "github.com/authzed/cel-go/common/decls" ) // Scopes represents nested Decl sets where the Scopes value contains a Groups containing all diff --git a/checker/types.go b/checker/types.go index b716f4e9b..e94d8c73f 100644 --- a/checker/types.go +++ b/checker/types.go @@ -15,7 +15,7 @@ package checker import ( - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/common/types" ) // isDyn returns true if the input t is either type DYN or a well-known ANY message. diff --git a/codelab/README.md b/codelab/README.md index 27772532e..5f6caa072 100644 --- a/codelab/README.md +++ b/codelab/README.md @@ -3,6 +3,6 @@ Find the codelab instructions [here](https://codelabs.developers.google.com/codelabs/cel-go/#0). It requires some knowledge of GoLang and Protobuf. -If you get stuck, check out the [solutions](https://cel.dev/cel-go/blob/master/codelab/solution/codelab.go). +If you get stuck, check out the [solutions](https://github.com/authzed/cel-go/blob/master/codelab/solution/codelab.go). -If you find a bug or want to make an improvement, PRs and issues are welcome. Please follow the [contributing guidelines](https://cel.dev/cel-go/blob/master/CONTRIBUTING.md). +If you find a bug or want to make an improvement, PRs and issues are welcome. Please follow the [contributing guidelines](https://github.com/authzed/cel-go/blob/master/CONTRIBUTING.md). diff --git a/codelab/codelab.go b/codelab/codelab.go index 77e7eeb3d..89a8f1983 100644 --- a/codelab/codelab.go +++ b/codelab/codelab.go @@ -24,10 +24,10 @@ import ( "strings" "time" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" "github.com/golang/glog" "google.golang.org/protobuf/encoding/protojson" diff --git a/codelab/go.mod b/codelab/go.mod index 40128a408..f664521b4 100644 --- a/codelab/go.mod +++ b/codelab/go.mod @@ -1,9 +1,9 @@ -module cel.dev/cel-go/codelab +module github.com/authzed/cel-go/codelab go 1.23.0 require ( - cel.dev/cel-go v0.21.0 + github.com/authzed/cel-go v0.21.0 github.com/golang/glog v1.2.4 google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 google.golang.org/protobuf v1.36.10 @@ -17,4 +17,4 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect ) -replace cel.dev/cel-go => ../. +replace github.com/authzed/cel-go => ../. diff --git a/codelab/solution/codelab.go b/codelab/solution/codelab.go index f7ef829d2..5a758ded7 100644 --- a/codelab/solution/codelab.go +++ b/codelab/solution/codelab.go @@ -24,10 +24,10 @@ import ( "strings" "time" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" "github.com/golang/glog" "google.golang.org/protobuf/encoding/protojson" diff --git a/common/BUILD.bazel b/common/BUILD.bazel index 408888472..b1a5b82ab 100644 --- a/common/BUILD.bazel +++ b/common/BUILD.bazel @@ -15,7 +15,7 @@ go_library( "location.go", "source.go", ], - importpath = "cel.dev/cel-go/common", + importpath = "github.com/authzed/cel-go/common", deps = [ "//common/runes:go_default_library", "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", diff --git a/common/ast/BUILD.bazel b/common/ast/BUILD.bazel index d62a4cb6e..94ca875f9 100644 --- a/common/ast/BUILD.bazel +++ b/common/ast/BUILD.bazel @@ -14,7 +14,7 @@ go_library( "factory.go", "navigable.go", ], - importpath = "cel.dev/cel-go/common/ast", + importpath = "github.com/authzed/cel-go/common/ast", deps = [ "//common:go_default_library", "//common/types:go_default_library", diff --git a/common/ast/ast.go b/common/ast/ast.go index d65d10b59..e5f8db8df 100644 --- a/common/ast/ast.go +++ b/common/ast/ast.go @@ -18,9 +18,9 @@ package ast import ( "slices" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // AST contains a protobuf expression and source info along with CEL-native type and reference information. diff --git a/common/ast/ast_test.go b/common/ast/ast_test.go index 20e9181e1..3fd1d39a7 100644 --- a/common/ast/ast_test.go +++ b/common/ast/ast_test.go @@ -20,11 +20,11 @@ import ( "reflect" "testing" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" ) diff --git a/common/ast/conversion.go b/common/ast/conversion.go index 5d032cc1d..c0fad20ad 100644 --- a/common/ast/conversion.go +++ b/common/ast/conversion.go @@ -19,8 +19,8 @@ import ( "google.golang.org/protobuf/proto" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" celpb "cel.dev/expr" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" diff --git a/common/ast/conversion_test.go b/common/ast/conversion_test.go index a7e799a4f..02091ae97 100644 --- a/common/ast/conversion_test.go +++ b/common/ast/conversion_test.go @@ -23,14 +23,14 @@ import ( "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" - chkdecls "cel.dev/cel-go/checker/decls" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/parser" + chkdecls "github.com/authzed/cel-go/checker/decls" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/ast/expr.go b/common/ast/expr.go index 459311f0d..7c4a588d9 100644 --- a/common/ast/expr.go +++ b/common/ast/expr.go @@ -15,7 +15,7 @@ package ast import ( - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) // ExprKind represents the expression node kind. diff --git a/common/ast/expr_test.go b/common/ast/expr_test.go index 339d5c5cb..c550cf828 100644 --- a/common/ast/expr_test.go +++ b/common/ast/expr_test.go @@ -19,9 +19,9 @@ import ( "reflect" "testing" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" ) func TestSetKindCase(t *testing.T) { diff --git a/common/ast/factory.go b/common/ast/factory.go index b83e5555a..140ac8be5 100644 --- a/common/ast/factory.go +++ b/common/ast/factory.go @@ -14,7 +14,7 @@ package ast -import "cel.dev/cel-go/common/types/ref" +import "github.com/authzed/cel-go/common/types/ref" // ExprFactory interfaces defines a set of methods necessary for building native expression values. type ExprFactory interface { diff --git a/common/ast/navigable.go b/common/ast/navigable.go index 3a71f865f..091c6a6f5 100644 --- a/common/ast/navigable.go +++ b/common/ast/navigable.go @@ -15,8 +15,8 @@ package ast import ( - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // NavigableExpr represents the base navigable expression value with methods to inspect the diff --git a/common/ast/navigable_test.go b/common/ast/navigable_test.go index 1784e5f71..1f2b03239 100644 --- a/common/ast/navigable_test.go +++ b/common/ast/navigable_test.go @@ -18,17 +18,17 @@ import ( "reflect" "testing" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/stdlib" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/stdlib" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/parser" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) func TestNavigateAST(t *testing.T) { diff --git a/common/containers/BUILD.bazel b/common/containers/BUILD.bazel index 7def2f256..8e4a4c538 100644 --- a/common/containers/BUILD.bazel +++ b/common/containers/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "container.go", ], - importpath = "cel.dev/cel-go/common/containers", + importpath = "github.com/authzed/cel-go/common/containers", deps = [ "//common/ast:go_default_library", ], diff --git a/common/containers/container.go b/common/containers/container.go index 2330e0764..2eab44b4a 100644 --- a/common/containers/container.go +++ b/common/containers/container.go @@ -21,7 +21,7 @@ import ( "strings" "unicode" - "cel.dev/cel-go/common/ast" + "github.com/authzed/cel-go/common/ast" ) var ( diff --git a/common/containers/container_test.go b/common/containers/container_test.go index efab6391f..8bb6ffb36 100644 --- a/common/containers/container_test.go +++ b/common/containers/container_test.go @@ -19,7 +19,7 @@ import ( "reflect" "testing" - "cel.dev/cel-go/common/ast" + "github.com/authzed/cel-go/common/ast" ) func TestContainers_ResolveCandidateNames(t *testing.T) { diff --git a/common/cost/BUILD.bazel b/common/cost/BUILD.bazel index b7d416b5f..932dd3d6f 100644 --- a/common/cost/BUILD.bazel +++ b/common/cost/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "cost.go", ], - importpath = "cel.dev/cel-go/common/cost", + importpath = "github.com/authzed/cel-go/common/cost", ) go_test( diff --git a/common/debug/BUILD.bazel b/common/debug/BUILD.bazel index 07fed4271..24d8d40d0 100644 --- a/common/debug/BUILD.bazel +++ b/common/debug/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "debug.go", ], - importpath = "cel.dev/cel-go/common/debug", + importpath = "github.com/authzed/cel-go/common/debug", deps = [ "//common:go_default_library", "//common/ast:go_default_library", diff --git a/common/debug/debug.go b/common/debug/debug.go index 67593c996..c195e2d51 100644 --- a/common/debug/debug.go +++ b/common/debug/debug.go @@ -22,9 +22,9 @@ import ( "strconv" "strings" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // Adorner returns debug metadata that will be tacked on to the string diff --git a/common/decls/BUILD.bazel b/common/decls/BUILD.bazel index 85c7d5c41..7d87692ab 100644 --- a/common/decls/BUILD.bazel +++ b/common/decls/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "decls.go", ], - importpath = "cel.dev/cel-go/common/decls", + importpath = "github.com/authzed/cel-go/common/decls", deps = [ "//checker/decls:go_default_library", "//common:go_default_library", diff --git a/common/decls/decls.go b/common/decls/decls.go index d63a202d6..ebbe6f119 100644 --- a/common/decls/decls.go +++ b/common/decls/decls.go @@ -20,12 +20,12 @@ import ( "fmt" "strings" - chkdecls "cel.dev/cel-go/checker/decls" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + chkdecls "github.com/authzed/cel-go/checker/decls" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/decls/decls_test.go b/common/decls/decls_test.go index 50ce48e41..113580766 100644 --- a/common/decls/decls_test.go +++ b/common/decls/decls_test.go @@ -23,13 +23,13 @@ import ( "google.golang.org/protobuf/proto" - chkdecls "cel.dev/cel-go/checker/decls" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + chkdecls "github.com/authzed/cel-go/checker/decls" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/env/BUILD.bazel b/common/env/BUILD.bazel index 1cd6f9ee7..81e70fbc1 100644 --- a/common/env/BUILD.bazel +++ b/common/env/BUILD.bazel @@ -25,7 +25,7 @@ go_library( "env.go", "io.go", ], - importpath = "cel.dev/cel-go/common/env", + importpath = "github.com/authzed/cel-go/common/env", deps = [ "//common:go_default_library", "//common/decls:go_default_library", diff --git a/common/env/env.go b/common/env/env.go index 19e17c5da..bc6e06ba5 100644 --- a/common/env/env.go +++ b/common/env/env.go @@ -22,8 +22,8 @@ import ( "strconv" "strings" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/types" ) // NewConfig creates an instance of a YAML serializable CEL environment configuration. diff --git a/common/env/env_test.go b/common/env/env_test.go index 35e38de2f..8cc83edac 100644 --- a/common/env/env_test.go +++ b/common/env/env_test.go @@ -25,11 +25,11 @@ import ( "go.yaml.in/yaml/v3" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" ) func TestConfig(t *testing.T) { diff --git a/common/functions/BUILD.bazel b/common/functions/BUILD.bazel index 4511d06f7..92ca48bbc 100644 --- a/common/functions/BUILD.bazel +++ b/common/functions/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "functions.go", ], - importpath = "cel.dev/cel-go/common/functions", + importpath = "github.com/authzed/cel-go/common/functions", deps = [ "//common/types/ref:go_default_library", ], diff --git a/common/functions/functions.go b/common/functions/functions.go index c5e68571e..09703d331 100644 --- a/common/functions/functions.go +++ b/common/functions/functions.go @@ -18,7 +18,7 @@ package functions import ( "context" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) // Overload defines a named overload of a function, indicating an operand trait diff --git a/common/operators/BUILD.bazel b/common/operators/BUILD.bazel index 21776cecc..869c82e79 100644 --- a/common/operators/BUILD.bazel +++ b/common/operators/BUILD.bazel @@ -10,5 +10,5 @@ go_library( srcs = [ "operators.go", ], - importpath = "cel.dev/cel-go/common/operators", + importpath = "github.com/authzed/cel-go/common/operators", ) diff --git a/common/overloads/BUILD.bazel b/common/overloads/BUILD.bazel index 1f1eaa094..3a1f47fda 100644 --- a/common/overloads/BUILD.bazel +++ b/common/overloads/BUILD.bazel @@ -10,5 +10,5 @@ go_library( srcs = [ "overloads.go", ], - importpath = "cel.dev/cel-go/common/overloads", + importpath = "github.com/authzed/cel-go/common/overloads", ) diff --git a/common/runes/BUILD.bazel b/common/runes/BUILD.bazel index 0674eaed1..658969ffe 100644 --- a/common/runes/BUILD.bazel +++ b/common/runes/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "buffer.go", ], - importpath = "cel.dev/cel-go/common/runes", + importpath = "github.com/authzed/cel-go/common/runes", ) go_test( diff --git a/common/source.go b/common/source.go index 73237edf9..bc1413c95 100644 --- a/common/source.go +++ b/common/source.go @@ -15,7 +15,7 @@ package common import ( - "cel.dev/cel-go/common/runes" + "github.com/authzed/cel-go/common/runes" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/stdlib/BUILD.bazel b/common/stdlib/BUILD.bazel index dd34ac1e5..23f19a8af 100644 --- a/common/stdlib/BUILD.bazel +++ b/common/stdlib/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "standard.go", ], - importpath = "cel.dev/cel-go/common/stdlib", + importpath = "github.com/authzed/cel-go/common/stdlib", deps = [ "//common:go_default_library", "//common/decls:go_default_library", diff --git a/common/stdlib/standard.go b/common/stdlib/standard.go index 1c2a54585..335be8abc 100644 --- a/common/stdlib/standard.go +++ b/common/stdlib/standard.go @@ -22,14 +22,14 @@ import ( "strings" "time" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" ) var ( diff --git a/common/types/BUILD.bazel b/common/types/BUILD.bazel index 8626df967..c13cacf59 100644 --- a/common/types/BUILD.bazel +++ b/common/types/BUILD.bazel @@ -38,7 +38,7 @@ go_library( "unknown.go", "util.go", ], - importpath = "cel.dev/cel-go/common/types", + importpath = "github.com/authzed/cel-go/common/types", deps = [ "//checker/decls:go_default_library", "//common/overloads:go_default_library", diff --git a/common/types/bool.go b/common/types/bool.go index d16964cdb..d987fef07 100644 --- a/common/types/bool.go +++ b/common/types/bool.go @@ -20,7 +20,7 @@ import ( "strconv" "strings" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/bytes.go b/common/types/bytes.go index 206dd3bc5..5671625c1 100644 --- a/common/types/bytes.go +++ b/common/types/bytes.go @@ -22,7 +22,7 @@ import ( "strings" "unicode/utf8" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/compare.go b/common/types/compare.go index caad97b94..8a9b05bf5 100644 --- a/common/types/compare.go +++ b/common/types/compare.go @@ -17,7 +17,7 @@ package types import ( "math" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) func compareDoubleInt(d Double, i Int) Int { diff --git a/common/types/double.go b/common/types/double.go index 729fc1ec4..07a956fdd 100644 --- a/common/types/double.go +++ b/common/types/double.go @@ -21,7 +21,7 @@ import ( "strconv" "strings" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/double_test.go b/common/types/double_test.go index 96f871655..7f3002a50 100644 --- a/common/types/double_test.go +++ b/common/types/double_test.go @@ -23,8 +23,8 @@ import ( "google.golang.org/protobuf/proto" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/duration.go b/common/types/duration.go index cd3efa1ce..a7bbe07f8 100644 --- a/common/types/duration.go +++ b/common/types/duration.go @@ -21,8 +21,8 @@ import ( "strings" "time" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" dpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/duration_test.go b/common/types/duration_test.go index 76362d366..7694ec54c 100644 --- a/common/types/duration_test.go +++ b/common/types/duration_test.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/proto" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" dpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/err.go b/common/types/err.go index 22749de6c..dd99b1b75 100644 --- a/common/types/err.go +++ b/common/types/err.go @@ -19,7 +19,7 @@ import ( "fmt" "reflect" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) // Error interface which allows types types.Err values to be treated as error values. diff --git a/common/types/format.go b/common/types/format.go index 3b2c06f0a..89c2dacaa 100644 --- a/common/types/format.go +++ b/common/types/format.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" ) type formattable interface { diff --git a/common/types/int.go b/common/types/int.go index 82dbb1738..6b99d1fb4 100644 --- a/common/types/int.go +++ b/common/types/int.go @@ -22,7 +22,7 @@ import ( "strings" "time" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/int_test.go b/common/types/int_test.go index 0d573eb29..60c4d7b86 100644 --- a/common/types/int_test.go +++ b/common/types/int_test.go @@ -24,8 +24,8 @@ import ( "google.golang.org/protobuf/proto" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/iterator.go b/common/types/iterator.go index 77fc90f42..0d7913b15 100644 --- a/common/types/iterator.go +++ b/common/types/iterator.go @@ -18,8 +18,8 @@ import ( "fmt" "reflect" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" ) var ( diff --git a/common/types/json_list_test.go b/common/types/json_list_test.go index 867b7bbe8..d3d59e7b9 100644 --- a/common/types/json_list_test.go +++ b/common/types/json_list_test.go @@ -19,7 +19,7 @@ import ( "reflect" "testing" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/traits" "google.golang.org/protobuf/proto" anypb "google.golang.org/protobuf/types/known/anypb" diff --git a/common/types/list.go b/common/types/list.go index bbdd7adac..cef411f6b 100644 --- a/common/types/list.go +++ b/common/types/list.go @@ -23,8 +23,8 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/list_test.go b/common/types/list_test.go index 43e2fc015..756bd6020 100644 --- a/common/types/list_test.go +++ b/common/types/list_test.go @@ -24,8 +24,8 @@ import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" dpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/map.go b/common/types/map.go index 782003e56..45761874f 100644 --- a/common/types/map.go +++ b/common/types/map.go @@ -25,9 +25,9 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "cel.dev/cel-go/common/types/pb" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/pb" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/map_test.go b/common/types/map_test.go index afe759ccf..eaacb12e8 100644 --- a/common/types/map_test.go +++ b/common/types/map_test.go @@ -26,11 +26,11 @@ import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "cel.dev/cel-go/common/types/pb" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/pb" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" tpb "google.golang.org/protobuf/types/known/timestamppb" diff --git a/common/types/native.go b/common/types/native.go index a3736efba..871f377cb 100644 --- a/common/types/native.go +++ b/common/types/native.go @@ -23,8 +23,8 @@ import ( "google.golang.org/protobuf/reflect/protoreflect" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" structpb "google.golang.org/protobuf/types/known/structpb" ) diff --git a/common/types/native_test.go b/common/types/native_test.go index f64891d96..a0b76e38a 100644 --- a/common/types/native_test.go +++ b/common/types/native_test.go @@ -26,17 +26,17 @@ import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/pb" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/ext" - "cel.dev/cel-go/test" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/pb" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/ext" + "github.com/authzed/cel-go/test" structpb "google.golang.org/protobuf/types/known/structpb" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) func TestNativeTypes(t *testing.T) { diff --git a/common/types/null.go b/common/types/null.go index 990609c69..835cc0618 100644 --- a/common/types/null.go +++ b/common/types/null.go @@ -21,7 +21,7 @@ import ( "google.golang.org/protobuf/proto" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/null_test.go b/common/types/null_test.go index df6e31c8c..33a82b761 100644 --- a/common/types/null_test.go +++ b/common/types/null_test.go @@ -22,7 +22,7 @@ import ( "google.golang.org/protobuf/proto" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" dynamicpb "google.golang.org/protobuf/types/dynamicpb" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/object.go b/common/types/object.go index 3eeb60c09..af4f17745 100644 --- a/common/types/object.go +++ b/common/types/object.go @@ -24,8 +24,8 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "cel.dev/cel-go/common/types/pb" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/pb" + "github.com/authzed/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/object_test.go b/common/types/object_test.go index f2a9a820a..c496f83dd 100644 --- a/common/types/object_test.go +++ b/common/types/object_test.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" anypb "google.golang.org/protobuf/types/known/anypb" diff --git a/common/types/optional.go b/common/types/optional.go index b2092f957..d38b45349 100644 --- a/common/types/optional.go +++ b/common/types/optional.go @@ -20,7 +20,7 @@ import ( "reflect" "strings" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) var ( diff --git a/common/types/optional_test.go b/common/types/optional_test.go index 99e12317e..e0c6bb225 100644 --- a/common/types/optional_test.go +++ b/common/types/optional_test.go @@ -19,7 +19,7 @@ import ( "reflect" "testing" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) func TestOptionalOptionalOf(t *testing.T) { diff --git a/common/types/pb/BUILD.bazel b/common/types/pb/BUILD.bazel index 66d333c36..0c79e718a 100644 --- a/common/types/pb/BUILD.bazel +++ b/common/types/pb/BUILD.bazel @@ -15,7 +15,7 @@ go_library( "pb.go", "type.go", ], - importpath = "cel.dev/cel-go/common/types/pb", + importpath = "github.com/authzed/cel-go/common/types/pb", deps = [ "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", "@org_golang_google_protobuf//encoding/protowire:go_default_library", diff --git a/common/types/pb/equal_test.go b/common/types/pb/equal_test.go index 341dfec0a..0f7ac3138 100644 --- a/common/types/pb/equal_test.go +++ b/common/types/pb/equal_test.go @@ -20,7 +20,7 @@ import ( "google.golang.org/protobuf/proto" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" anypb "google.golang.org/protobuf/types/known/anypb" ) diff --git a/common/types/pb/file_test.go b/common/types/pb/file_test.go index fa0db187e..dde847fa8 100644 --- a/common/types/pb/file_test.go +++ b/common/types/pb/file_test.go @@ -21,10 +21,10 @@ import ( "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoreflect" - "cel.dev/cel-go/checker/decls" + "github.com/authzed/cel-go/checker/decls" - proto2pb "cel.dev/cel-go/test/proto2pb" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto2pb "github.com/authzed/cel-go/test/proto2pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" descpb "google.golang.org/protobuf/types/descriptorpb" ) diff --git a/common/types/pb/pb_test.go b/common/types/pb/pb_test.go index e709ad9f7..45260f203 100644 --- a/common/types/pb/pb_test.go +++ b/common/types/pb/pb_test.go @@ -21,8 +21,8 @@ import ( "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoreflect" - proto2pb "cel.dev/cel-go/test/proto2pb" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto2pb "github.com/authzed/cel-go/test/proto2pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" descpb "google.golang.org/protobuf/types/descriptorpb" dynamicpb "google.golang.org/protobuf/types/dynamicpb" durationpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/pb/type_test.go b/common/types/pb/type_test.go index e859a1a66..630e06286 100644 --- a/common/types/pb/type_test.go +++ b/common/types/pb/type_test.go @@ -19,11 +19,11 @@ import ( "testing" "time" - "cel.dev/cel-go/checker/decls" + "github.com/authzed/cel-go/checker/decls" "google.golang.org/protobuf/proto" - proto2pb "cel.dev/cel-go/test/proto2pb" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto2pb "github.com/authzed/cel-go/test/proto2pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" dynamicpb "google.golang.org/protobuf/types/dynamicpb" anypb "google.golang.org/protobuf/types/known/anypb" diff --git a/common/types/provider.go b/common/types/provider.go index ba45df764..453e53aeb 100644 --- a/common/types/provider.go +++ b/common/types/provider.go @@ -25,9 +25,9 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "cel.dev/cel-go/common/types/pb" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/pb" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" anypb "google.golang.org/protobuf/types/known/anypb" diff --git a/common/types/provider_test.go b/common/types/provider_test.go index 4dc21d744..6e99c2374 100644 --- a/common/types/provider_test.go +++ b/common/types/provider_test.go @@ -25,11 +25,11 @@ import ( "testing" "time" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" "google.golang.org/protobuf/proto" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" anypb "google.golang.org/protobuf/types/known/anypb" dpb "google.golang.org/protobuf/types/known/durationpb" diff --git a/common/types/ref/BUILD.bazel b/common/types/ref/BUILD.bazel index 9df5779d8..65ccb68f3 100644 --- a/common/types/ref/BUILD.bazel +++ b/common/types/ref/BUILD.bazel @@ -11,7 +11,7 @@ go_library( "provider.go", "reference.go", ], - importpath = "cel.dev/cel-go/common/types/ref", + importpath = "github.com/authzed/cel-go/common/types/ref", deps = [ "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", "@org_golang_google_protobuf//proto:go_default_library", diff --git a/common/types/size_calc.go b/common/types/size_calc.go index fecb2f306..edc6c1ae7 100644 --- a/common/types/size_calc.go +++ b/common/types/size_calc.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" ) const ( diff --git a/common/types/size_calc_test.go b/common/types/size_calc_test.go index c501bd15f..f2077664d 100644 --- a/common/types/size_calc_test.go +++ b/common/types/size_calc_test.go @@ -24,10 +24,10 @@ import ( "google.golang.org/protobuf/reflect/protoreflect" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) func TestCalculateSize(t *testing.T) { diff --git a/common/types/string.go b/common/types/string.go index 238b85487..0d73db5eb 100644 --- a/common/types/string.go +++ b/common/types/string.go @@ -22,8 +22,8 @@ import ( "strings" "time" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/string_test.go b/common/types/string_test.go index dc0cf40b2..1914e4e11 100644 --- a/common/types/string_test.go +++ b/common/types/string_test.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/proto" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/struct.go b/common/types/struct.go index 0b50895a5..49790e66e 100644 --- a/common/types/struct.go +++ b/common/types/struct.go @@ -17,7 +17,7 @@ package types import ( "reflect" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) // StructTypeDescriptor describes a CEL struct type, providing field metadata and value instantiation. diff --git a/common/types/timestamp.go b/common/types/timestamp.go index 7b316c6d3..91ed60ebb 100644 --- a/common/types/timestamp.go +++ b/common/types/timestamp.go @@ -25,8 +25,8 @@ import ( "time" "unicode" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/timestamp_test.go b/common/types/timestamp_test.go index 244219449..46240cc71 100644 --- a/common/types/timestamp_test.go +++ b/common/types/timestamp_test.go @@ -22,8 +22,8 @@ import ( "testing" "time" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types/ref" "google.golang.org/protobuf/proto" diff --git a/common/types/traits/BUILD.bazel b/common/types/traits/BUILD.bazel index e2f7d6fdb..5d054f56c 100644 --- a/common/types/traits/BUILD.bazel +++ b/common/types/traits/BUILD.bazel @@ -22,7 +22,7 @@ go_library( "traits.go", "zeroer.go", ], - importpath = "cel.dev/cel-go/common/types/traits", + importpath = "github.com/authzed/cel-go/common/types/traits", deps = [ "//common/types/ref:go_default_library", ], diff --git a/common/types/traits/comparer.go b/common/types/traits/comparer.go index 83b139535..4888da175 100644 --- a/common/types/traits/comparer.go +++ b/common/types/traits/comparer.go @@ -15,7 +15,7 @@ package traits import ( - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) // Comparer interface for ordering comparisons between values in order to diff --git a/common/types/traits/container.go b/common/types/traits/container.go index 357ccd31d..fd6c607bf 100644 --- a/common/types/traits/container.go +++ b/common/types/traits/container.go @@ -14,7 +14,7 @@ package traits -import "cel.dev/cel-go/common/types/ref" +import "github.com/authzed/cel-go/common/types/ref" // Container interface which permits containment tests such as 'a in b'. type Container interface { diff --git a/common/types/traits/field_tester.go b/common/types/traits/field_tester.go index d80351b40..865ea6a07 100644 --- a/common/types/traits/field_tester.go +++ b/common/types/traits/field_tester.go @@ -15,7 +15,7 @@ package traits import ( - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) // FieldTester indicates if a defined field on an object type is set to a diff --git a/common/types/traits/indexer.go b/common/types/traits/indexer.go index 2d1f72ae2..ce8a13af2 100644 --- a/common/types/traits/indexer.go +++ b/common/types/traits/indexer.go @@ -15,7 +15,7 @@ package traits import ( - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) // Indexer permits random access of elements by index 'a[b()]'. diff --git a/common/types/traits/iterator.go b/common/types/traits/iterator.go index f0a0b4cb9..d7b42aa77 100644 --- a/common/types/traits/iterator.go +++ b/common/types/traits/iterator.go @@ -15,7 +15,7 @@ package traits import ( - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) // Iterable aggregate types permit traversal over their elements. diff --git a/common/types/traits/lister.go b/common/types/traits/lister.go index 67f32391b..e9a17f116 100644 --- a/common/types/traits/lister.go +++ b/common/types/traits/lister.go @@ -14,7 +14,7 @@ package traits -import "cel.dev/cel-go/common/types/ref" +import "github.com/authzed/cel-go/common/types/ref" // Lister interface which aggregates the traits of a list. type Lister interface { diff --git a/common/types/traits/mapper.go b/common/types/traits/mapper.go index 7a4acedc5..f7ba9e05f 100644 --- a/common/types/traits/mapper.go +++ b/common/types/traits/mapper.go @@ -14,7 +14,7 @@ package traits -import "cel.dev/cel-go/common/types/ref" +import "github.com/authzed/cel-go/common/types/ref" // Mapper interface which aggregates the traits of a maps. type Mapper interface { diff --git a/common/types/traits/matcher.go b/common/types/traits/matcher.go index 4ea283e90..5b7aae7a6 100644 --- a/common/types/traits/matcher.go +++ b/common/types/traits/matcher.go @@ -14,7 +14,7 @@ package traits -import "cel.dev/cel-go/common/types/ref" +import "github.com/authzed/cel-go/common/types/ref" // Matcher interface for supporting 'matches()' overloads. type Matcher interface { diff --git a/common/types/traits/math.go b/common/types/traits/math.go index cb6354f7e..4062ff90a 100644 --- a/common/types/traits/math.go +++ b/common/types/traits/math.go @@ -14,7 +14,7 @@ package traits -import "cel.dev/cel-go/common/types/ref" +import "github.com/authzed/cel-go/common/types/ref" // Adder interface to support '+' operator overloads. type Adder interface { diff --git a/common/types/traits/receiver.go b/common/types/traits/receiver.go index 4760192c6..ad3e2e985 100644 --- a/common/types/traits/receiver.go +++ b/common/types/traits/receiver.go @@ -14,7 +14,7 @@ package traits -import "cel.dev/cel-go/common/types/ref" +import "github.com/authzed/cel-go/common/types/ref" // Receiver interface for routing instance method calls within a value. type Receiver interface { diff --git a/common/types/traits/sizer.go b/common/types/traits/sizer.go index 8e2e3cf92..85583d6b6 100644 --- a/common/types/traits/sizer.go +++ b/common/types/traits/sizer.go @@ -15,7 +15,7 @@ package traits import ( - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) // Sizer interface for supporting 'size()' overloads. diff --git a/common/types/type_test.go b/common/types/type_test.go index fbebeeeb3..8e90b033f 100644 --- a/common/types/type_test.go +++ b/common/types/type_test.go @@ -17,7 +17,7 @@ package types import ( "testing" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) func TestType_ConvertToType(t *testing.T) { diff --git a/common/types/types.go b/common/types/types.go index fc35bddb4..417376b45 100644 --- a/common/types/types.go +++ b/common/types/types.go @@ -21,9 +21,9 @@ import ( "google.golang.org/protobuf/proto" - chkdecls "cel.dev/cel-go/checker/decls" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + chkdecls "github.com/authzed/cel-go/checker/decls" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" celpb "cel.dev/expr" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" diff --git a/common/types/types_test.go b/common/types/types_test.go index 9ea7b8f6c..3d69e7224 100644 --- a/common/types/types_test.go +++ b/common/types/types_test.go @@ -22,8 +22,8 @@ import ( "google.golang.org/protobuf/proto" - chkdecls "cel.dev/cel-go/checker/decls" - "cel.dev/cel-go/common/types/traits" + chkdecls "github.com/authzed/cel-go/checker/decls" + "github.com/authzed/cel-go/common/types/traits" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/common/types/uint.go b/common/types/uint.go index 6c2bef729..1676de780 100644 --- a/common/types/uint.go +++ b/common/types/uint.go @@ -21,7 +21,7 @@ import ( "strconv" "strings" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/uint_test.go b/common/types/uint_test.go index e31fda8a3..926c84f38 100644 --- a/common/types/uint_test.go +++ b/common/types/uint_test.go @@ -23,8 +23,8 @@ import ( "google.golang.org/protobuf/proto" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" anypb "google.golang.org/protobuf/types/known/anypb" structpb "google.golang.org/protobuf/types/known/structpb" diff --git a/common/types/unknown.go b/common/types/unknown.go index 96150fc3f..9ee22ebdf 100644 --- a/common/types/unknown.go +++ b/common/types/unknown.go @@ -23,7 +23,7 @@ import ( "strings" "unicode" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) var ( diff --git a/common/types/unknown_test.go b/common/types/unknown_test.go index 6957e5846..4ae841ed7 100644 --- a/common/types/unknown_test.go +++ b/common/types/unknown_test.go @@ -21,7 +21,7 @@ import ( "strings" "testing" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) func TestIsUnknown(t *testing.T) { diff --git a/common/types/util.go b/common/types/util.go index cb37389f9..5dd0f9e92 100644 --- a/common/types/util.go +++ b/common/types/util.go @@ -15,7 +15,7 @@ package types import ( - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) // IsUnknownOrError returns whether the input element ref.Val is an ErrType or UnknownType. diff --git a/conformance/conformance_test.go b/conformance/conformance_test.go index 2d6c10bd5..6f95ef6b8 100644 --- a/conformance/conformance_test.go +++ b/conformance/conformance_test.go @@ -11,12 +11,12 @@ import ( "github.com/bazelbuild/rules_go/go/runfiles" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/ext" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/ext" "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/encoding/prototext" diff --git a/conformance/go.mod b/conformance/go.mod index e543dea61..971d12421 100644 --- a/conformance/go.mod +++ b/conformance/go.mod @@ -1,11 +1,11 @@ -module cel.dev/cel-go/conformance +module github.com/authzed/cel-go/conformance go 1.23.0 require ( - cel.dev/cel-go v0.26.1 - cel.dev/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1 - cel.dev/cel-go/tools v0.0.0-20251023215754-a36d461be521 + github.com/authzed/cel-go v0.26.1 + github.com/authzed/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1 + github.com/authzed/cel-go/tools v0.0.0-20251023215754-a36d461be521 cel.dev/expr v0.25.1 github.com/bazelbuild/rules_go v0.49.0 github.com/google/go-cmp v0.7.0 @@ -21,8 +21,8 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf // indirect ) -replace cel.dev/cel-go => ./.. +replace github.com/authzed/cel-go => ./.. -replace cel.dev/cel-go/policy => ../policy +replace github.com/authzed/cel-go/policy => ../policy -replace cel.dev/cel-go/tools => ../tools +replace github.com/authzed/cel-go/tools => ../tools diff --git a/conformance/policy/policy_conformance_test.go b/conformance/policy/policy_conformance_test.go index 21966df04..2cc1563c6 100644 --- a/conformance/policy/policy_conformance_test.go +++ b/conformance/policy/policy_conformance_test.go @@ -24,12 +24,12 @@ import ( "testing" "github.com/bazelbuild/rules_go/go/runfiles" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/policy" - "cel.dev/cel-go/tools/celtest" - "cel.dev/cel-go/tools/compiler" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/policy" + "github.com/authzed/cel-go/tools/celtest" + "github.com/authzed/cel-go/tools/compiler" _ "cel.dev/expr/conformance/proto3" ) diff --git a/examples/README.md b/examples/README.md index ecb6b33c3..2e12245f5 100644 --- a/examples/README.md +++ b/examples/README.md @@ -15,7 +15,7 @@ import ( "fmt" "log" - "cel.dev/cel-go/cel" + "github.com/authzed/cel-go/cel" ) func main() { diff --git a/examples/example_cel_advanced_test.go b/examples/example_cel_advanced_test.go index f0ee5e736..b537ce4ae 100644 --- a/examples/example_cel_advanced_test.go +++ b/examples/example_cel_advanced_test.go @@ -17,7 +17,7 @@ package examples import ( "fmt" - "cel.dev/cel-go/cel" + "github.com/authzed/cel-go/cel" ) // Example_cel_CommonErrors showcases handling common runtime errors (division by zero, index out of bounds, missing key) diff --git a/examples/example_cel_collections_test.go b/examples/example_cel_collections_test.go index c2e6cfa88..a3422033f 100644 --- a/examples/example_cel_collections_test.go +++ b/examples/example_cel_collections_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "cel.dev/cel-go/cel" + "github.com/authzed/cel-go/cel" ) // Example_cel_Collections showcases membership, indexing, search (exists), all, and filter diff --git a/examples/example_cel_compile_test.go b/examples/example_cel_compile_test.go index f5217c420..fbe170944 100644 --- a/examples/example_cel_compile_test.go +++ b/examples/example_cel_compile_test.go @@ -18,8 +18,8 @@ import ( "fmt" "log" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/ext" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/ext" ) // Example_cel_Compile showcases compiling a CEL expression with variable declarations diff --git a/examples/example_cel_context_eval_test.go b/examples/example_cel_context_eval_test.go index 3d5c0fb4b..e4804036d 100644 --- a/examples/example_cel_context_eval_test.go +++ b/examples/example_cel_context_eval_test.go @@ -19,7 +19,7 @@ import ( "fmt" "log" - "cel.dev/cel-go/cel" + "github.com/authzed/cel-go/cel" ) // Example_cel_ContextEval showcases evaluation cancellation and timeout using ContextEval diff --git a/examples/example_cel_custom_functions_test.go b/examples/example_cel_custom_functions_test.go index 11fcf5394..20628b88d 100644 --- a/examples/example_cel_custom_functions_test.go +++ b/examples/example_cel_custom_functions_test.go @@ -18,9 +18,9 @@ import ( "fmt" "log" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // Example_cel_Overload showcases defining custom global functions with cel.Overload diff --git a/examples/example_cel_custom_macros_test.go b/examples/example_cel_custom_macros_test.go index 387c9a849..30e07be1f 100644 --- a/examples/example_cel_custom_macros_test.go +++ b/examples/example_cel_custom_macros_test.go @@ -18,12 +18,12 @@ import ( "fmt" "log" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/parser" ) // Example_cel_CustomMacros showcases defining custom AST transformation macros diff --git a/examples/example_cel_execution_cost_test.go b/examples/example_cel_execution_cost_test.go index 123c8b747..4e80dba7b 100644 --- a/examples/example_cel_execution_cost_test.go +++ b/examples/example_cel_execution_cost_test.go @@ -19,9 +19,9 @@ import ( "log" "strings" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/types/ref" ) type exampleCostEstimator struct { diff --git a/examples/example_cel_logic_and_conditions_test.go b/examples/example_cel_logic_and_conditions_test.go index 2456e06c1..40e3b7a19 100644 --- a/examples/example_cel_logic_and_conditions_test.go +++ b/examples/example_cel_logic_and_conditions_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "cel.dev/cel-go/cel" + "github.com/authzed/cel-go/cel" ) // Example_cel_LogicAndConditions showcases logical operators, conditional (ternary) operator, and evaluation diff --git a/examples/example_cel_native_structs_test.go b/examples/example_cel_native_structs_test.go index b2b164eac..00750d264 100644 --- a/examples/example_cel_native_structs_test.go +++ b/examples/example_cel_native_structs_test.go @@ -19,8 +19,8 @@ import ( "log" "reflect" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/ext" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/ext" ) type User struct { diff --git a/examples/example_cel_operators_test.go b/examples/example_cel_operators_test.go index 2d66a2c5d..e6625da92 100644 --- a/examples/example_cel_operators_test.go +++ b/examples/example_cel_operators_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "cel.dev/cel-go/cel" + "github.com/authzed/cel-go/cel" ) // Example_cel_Arithmetic showcases negation, basic operations, modulo, and precedence diff --git a/examples/example_cel_protocol_buffers_test.go b/examples/example_cel_protocol_buffers_test.go index 30b32d7fa..ce622757e 100644 --- a/examples/example_cel_protocol_buffers_test.go +++ b/examples/example_cel_protocol_buffers_test.go @@ -18,8 +18,8 @@ import ( "fmt" "log" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/test/proto3pb" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/test/proto3pb" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/wrapperspb" ) diff --git a/examples/example_cel_strings_and_numbers_test.go b/examples/example_cel_strings_and_numbers_test.go index b61a244b0..3c253ba85 100644 --- a/examples/example_cel_strings_and_numbers_test.go +++ b/examples/example_cel_strings_and_numbers_test.go @@ -18,8 +18,8 @@ import ( "fmt" "log" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/ext" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/ext" ) // Example_cel_StringsAndNumbers showcases string functions, concatenation, and numeric comparisons diff --git a/examples/example_cel_time_test.go b/examples/example_cel_time_test.go index cfe34b96b..62395cda5 100644 --- a/examples/example_cel_time_test.go +++ b/examples/example_cel_time_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "cel.dev/cel-go/cel" + "github.com/authzed/cel-go/cel" ) // Example_cel_TimestampsAndDurations showcases timestamps, durations, arithmetic, and field access diff --git a/examples/example_cel_transforming_data_test.go b/examples/example_cel_transforming_data_test.go index d27a4b4dc..a0432f17d 100644 --- a/examples/example_cel_transforming_data_test.go +++ b/examples/example_cel_transforming_data_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "cel.dev/cel-go/cel" + "github.com/authzed/cel-go/cel" ) // Example_cel_TransformingData showcases building maps, transforming lists with map(), diff --git a/examples/example_cel_type_conversions_test.go b/examples/example_cel_type_conversions_test.go index 3a816dec1..95c252f6b 100644 --- a/examples/example_cel_type_conversions_test.go +++ b/examples/example_cel_type_conversions_test.go @@ -18,7 +18,7 @@ import ( "fmt" "log" - "cel.dev/cel-go/cel" + "github.com/authzed/cel-go/cel" ) // Example_cel_TypeConversions showcases type casting functions (int, uint, double, string, bytes, dyn) diff --git a/ext/BUILD.bazel b/ext/BUILD.bazel index b4f9d6380..f172478f0 100644 --- a/ext/BUILD.bazel +++ b/ext/BUILD.bazel @@ -24,7 +24,7 @@ go_library( "sets.go", "strings.go", ], - importpath = "cel.dev/cel-go/ext", + importpath = "github.com/authzed/cel-go/ext", visibility = ["//visibility:public"], deps = [ "//cel:go_default_library", diff --git a/ext/bindings.go b/ext/bindings.go index 1bf97c59c..e0eefa1c1 100644 --- a/ext/bindings.go +++ b/ext/bindings.go @@ -22,12 +22,12 @@ import ( "strings" "sync" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/interpreter" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/interpreter" ) // Bindings returns a cel.EnvOption to configure support for local variable diff --git a/ext/bindings_test.go b/ext/bindings_test.go index ced229716..5d8490edc 100644 --- a/ext/bindings_test.go +++ b/ext/bindings_test.go @@ -20,14 +20,14 @@ import ( "sync" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/interpreter" - "cel.dev/cel-go/test" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/interpreter" + "github.com/authzed/cel-go/test" ) var bindingTests = []struct { diff --git a/ext/comprehensions.go b/ext/comprehensions.go index d01524bb1..6fab5c98e 100644 --- a/ext/comprehensions.go +++ b/ext/comprehensions.go @@ -18,13 +18,13 @@ import ( "fmt" "math" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/parser" ) const ( diff --git a/ext/comprehensions_test.go b/ext/comprehensions_test.go index 075c2af38..c7ba2f86f 100644 --- a/ext/comprehensions_test.go +++ b/ext/comprehensions_test.go @@ -19,10 +19,10 @@ import ( "strings" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/interpreter" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/interpreter" ) func TestTwoVarComprehensions(t *testing.T) { diff --git a/ext/costs.go b/ext/costs.go index 3a4209ef4..ec2323eec 100644 --- a/ext/costs.go +++ b/ext/costs.go @@ -17,12 +17,12 @@ package ext import ( "math" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" ) var ( diff --git a/ext/encoders.go b/ext/encoders.go index f2b022349..8535da374 100644 --- a/ext/encoders.go +++ b/ext/encoders.go @@ -20,12 +20,12 @@ import ( "fmt" "math" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/cost" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/interpreter" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/cost" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/interpreter" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/structpb" ) diff --git a/ext/encoders_test.go b/ext/encoders_test.go index 1d526a15d..51dd9f7e0 100644 --- a/ext/encoders_test.go +++ b/ext/encoders_test.go @@ -20,8 +20,8 @@ import ( "strings" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" ) func TestEncoders(t *testing.T) { diff --git a/ext/extension_option_factory.go b/ext/extension_option_factory.go index 6aeb157bc..5b1a64f8c 100644 --- a/ext/extension_option_factory.go +++ b/ext/extension_option_factory.go @@ -17,8 +17,8 @@ package ext import ( "fmt" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/env" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/env" ) // ExtensionOptionFactory converts an ExtensionConfig value to a CEL environment option. diff --git a/ext/extension_option_factory_test.go b/ext/extension_option_factory_test.go index 260bf795b..e6a66ba55 100644 --- a/ext/extension_option_factory_test.go +++ b/ext/extension_option_factory_test.go @@ -18,8 +18,8 @@ import ( "fmt" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/env" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/env" ) func TestExtensionOptionFactoryInvalidExtension(t *testing.T) { diff --git a/ext/formatting.go b/ext/formatting.go index f8633dbc0..5409086f6 100644 --- a/ext/formatting.go +++ b/ext/formatting.go @@ -26,12 +26,12 @@ import ( "golang.org/x/text/language" "golang.org/x/text/message" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" ) type clauseImpl func(ref.Val, string) (string, error) diff --git a/ext/formatting_test.go b/ext/formatting_test.go index c72316588..4cc3ac1b2 100644 --- a/ext/formatting_test.go +++ b/ext/formatting_test.go @@ -24,12 +24,12 @@ import ( "google.golang.org/protobuf/proto" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) func TestStringFormat(t *testing.T) { diff --git a/ext/formatting_v2.go b/ext/formatting_v2.go index 969a779e1..3972998c2 100644 --- a/ext/formatting_v2.go +++ b/ext/formatting_v2.go @@ -24,11 +24,11 @@ import ( "time" "unicode" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" ) type clauseImplV2 func(ref.Val) (string, error) diff --git a/ext/formatting_v2_test.go b/ext/formatting_v2_test.go index 71c96576a..9f2edbc67 100644 --- a/ext/formatting_v2_test.go +++ b/ext/formatting_v2_test.go @@ -24,12 +24,12 @@ import ( "google.golang.org/protobuf/proto" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) func TestStringsWithExtensionV2(t *testing.T) { diff --git a/ext/guards.go b/ext/guards.go index 83e64e89a..4606a64ce 100644 --- a/ext/guards.go +++ b/ext/guards.go @@ -15,9 +15,9 @@ package ext import ( - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // function invocation guards for common call signatures within extension functions. diff --git a/ext/lists.go b/ext/lists.go index 936625cc5..5f19ead44 100644 --- a/ext/lists.go +++ b/ext/lists.go @@ -19,17 +19,17 @@ import ( "math" "sort" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/cost" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/interpreter" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/cost" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/interpreter" + "github.com/authzed/cel-go/parser" ) var comparableTypes = []*cel.Type{ diff --git a/ext/lists_test.go b/ext/lists_test.go index 8b68cf4bd..ec7817f4f 100644 --- a/ext/lists_test.go +++ b/ext/lists_test.go @@ -19,11 +19,11 @@ import ( "strings" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/types" - proto2pb "cel.dev/cel-go/test/proto2pb" + proto2pb "github.com/authzed/cel-go/test/proto2pb" ) func TestLists(t *testing.T) { diff --git a/ext/math.go b/ext/math.go index d1030dc72..6f037b652 100644 --- a/ext/math.go +++ b/ext/math.go @@ -19,14 +19,14 @@ import ( "math" "strings" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/cost" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/interpreter" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/cost" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/interpreter" ) // Math returns a cel.EnvOption to configure namespaced math helper macros and diff --git a/ext/math_test.go b/ext/math_test.go index 815d113fc..413be8e8c 100644 --- a/ext/math_test.go +++ b/ext/math_test.go @@ -19,9 +19,9 @@ import ( "strings" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/types" ) func TestMath(t *testing.T) { diff --git a/ext/native.go b/ext/native.go index 213a2443e..42719cab1 100644 --- a/ext/native.go +++ b/ext/native.go @@ -15,8 +15,8 @@ package ext import ( - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" ) // NativeTypesOption is a functional interface for configuring handling of native types. diff --git a/ext/native_test.go b/ext/native_test.go index a5c870044..31e8987a5 100644 --- a/ext/native_test.go +++ b/ext/native_test.go @@ -26,16 +26,16 @@ import ( "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/pb" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/test" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/pb" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/test" structpb "google.golang.org/protobuf/types/known/structpb" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) func TestNativeTypes(t *testing.T) { diff --git a/ext/network.go b/ext/network.go index bb0b82f14..32f36c8c7 100644 --- a/ext/network.go +++ b/ext/network.go @@ -20,13 +20,13 @@ import ( "net/netip" "reflect" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/cost" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/interpreter" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/cost" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/interpreter" ) const ( diff --git a/ext/network_test.go b/ext/network_test.go index 993c6ef08..28dad5429 100644 --- a/ext/network_test.go +++ b/ext/network_test.go @@ -19,9 +19,9 @@ import ( "reflect" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/types" ) func TestNetwork_Success(t *testing.T) { diff --git a/ext/protos.go b/ext/protos.go index 0dc2f6a0d..a425dcf7c 100644 --- a/ext/protos.go +++ b/ext/protos.go @@ -17,8 +17,8 @@ package ext import ( "math" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/ast" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/ast" ) // Protos returns a cel.EnvOption to configure extended macros and functions for diff --git a/ext/protos_test.go b/ext/protos_test.go index 6c2fce992..6c5aede00 100644 --- a/ext/protos_test.go +++ b/ext/protos_test.go @@ -20,13 +20,13 @@ import ( "google.golang.org/protobuf/proto" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/test" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/test" - proto2pb "cel.dev/cel-go/test/proto2pb" + proto2pb "github.com/authzed/cel-go/test/proto2pb" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" ) diff --git a/ext/regex.go b/ext/regex.go index cbffa6470..ec3c2a755 100644 --- a/ext/regex.go +++ b/ext/regex.go @@ -22,13 +22,13 @@ import ( "strconv" "strings" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/cost" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/interpreter" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/cost" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/interpreter" ) const ( diff --git a/ext/regex_test.go b/ext/regex_test.go index 6c7b401e0..7429f9f2b 100644 --- a/ext/regex_test.go +++ b/ext/regex_test.go @@ -19,8 +19,8 @@ import ( "strings" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" ) func TestRegex(t *testing.T) { diff --git a/ext/security/go.mod b/ext/security/go.mod index f2b880306..e94d07cf6 100644 --- a/ext/security/go.mod +++ b/ext/security/go.mod @@ -1,8 +1,8 @@ -module cel.dev/cel-go/ext/security +module github.com/authzed/cel-go/ext/security go 1.23.0 -require cel.dev/cel-go v0.31.0 +require github.com/authzed/cel-go v0.31.0 require ( cel.dev/expr v0.25.1 // indirect @@ -15,4 +15,4 @@ require ( google.golang.org/protobuf v1.36.10 // indirect ) -replace cel.dev/cel-go => ../../ +replace github.com/authzed/cel-go => ../../ diff --git a/ext/security/hmac/BUILD.bazel b/ext/security/hmac/BUILD.bazel index 687c5d568..f323594b1 100644 --- a/ext/security/hmac/BUILD.bazel +++ b/ext/security/hmac/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "hmac.go", ], - importpath = "cel.dev/cel-go/ext/security/hmac", + importpath = "github.com/authzed/cel-go/ext/security/hmac", deps = [ "//cel:go_default_library", "//common/types:go_default_library", diff --git a/ext/security/hmac/hmac.go b/ext/security/hmac/hmac.go index c51a74cd3..ee24c2d64 100644 --- a/ext/security/hmac/hmac.go +++ b/ext/security/hmac/hmac.go @@ -27,9 +27,9 @@ import ( "fmt" "strings" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // Library returns a cel.EnvOption to configure extended functions for HMAC signature verification and computation. diff --git a/ext/security/hmac/hmac_test.go b/ext/security/hmac/hmac_test.go index 11a91bf5b..a0dd74ad3 100644 --- a/ext/security/hmac/hmac_test.go +++ b/ext/security/hmac/hmac_test.go @@ -26,9 +26,9 @@ import ( "reflect" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/ext" - hmaclib "cel.dev/cel-go/ext/security/hmac" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/ext" + hmaclib "github.com/authzed/cel-go/ext/security/hmac" ) func evalExpr(t *testing.T, env *cel.Env, expr string, vars map[string]any) any { diff --git a/ext/security/jwt/BUILD.bazel b/ext/security/jwt/BUILD.bazel index e8e0cb382..6b6051ed0 100644 --- a/ext/security/jwt/BUILD.bazel +++ b/ext/security/jwt/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "jwt.go", ], - importpath = "cel.dev/cel-go/ext/security/jwt", + importpath = "github.com/authzed/cel-go/ext/security/jwt", deps = [ "//cel:go_default_library", "//common/types:go_default_library", diff --git a/ext/security/jwt/jwt.go b/ext/security/jwt/jwt.go index 553a2cdfe..0d9910df3 100644 --- a/ext/security/jwt/jwt.go +++ b/ext/security/jwt/jwt.go @@ -24,9 +24,9 @@ import ( "strings" "time" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) const ( diff --git a/ext/security/jwt/jwt_test.go b/ext/security/jwt/jwt_test.go index adb2e246b..8b889adf2 100644 --- a/ext/security/jwt/jwt_test.go +++ b/ext/security/jwt/jwt_test.go @@ -22,10 +22,10 @@ import ( "testing" "time" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/ext/security/jwt" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/ext/security/jwt" ) func createTestJWT(t *testing.T, header, payload map[string]any) string { diff --git a/ext/sets.go b/ext/sets.go index 3263ff819..143d33b41 100644 --- a/ext/sets.go +++ b/ext/sets.go @@ -15,15 +15,15 @@ package ext import ( - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/cost" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/interpreter" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/cost" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/interpreter" ) // Sets returns a cel.EnvOption to configure namespaced set relationship diff --git a/ext/sets_test.go b/ext/sets_test.go index 161c2ae9a..92182ade8 100644 --- a/ext/sets_test.go +++ b/ext/sets_test.go @@ -20,12 +20,12 @@ import ( "strings" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) func TestSets(t *testing.T) { diff --git a/ext/strings.go b/ext/strings.go index 2e3bc8ac3..3c41aaff7 100644 --- a/ext/strings.go +++ b/ext/strings.go @@ -27,14 +27,14 @@ import ( "golang.org/x/text/language" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/cost" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/interpreter" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/cost" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/interpreter" ) const ( diff --git a/ext/strings_test.go b/ext/strings_test.go index 8e9e01b11..23e07e465 100644 --- a/ext/strings_test.go +++ b/ext/strings_test.go @@ -21,10 +21,10 @@ import ( "time" "unicode/utf8" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // TODO: move these tests to a conformance test. diff --git a/go.mod b/go.mod index 188cf7bca..b5226a335 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module cel.dev/cel-go +module github.com/authzed/cel-go go 1.23.0 diff --git a/interpreter/BUILD.bazel b/interpreter/BUILD.bazel index ee1f2ebda..a1257a771 100644 --- a/interpreter/BUILD.bazel +++ b/interpreter/BUILD.bazel @@ -23,7 +23,7 @@ go_library( "prune.go", "runtimecost.go", ], - importpath = "cel.dev/cel-go/interpreter", + importpath = "github.com/authzed/cel-go/interpreter", deps = [ "//common:go_default_library", "//common/ast:go_default_library", diff --git a/interpreter/activation.go b/interpreter/activation.go index 15cbd3002..6d4bae3b7 100644 --- a/interpreter/activation.go +++ b/interpreter/activation.go @@ -18,7 +18,7 @@ import ( "errors" "fmt" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) // Activation used to resolve identifiers by name and references by id. diff --git a/interpreter/activation_test.go b/interpreter/activation_test.go index 21cf93636..28aa3a857 100644 --- a/interpreter/activation_test.go +++ b/interpreter/activation_test.go @@ -18,8 +18,8 @@ import ( "testing" "time" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) func TestActivation(t *testing.T) { diff --git a/interpreter/async.go b/interpreter/async.go index a678705a7..fff5da9a6 100644 --- a/interpreter/async.go +++ b/interpreter/async.go @@ -23,9 +23,9 @@ import ( "sync" "sync/atomic" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // Async extension function support. diff --git a/interpreter/async_test.go b/interpreter/async_test.go index 228eddb14..0f16d88e8 100644 --- a/interpreter/async_test.go +++ b/interpreter/async_test.go @@ -24,15 +24,15 @@ import ( "testing" "time" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/parser" ) // asyncReturning returns an AsyncOp that immediately produces the given value, while counting the diff --git a/interpreter/attribute_patterns.go b/interpreter/attribute_patterns.go index 8e33ddd23..fa00e37ed 100644 --- a/interpreter/attribute_patterns.go +++ b/interpreter/attribute_patterns.go @@ -18,9 +18,9 @@ import ( "fmt" "strings" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // AttributePattern represents a top-level variable with an optional set of qualifier patterns. diff --git a/interpreter/attribute_patterns_test.go b/interpreter/attribute_patterns_test.go index 99be5604c..0863589ed 100644 --- a/interpreter/attribute_patterns_test.go +++ b/interpreter/attribute_patterns_test.go @@ -18,8 +18,8 @@ import ( "fmt" "testing" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/types" ) // attr describes a simplified format for specifying common Attribute and Qualifier values for diff --git a/interpreter/attributes.go b/interpreter/attributes.go index ce344eb62..721a1df05 100644 --- a/interpreter/attributes.go +++ b/interpreter/attributes.go @@ -18,10 +18,10 @@ import ( "fmt" "strings" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" ) // AttributeFactory provides methods creating Attribute and Qualifier values. diff --git a/interpreter/attributes_test.go b/interpreter/attributes_test.go index 35582f676..7e8cd5d3c 100644 --- a/interpreter/attributes_test.go +++ b/interpreter/attributes_test.go @@ -20,19 +20,19 @@ import ( "reflect" "testing" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/stdlib" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/stdlib" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" anypb "google.golang.org/protobuf/types/known/anypb" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) func TestAttributesAbsoluteAttr(t *testing.T) { diff --git a/interpreter/decorators.go b/interpreter/decorators.go index 7402d18fa..f190a4e53 100644 --- a/interpreter/decorators.go +++ b/interpreter/decorators.go @@ -17,10 +17,10 @@ package interpreter import ( "fmt" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" ) // InterpretableDecorator is a functional interface for decorating or replacing diff --git a/interpreter/dispatcher.go b/interpreter/dispatcher.go index 40efe3009..b2a1eca89 100644 --- a/interpreter/dispatcher.go +++ b/interpreter/dispatcher.go @@ -17,7 +17,7 @@ package interpreter import ( "fmt" - "cel.dev/cel-go/common/functions" + "github.com/authzed/cel-go/common/functions" ) // Dispatcher resolves function calls to their appropriate overload. diff --git a/interpreter/evalstate.go b/interpreter/evalstate.go index c1ee6ea1c..d0b8094e2 100644 --- a/interpreter/evalstate.go +++ b/interpreter/evalstate.go @@ -15,7 +15,7 @@ package interpreter import ( - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/ref" ) // EvalState tracks the values associated with expression ids during execution. diff --git a/interpreter/frame.go b/interpreter/frame.go index 2fd93052d..a5b0c526c 100644 --- a/interpreter/frame.go +++ b/interpreter/frame.go @@ -21,9 +21,9 @@ import ( "sync" "sync/atomic" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // evalContext contains the stateful information needed for a single evaluation. diff --git a/interpreter/frame_test.go b/interpreter/frame_test.go index a1bb7bbf6..fe4daf7b9 100644 --- a/interpreter/frame_test.go +++ b/interpreter/frame_test.go @@ -18,8 +18,8 @@ import ( "context" "testing" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) func TestFrameCheckInterrupt(t *testing.T) { diff --git a/interpreter/functions/BUILD.bazel b/interpreter/functions/BUILD.bazel index e438b126b..0393b9251 100644 --- a/interpreter/functions/BUILD.bazel +++ b/interpreter/functions/BUILD.bazel @@ -10,7 +10,7 @@ go_library( srcs = [ "functions.go", ], - importpath = "cel.dev/cel-go/interpreter/functions", + importpath = "github.com/authzed/cel-go/interpreter/functions", deps = [ "//common/functions:go_default_library", ], diff --git a/interpreter/functions/functions.go b/interpreter/functions/functions.go index 02123f1ab..331f39e9f 100644 --- a/interpreter/functions/functions.go +++ b/interpreter/functions/functions.go @@ -16,7 +16,7 @@ // interpreter and as declared within the checker#StandardDeclarations. package functions -import fn "cel.dev/cel-go/common/functions" +import fn "github.com/authzed/cel-go/common/functions" // Overload defines a named overload of a function, indicating an operand trait // which must be present on the first argument to the overload as well as one diff --git a/interpreter/interpretable.go b/interpreter/interpretable.go index d17e51d10..7e92984b5 100644 --- a/interpreter/interpretable.go +++ b/interpreter/interpretable.go @@ -18,12 +18,12 @@ import ( "fmt" "sync" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" ) // Interpretable evaluates an Activation and produces a value. diff --git a/interpreter/interpreter.go b/interpreter/interpreter.go index e5a583f6d..493b82b71 100644 --- a/interpreter/interpreter.go +++ b/interpreter/interpreter.go @@ -20,10 +20,10 @@ package interpreter import ( "errors" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // PlannerOption configures the program plan options during interpretable setup. diff --git a/interpreter/interpreter_test.go b/interpreter/interpreter_test.go index 76e9e1b41..5d27997a7 100644 --- a/interpreter/interpreter_test.go +++ b/interpreter/interpreter_test.go @@ -25,26 +25,26 @@ import ( "testing" "time" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/stdlib" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/stdlib" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" structpb "google.golang.org/protobuf/types/known/structpb" tpb "google.golang.org/protobuf/types/known/timestamppb" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" - proto2pb "cel.dev/cel-go/test/proto2pb" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto2pb "github.com/authzed/cel-go/test/proto2pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) type testCase struct { diff --git a/interpreter/optimizations.go b/interpreter/optimizations.go index c6478fa72..5a90513b6 100644 --- a/interpreter/optimizations.go +++ b/interpreter/optimizations.go @@ -17,8 +17,8 @@ package interpreter import ( "regexp" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // MatchesRegexOptimization optimizes the 'matches' standard library function by compiling the regex pattern and diff --git a/interpreter/planner.go b/interpreter/planner.go index bdf183be7..035b221d9 100644 --- a/interpreter/planner.go +++ b/interpreter/planner.go @@ -18,12 +18,12 @@ import ( "fmt" "strings" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // newPlanner creates an interpretablePlanner which references a Dispatcher, TypeProvider, diff --git a/interpreter/prune.go b/interpreter/prune.go index 9f55f1a81..ea659757b 100644 --- a/interpreter/prune.go +++ b/interpreter/prune.go @@ -15,12 +15,12 @@ package interpreter import ( - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" ) type astPruner struct { diff --git a/interpreter/prune_test.go b/interpreter/prune_test.go index 631d8195d..ad6f3954a 100644 --- a/interpreter/prune_test.go +++ b/interpreter/prune_test.go @@ -17,17 +17,17 @@ package interpreter import ( "testing" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/parser" - "cel.dev/cel-go/test" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/parser" + "github.com/authzed/cel-go/test" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) type testInfo struct { diff --git a/interpreter/runtimecost.go b/interpreter/runtimecost.go index 5558cf6fd..1a364c941 100644 --- a/interpreter/runtimecost.go +++ b/interpreter/runtimecost.go @@ -17,12 +17,12 @@ package interpreter import ( "errors" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/cost" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/cost" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" ) // WARNING: Any changes to cost calculations in this file require a corresponding change in checker/cost.go diff --git a/interpreter/runtimecost_test.go b/interpreter/runtimecost_test.go index b160318b7..a672e1e20 100644 --- a/interpreter/runtimecost_test.go +++ b/interpreter/runtimecost_test.go @@ -23,16 +23,16 @@ import ( "testing" "time" - "cel.dev/cel-go/checker" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/overloads" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/checker" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/overloads" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/parser" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) func TestTrackCostAdvanced(t *testing.T) { diff --git a/parser/BUILD.bazel b/parser/BUILD.bazel index c66661639..a4c722a88 100644 --- a/parser/BUILD.bazel +++ b/parser/BUILD.bazel @@ -16,7 +16,7 @@ go_library( "unescape.go", "unparser.go", ], - importpath = "cel.dev/cel-go/parser", + importpath = "github.com/authzed/cel-go/parser", visibility = ["//visibility:public"], deps = [ "//common:go_default_library", diff --git a/parser/errors.go b/parser/errors.go index 968dbe82b..013714a31 100644 --- a/parser/errors.go +++ b/parser/errors.go @@ -15,7 +15,7 @@ package parser import ( - "cel.dev/cel-go/common" + "github.com/authzed/cel-go/common" ) // parseErrors is a specialization of Errors. diff --git a/parser/gen/BUILD.bazel b/parser/gen/BUILD.bazel index 6c0187d58..e7f9d9fc4 100644 --- a/parser/gen/BUILD.bazel +++ b/parser/gen/BUILD.bazel @@ -19,7 +19,7 @@ go_library( "CEL.tokens", "CELLexer.tokens", ], - importpath = "cel.dev/cel-go/parser/gen", + importpath = "github.com/authzed/cel-go/parser/gen", deps = [ "@com_github_antlr4_go_antlr_v4//:go_default_library", ], diff --git a/parser/helper.go b/parser/helper.go index b043ef54b..bb2efed7a 100644 --- a/parser/helper.go +++ b/parser/helper.go @@ -19,10 +19,10 @@ import ( antlr "github.com/antlr4-go/antlr/v4" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) type parserHelper struct { diff --git a/parser/helper_test.go b/parser/helper_test.go index 22b2216fb..c7f983f34 100644 --- a/parser/helper_test.go +++ b/parser/helper_test.go @@ -17,8 +17,8 @@ package parser import ( "testing" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" "google.golang.org/protobuf/proto" ) diff --git a/parser/input.go b/parser/input.go index 9ccccd0fc..f9349a1e4 100644 --- a/parser/input.go +++ b/parser/input.go @@ -17,7 +17,7 @@ package parser import ( antlr "github.com/antlr4-go/antlr/v4" - "cel.dev/cel-go/common/runes" + "github.com/authzed/cel-go/common/runes" ) type charStream struct { diff --git a/parser/macro.go b/parser/macro.go index b9f53d7ba..3e1b5775b 100644 --- a/parser/macro.go +++ b/parser/macro.go @@ -17,11 +17,11 @@ package parser import ( "fmt" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // MacroOpt defines a functional option for configuring macro behavior. diff --git a/parser/macro_test.go b/parser/macro_test.go index 988138d1e..5c6c17e74 100644 --- a/parser/macro_test.go +++ b/parser/macro_test.go @@ -17,8 +17,8 @@ package parser import ( "testing" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" ) func TestReceiverVarArgMacro(t *testing.T) { diff --git a/parser/parser.go b/parser/parser.go index 2df20a704..2fb7fbadf 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -25,12 +25,12 @@ import ( antlr "github.com/antlr4-go/antlr/v4" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/runes" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/parser/gen" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/runes" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/parser/gen" ) // Parser encapsulates the context necessary to perform parsing for different expressions. diff --git a/parser/parser_test.go b/parser/parser_test.go index 7730a94ad..b01076e52 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -21,12 +21,12 @@ import ( "strings" "testing" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/debug" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/test" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/debug" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/test" ) var testCases = []testInfo{ diff --git a/parser/unparser.go b/parser/unparser.go index 8b5fdbdcc..6722601c3 100644 --- a/parser/unparser.go +++ b/parser/unparser.go @@ -21,10 +21,10 @@ import ( "strconv" "strings" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // Unparse takes an input expression and source position information and generates a human-readable diff --git a/parser/unparser_test.go b/parser/unparser_test.go index 6d0accfe9..8eab160dc 100644 --- a/parser/unparser_test.go +++ b/parser/unparser_test.go @@ -19,9 +19,9 @@ import ( "strings" "testing" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/operators" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/operators" "google.golang.org/protobuf/proto" diff --git a/policy/BUILD.bazel b/policy/BUILD.bazel index 72c78bcc8..879dc5791 100644 --- a/policy/BUILD.bazel +++ b/policy/BUILD.bazel @@ -37,7 +37,7 @@ go_library( "test_tag_handler_k8s.go", "yaml.go", ], - importpath = "cel.dev/cel-go/policy", + importpath = "github.com/authzed/cel-go/policy", deps = [ "//cel:go_default_library", "//common:go_default_library", diff --git a/policy/compiler.go b/policy/compiler.go index c9525d1aa..9fc599dc4 100644 --- a/policy/compiler.go +++ b/policy/compiler.go @@ -19,13 +19,13 @@ package policy import ( "fmt" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/containers" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/containers" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // CompiledRule represents the variables and match blocks associated with a rule block. diff --git a/policy/compiler_test.go b/policy/compiler_test.go index 11341d762..1f6ea9016 100644 --- a/policy/compiler_test.go +++ b/policy/compiler_test.go @@ -23,11 +23,11 @@ import ( "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/ext" - "cel.dev/cel-go/interpreter" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/ext" + "github.com/authzed/cel-go/interpreter" "github.com/google/go-cmp/cmp" ) diff --git a/policy/composer.go b/policy/composer.go index 87b30ca40..a05da2144 100644 --- a/policy/composer.go +++ b/policy/composer.go @@ -20,11 +20,11 @@ import ( "slices" "strings" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" ) // ComposerOption is a functional option used to configure a RuleComposer diff --git a/policy/composer_test.go b/policy/composer_test.go index 7a24ab359..db0334bbf 100644 --- a/policy/composer_test.go +++ b/policy/composer_test.go @@ -5,11 +5,11 @@ import ( "strings" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/debug" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/ext" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/debug" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/ext" ) func TestCompose(t *testing.T) { diff --git a/policy/config.go b/policy/config.go index 33ccd998b..43f466aa5 100644 --- a/policy/config.go +++ b/policy/config.go @@ -15,9 +15,9 @@ package policy import ( - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/env" - "cel.dev/cel-go/ext" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/ext" ) // FromConfig configures a CEL policy environment from a config file. diff --git a/policy/config_test.go b/policy/config_test.go index 3c544950c..7c8264d27 100644 --- a/policy/config_test.go +++ b/policy/config_test.go @@ -17,12 +17,12 @@ package policy import ( "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/env" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/env" "go.yaml.in/yaml/v3" - proto3pb "cel.dev/cel-go/test/proto3pb" + proto3pb "github.com/authzed/cel-go/test/proto3pb" ) func TestConfig(t *testing.T) { diff --git a/policy/go.mod b/policy/go.mod index ce093cae2..68c9404da 100644 --- a/policy/go.mod +++ b/policy/go.mod @@ -1,10 +1,10 @@ -module cel.dev/cel-go/policy +module github.com/authzed/cel-go/policy go 1.23.0 require ( - cel.dev/cel-go v0.26.1 - cel.dev/cel-go/tools v0.0.0-20251023215754-a36d461be521 + github.com/authzed/cel-go v0.26.1 + github.com/authzed/cel-go/tools v0.0.0-20251023215754-a36d461be521 github.com/google/go-cmp v0.7.0 go.yaml.in/yaml/v3 v3.0.4 google.golang.org/protobuf v1.36.10 @@ -19,6 +19,6 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf // indirect ) -replace cel.dev/cel-go => ../. +replace github.com/authzed/cel-go => ../. -replace cel.dev/cel-go/tools => ../tools/. +replace github.com/authzed/cel-go/tools => ../tools/. diff --git a/policy/helper_test.go b/policy/helper_test.go index 9dabc9920..53ef3762d 100644 --- a/policy/helper_test.go +++ b/policy/helper_test.go @@ -19,12 +19,12 @@ import ( "os" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/env" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/common/types/traits" - "cel.dev/cel-go/test" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types/traits" + "github.com/authzed/cel-go/test" "go.yaml.in/yaml/v3" ) diff --git a/policy/parser.go b/policy/parser.go index 65b9d18a0..d91abbc1e 100644 --- a/policy/parser.go +++ b/policy/parser.go @@ -20,9 +20,9 @@ import ( "go.yaml.in/yaml/v3" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/ast" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/ast" ) // SemanticType describes the evaluation semantic for a given policy block. diff --git a/policy/parser_test.go b/policy/parser_test.go index 5dc291a79..ddfa926f9 100644 --- a/policy/parser_test.go +++ b/policy/parser_test.go @@ -18,9 +18,9 @@ import ( "fmt" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/ext" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/ext" "github.com/google/go-cmp/cmp" "go.yaml.in/yaml/v3" ) diff --git a/policy/source.go b/policy/source.go index d07f91f1f..c11ddb2a8 100644 --- a/policy/source.go +++ b/policy/source.go @@ -15,7 +15,7 @@ package policy import ( - "cel.dev/cel-go/common" + "github.com/authzed/cel-go/common" ) // ByteSource converts a byte sequence and location description to a model.Source. diff --git a/policy/test/cel_test_runner.go b/policy/test/cel_test_runner.go index be5850b4a..a653a08a1 100644 --- a/policy/test/cel_test_runner.go +++ b/policy/test/cel_test_runner.go @@ -18,10 +18,10 @@ import ( "os" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/tools/celtest" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/tools/celtest" ) // TestCEL triggers the celtest test runner with a list of custom options which are used to set up diff --git a/policy/test/k8s_cel_test_runner.go b/policy/test/k8s_cel_test_runner.go index 6380ecafb..3829246e2 100644 --- a/policy/test/k8s_cel_test_runner.go +++ b/policy/test/k8s_cel_test_runner.go @@ -18,8 +18,8 @@ import ( "os" "testing" - "cel.dev/cel-go/policy" - "cel.dev/cel-go/tools/celtest" + "github.com/authzed/cel-go/policy" + "github.com/authzed/cel-go/tools/celtest" ) // TestK8sCEL triggers compilation and test execution of a k8s policy which diff --git a/repl/BUILD.bazel b/repl/BUILD.bazel index 2550ab1c1..efa0f6f60 100644 --- a/repl/BUILD.bazel +++ b/repl/BUILD.bazel @@ -26,7 +26,7 @@ go_library( "evaluator.go", "typefmt.go", ], - importpath = "cel.dev/cel-go/repl", + importpath = "github.com/authzed/cel-go/repl", deps = [ "//cel:go_default_library", "//checker:go_default_library", diff --git a/repl/commands.go b/repl/commands.go index 6e0ac7bd7..0ef82da33 100644 --- a/repl/commands.go +++ b/repl/commands.go @@ -21,8 +21,8 @@ import ( antlr "github.com/antlr4-go/antlr/v4" - "cel.dev/cel-go/common/env" - "cel.dev/cel-go/repl/parser" + "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/repl/parser" ) var ( diff --git a/repl/evaluator.go b/repl/evaluator.go index 4c4b70bc7..ff0b9cdff 100644 --- a/repl/evaluator.go +++ b/repl/evaluator.go @@ -23,14 +23,14 @@ import ( "sort" "strings" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/env" - envlib "cel.dev/cel-go/common/env" - "cel.dev/cel-go/common/functions" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/ext" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/env" + envlib "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/common/functions" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/ext" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" diff --git a/repl/evaluator_test.go b/repl/evaluator_test.go index 8bb383630..55ad190b5 100644 --- a/repl/evaluator_test.go +++ b/repl/evaluator_test.go @@ -18,11 +18,11 @@ import ( "strings" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/env" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/env" "github.com/google/go-cmp/cmp" - proto2pb "cel.dev/cel-go/test/proto2pb" + proto2pb "github.com/authzed/cel-go/test/proto2pb" ) var testTextDescriptorFile string = "testdata/attribute_context_fds.textproto" diff --git a/repl/go.mod b/repl/go.mod index 7118b13c8..51cc9d53a 100644 --- a/repl/go.mod +++ b/repl/go.mod @@ -1,9 +1,9 @@ -module cel.dev/cel-go/repl +module github.com/authzed/cel-go/repl go 1.23.0 require ( - cel.dev/cel-go v0.26.1 + github.com/authzed/cel-go v0.26.1 cel.dev/expr v0.25.1 github.com/antlr4-go/antlr/v4 v4.13.1 github.com/chzyer/readline v1.5.1 @@ -20,4 +20,4 @@ require ( golang.org/x/text v0.22.0 // indirect ) -replace cel.dev/cel-go => ../. +replace github.com/authzed/cel-go => ../. diff --git a/repl/main/BUILD.bazel b/repl/main/BUILD.bazel index 0fa269833..52cfbabb0 100644 --- a/repl/main/BUILD.bazel +++ b/repl/main/BUILD.bazel @@ -21,14 +21,14 @@ package( go_binary( name = "main", embed = [":go_default_library"], - importpath = "cel.dev/cel-go/repl/main", + importpath = "github.com/authzed/cel-go/repl/main", visibility = ["//visibility:public"], ) go_library( name = "go_default_library", srcs = ["main.go"], - importpath = "cel.dev/cel-go/repl/main", + importpath = "github.com/authzed/cel-go/repl/main", visibility = ["//visibility:private"], deps = [ "//repl:go_default_library", diff --git a/repl/main/main.go b/repl/main/main.go index 4d6f5cd00..277a880a2 100644 --- a/repl/main/main.go +++ b/repl/main/main.go @@ -45,7 +45,7 @@ import ( "os" "path/filepath" - "cel.dev/cel-go/repl" + "github.com/authzed/cel-go/repl" "github.com/chzyer/readline" ) diff --git a/repl/parser/BUILD.bazel b/repl/parser/BUILD.bazel index 821ac1391..b0e932e5f 100644 --- a/repl/parser/BUILD.bazel +++ b/repl/parser/BUILD.bazel @@ -23,7 +23,7 @@ go_library( name = "go_default_library", srcs = glob(["*.go"], exclude=["*_test.go"]), data = glob(["*.tokens"]), - importpath = "cel.dev/cel-go/repl/parser", + importpath = "github.com/authzed/cel-go/repl/parser", deps = [ "@com_github_antlr4_go_antlr_v4//:go_default_library", ], diff --git a/repl/typefmt.go b/repl/typefmt.go index 3f559e286..9fb760d8a 100644 --- a/repl/typefmt.go +++ b/repl/typefmt.go @@ -20,10 +20,10 @@ import ( antlr "github.com/antlr4-go/antlr/v4" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/env" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/repl/parser" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/repl/parser" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/repl/typefmt_test.go b/repl/typefmt_test.go index 662247876..55580235d 100644 --- a/repl/typefmt_test.go +++ b/repl/typefmt_test.go @@ -17,7 +17,7 @@ package repl import ( "testing" - "cel.dev/cel-go/cel" + "github.com/authzed/cel-go/cel" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/test/BUILD.bazel b/test/BUILD.bazel index 3a6884590..3ae35ea31 100644 --- a/test/BUILD.bazel +++ b/test/BUILD.bazel @@ -23,7 +23,7 @@ go_library( "expr.go", "suite.go", ], - importpath = "cel.dev/cel-go/test", + importpath = "github.com/authzed/cel-go/test", deps = [ "//common/operators:go_default_library", "//common/types:go_default_library", diff --git a/test/async.go b/test/async.go index a4ab6ebbb..7f08d5c21 100644 --- a/test/async.go +++ b/test/async.go @@ -18,8 +18,8 @@ import ( "context" "time" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" ) // FakeRPC returns a blocking async function which simulates an RPC that succeeds after a short diff --git a/test/bench/BUILD.bazel b/test/bench/BUILD.bazel index e4ccee5e9..7091227db 100644 --- a/test/bench/BUILD.bazel +++ b/test/bench/BUILD.bazel @@ -9,7 +9,7 @@ go_library( srcs = [ "bench.go", ], - importpath = "cel.dev/cel-go/test/bench", + importpath = "github.com/authzed/cel-go/test/bench", deps = [ "//cel:go_default_library", "//ext:go_default_library", diff --git a/test/bench/bench.go b/test/bench/bench.go index 95d23cec3..cd7992f36 100644 --- a/test/bench/bench.go +++ b/test/bench/bench.go @@ -19,10 +19,10 @@ import ( "fmt" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/ext" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/ext" ) // Case represents a human-readable expression and an expected output given an input diff --git a/test/bench/bench_test.go b/test/bench/bench_test.go index d0798b11b..b1834ebf1 100644 --- a/test/bench/bench_test.go +++ b/test/bench/bench_test.go @@ -17,7 +17,7 @@ package bench import ( "testing" - "cel.dev/cel-go/cel" + "github.com/authzed/cel-go/cel" ) func BenchmarkReferenceCases(b *testing.B) { diff --git a/test/expr.go b/test/expr.go index 1390a6498..548c8b943 100644 --- a/test/expr.go +++ b/test/expr.go @@ -15,7 +15,7 @@ package test import ( - "cel.dev/cel-go/common/operators" + "github.com/authzed/cel-go/common/operators" "google.golang.org/protobuf/proto" diff --git a/test/proto2pb/BUILD.bazel b/test/proto2pb/BUILD.bazel index 82f27495c..4bd712d99 100644 --- a/test/proto2pb/BUILD.bazel +++ b/test/proto2pb/BUILD.bazel @@ -23,7 +23,7 @@ go_library( "test_all_types.pb.go", "test_extensions.pb.go", ], - importpath = "cel.dev/cel-go/test/proto2pb", + importpath = "github.com/authzed/cel-go/test/proto2pb", deps = [ "@org_golang_google_protobuf//proto:go_default_library", "@org_golang_google_protobuf//reflect/protoreflect:go_default_library", @@ -59,7 +59,7 @@ proto_library( go_proto_library( name = "test_all_types_go_proto", - importpath = "cel.dev/cel-go/test/proto2pb", + importpath = "github.com/authzed/cel-go/test/proto2pb", protos = [ ":test_all_types_proto", ":test_extensions_proto", diff --git a/test/proto2pb/test_all_types.proto b/test/proto2pb/test_all_types.proto index aaf1677ed..4e72647bb 100644 --- a/test/proto2pb/test_all_types.proto +++ b/test/proto2pb/test_all_types.proto @@ -3,7 +3,7 @@ syntax = "proto2"; package google.expr.proto2.test; -option go_package = "cel.dev/cel-go/test/proto2pb"; +option go_package = "github.com/authzed/cel-go/test/proto2pb"; import "google/protobuf/any.proto"; import "google/protobuf/duration.proto"; diff --git a/test/proto2pb/test_extensions.proto b/test/proto2pb/test_extensions.proto index c41db5a08..3e2e5ca77 100644 --- a/test/proto2pb/test_extensions.proto +++ b/test/proto2pb/test_extensions.proto @@ -2,7 +2,7 @@ syntax = "proto2"; package google.expr.proto2.test; -option go_package = "cel.dev/cel-go/test/proto2pb"; +option go_package = "github.com/authzed/cel-go/test/proto2pb"; import "google/protobuf/wrappers.proto"; import "test/proto2pb/test_all_types.proto"; diff --git a/test/proto3pb/BUILD.bazel b/test/proto3pb/BUILD.bazel index b30cc56c6..b33ceed4d 100644 --- a/test/proto3pb/BUILD.bazel +++ b/test/proto3pb/BUILD.bazel @@ -24,7 +24,7 @@ go_library( "test_all_types.pb.go", "test_import.pb.go", ], - importpath = "cel.dev/cel-go/test/proto3pb", + importpath = "github.com/authzed/cel-go/test/proto3pb", deps = [ "@org_golang_google_protobuf//proto:go_default_library", "@org_golang_google_protobuf//types/known/anypb:go_default_library", @@ -57,7 +57,7 @@ proto_library( go_proto_library( name = "test_all_types_go_proto", - importpath = "cel.dev/cel-go/test/proto3pb", + importpath = "github.com/authzed/cel-go/test/proto3pb", protos = [ ":test_all_types_proto", ":test_import_proto", diff --git a/test/proto3pb/test_all_types.proto b/test/proto3pb/test_all_types.proto index dce24bac0..cef88cda9 100644 --- a/test/proto3pb/test_all_types.proto +++ b/test/proto3pb/test_all_types.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package google.expr.proto3.test; -option go_package = "cel.dev/cel-go/test/proto3pb"; +option go_package = "github.com/authzed/cel-go/test/proto3pb"; import "google/protobuf/any.proto"; import "google/protobuf/duration.proto"; diff --git a/test/proto3pb/test_import.proto b/test/proto3pb/test_import.proto index afb62608d..31203de19 100644 --- a/test/proto3pb/test_import.proto +++ b/test/proto3pb/test_import.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package google.expr.proto3.test; -option go_package = "cel.dev/cel-go/test/proto3pb"; +option go_package = "github.com/authzed/cel-go/test/proto3pb"; enum ImportedGlobalEnum { IMPORT_FOO = 0; diff --git a/tools/celtest/BUILD.bazel b/tools/celtest/BUILD.bazel index bb1ad7826..271c49821 100644 --- a/tools/celtest/BUILD.bazel +++ b/tools/celtest/BUILD.bazel @@ -26,7 +26,7 @@ go_library( "test_coverage_reporter.go", "test_runner.go", ], - importpath = "cel.dev/cel-go/tools/celtest", + importpath = "github.com/authzed/cel-go/tools/celtest", deps = [ "//cel:go_default_library", "//common/ast:go_default_library", diff --git a/tools/celtest/test_coverage_reporter.go b/tools/celtest/test_coverage_reporter.go index e1eab67b5..ff2229ca2 100644 --- a/tools/celtest/test_coverage_reporter.go +++ b/tools/celtest/test_coverage_reporter.go @@ -20,9 +20,9 @@ import ( "strings" "testing" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/parser" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/parser" ) // reportCoverage reports the coverage information for the provided programs. diff --git a/tools/celtest/test_coverage_reporter_test.go b/tools/celtest/test_coverage_reporter_test.go index ac4a1de47..3cbac23ea 100644 --- a/tools/celtest/test_coverage_reporter_test.go +++ b/tools/celtest/test_coverage_reporter_test.go @@ -18,9 +18,9 @@ package celtest import ( "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/tools/compiler" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/tools/compiler" ) func TestCoverageStats(t *testing.T) { diff --git a/tools/celtest/test_runner.go b/tools/celtest/test_runner.go index 5e04bde59..202205367 100644 --- a/tools/celtest/test_runner.go +++ b/tools/celtest/test_runner.go @@ -25,16 +25,16 @@ import ( "strings" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/ast" - "cel.dev/cel-go/common/debug" - "cel.dev/cel-go/common/env" - "cel.dev/cel-go/common/operators" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/interpreter" - "cel.dev/cel-go/test" - "cel.dev/cel-go/tools/compiler" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/ast" + "github.com/authzed/cel-go/common/debug" + "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/common/operators" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/interpreter" + "github.com/authzed/cel-go/test" + "github.com/authzed/cel-go/tools/compiler" "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/encoding/prototext" diff --git a/tools/celtest/test_runner_test.go b/tools/celtest/test_runner_test.go index 0ccfd391f..3b788eb68 100644 --- a/tools/celtest/test_runner_test.go +++ b/tools/celtest/test_runner_test.go @@ -18,13 +18,13 @@ package celtest import ( "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/decls" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/common/types/ref" - "cel.dev/cel-go/policy" - "cel.dev/cel-go/test" - "cel.dev/cel-go/tools/compiler" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/decls" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/common/types/ref" + "github.com/authzed/cel-go/policy" + "github.com/authzed/cel-go/test" + "github.com/authzed/cel-go/tools/compiler" "go.yaml.in/yaml/v3" diff --git a/tools/compiler/BUILD.bazel b/tools/compiler/BUILD.bazel index 58ee40197..6e16f3ca0 100644 --- a/tools/compiler/BUILD.bazel +++ b/tools/compiler/BUILD.bazel @@ -24,7 +24,7 @@ go_library( srcs = [ "compiler.go", ], - importpath = "cel.dev/cel-go/tools/compiler", + importpath = "github.com/authzed/cel-go/tools/compiler", deps = [ "//cel:go_default_library", "//common:go_default_library", diff --git a/tools/compiler/compiler.go b/tools/compiler/compiler.go index e5830f364..84c3110b6 100644 --- a/tools/compiler/compiler.go +++ b/tools/compiler/compiler.go @@ -24,12 +24,12 @@ import ( "go.yaml.in/yaml/v3" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common" - "cel.dev/cel-go/common/env" - "cel.dev/cel-go/common/types" - "cel.dev/cel-go/ext" - "cel.dev/cel-go/policy" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common" + "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/common/types" + "github.com/authzed/cel-go/ext" + "github.com/authzed/cel-go/policy" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" diff --git a/tools/compiler/compiler_test.go b/tools/compiler/compiler_test.go index 4dbca315d..e9be8e5b3 100644 --- a/tools/compiler/compiler_test.go +++ b/tools/compiler/compiler_test.go @@ -18,10 +18,10 @@ import ( "reflect" "testing" - "cel.dev/cel-go/cel" - "cel.dev/cel-go/common/env" - "cel.dev/cel-go/ext" - "cel.dev/cel-go/policy" + "github.com/authzed/cel-go/cel" + "github.com/authzed/cel-go/common/env" + "github.com/authzed/cel-go/ext" + "github.com/authzed/cel-go/policy" celpb "cel.dev/expr" configpb "cel.dev/expr/conformance" diff --git a/tools/go.mod b/tools/go.mod index 3fb749df2..d794fdd2a 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -1,10 +1,10 @@ -module cel.dev/cel-go/tools +module github.com/authzed/cel-go/tools go 1.23.0 require ( - cel.dev/cel-go v0.26.1 - cel.dev/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1 + github.com/authzed/cel-go v0.26.1 + github.com/authzed/cel-go/policy v0.0.0-20250311174852-f5ea07b389a1 cel.dev/expr v0.25.1 github.com/google/go-cmp v0.7.0 go.yaml.in/yaml/v3 v3.0.4 @@ -19,6 +19,6 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf // indirect ) -replace cel.dev/cel-go => ../. +replace github.com/authzed/cel-go => ../. -replace cel.dev/cel-go/policy => ../policy +replace github.com/authzed/cel-go/policy => ../policy From 829691bbead4de06f6f7574c4d336681f6df73fc Mon Sep 17 00:00:00 2001 From: Maria Ines Parnisari Date: Wed, 19 Aug 2026 14:08:53 -0700 Subject: [PATCH 37/37] sync: follow upstream's move to cel.dev/cel-go --- .github/workflows/sync-upstream.sh | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/sync-upstream.sh b/.github/workflows/sync-upstream.sh index 711452133..97ab91b7d 100755 --- a/.github/workflows/sync-upstream.sh +++ b/.github/workflows/sync-upstream.sh @@ -7,10 +7,16 @@ # sync-upstream.sh rename [--reverse] # just the rename # # This fork differs from upstream in exactly one way: every -# `github.com/google/cel-go` import is rewritten to `github.com/authzed/cel-go`. +# `cel.dev/cel-go` import is rewritten to `github.com/authzed/cel-go`. # Merging upstream directly means both sides edited the same import lines, so it # conflicts on nearly every file upstream touched -- ~77 conflicts, none real. # +# UPSTREAM_PATH is whatever upstream calls itself today; it was +# `github.com/google/cel-go` until v0.32.0 moved it to `cel.dev/cel-go`. A sync +# spanning such a move has to un-rename to the old path (to match the merge +# base) and re-rename from the new one, which means two runs with the constant +# changed in between. +# # So the rename is applied last, and never merged: # # 1. un-rename, making our tree match the upstream commit we last synced from @@ -37,7 +43,7 @@ set -euo pipefail cd "$(git rev-parse --show-toplevel)" -UPSTREAM_PATH="github.com/google/cel-go" +UPSTREAM_PATH="cel.dev/cel-go" FORK_PATH="github.com/authzed/cel-go" UPSTREAM_REMOTE="${UPSTREAM_REMOTE:-upstream}" @@ -72,8 +78,12 @@ rename() { to="$UPSTREAM_PATH" fi - # -I skips binary files, and git grep only looks at tracked ones. - files="$(git grep -I --name-only --fixed-strings -e "$from" -- . || true)" + # -I skips binary files, and git grep only looks at tracked ones. This + # script is excluded because it names both paths itself: renaming it would + # collapse UPSTREAM_PATH and FORK_PATH onto the same value and break the + # next sync. + files="$(git grep -I --name-only --fixed-strings -e "$from" \ + -- . ':(exclude).github/workflows/sync-upstream.sh' || true)" if [[ -z "$files" ]]; then echo "rename: no occurrences of ${from}; nothing to do" return 0