Skip to content

Commit f6a8711

Browse files
Implement getField CEL function (#225)
I think it has to be a global overload, a member overload would require a generic on the receiver which doesn't seem possible. I'm proposing this as an eventual replacement (before 1.0) of our hack around the fact that the `in` identifier is reserved in CEL. This is especially urgent for protovalidate-cc which is currently carrying patches to the CEL implementation in order to enable it, since cel-cpp doesn't allow this sort of functionality to be added in at runtime. Protovalidate PR: bufbuild/protovalidate#352
1 parent d768025 commit f6a8711

File tree

2 files changed

+60
-7
lines changed

2 files changed

+60
-7
lines changed

cel/library.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,24 @@ func (l library) CompileOptions() []cel.EnvOption { //nolint:funlen,gocyclo
7878
l.uniqueMemberOverload(cel.StringType, l.uniqueScalar),
7979
l.uniqueMemberOverload(cel.BytesType, l.uniqueBytes),
8080
),
81+
cel.Function("getField",
82+
cel.Overload(
83+
"get_field_any_string",
84+
[]*cel.Type{cel.AnyType, cel.StringType},
85+
cel.AnyType,
86+
cel.FunctionBinding(func(values ...ref.Val) ref.Val {
87+
message, ok := values[0].(traits.Indexer)
88+
if !ok {
89+
return types.UnsupportedRefValConversionErr(values[0])
90+
}
91+
fieldName, ok := values[1].Value().(string)
92+
if !ok {
93+
return types.UnsupportedRefValConversionErr(values[1])
94+
}
95+
return message.Get(types.String(fieldName))
96+
}),
97+
),
98+
),
8199
cel.Function("isNan",
82100
cel.MemberOverload(
83101
"double_is_nan_bool",

cel/library_test.go

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ package cel
1717
import (
1818
"testing"
1919

20+
"github.com/bufbuild/protovalidate-go/internal/gen/buf/validate/conformance/cases"
2021
"github.com/google/cel-go/cel"
22+
"github.com/google/cel-go/common/types"
23+
"github.com/google/cel-go/common/types/ref"
2124
"github.com/google/cel-go/interpreter"
2225
"github.com/stretchr/testify/assert"
2326
"github.com/stretchr/testify/require"
@@ -26,15 +29,31 @@ import (
2629
func TestCELLib(t *testing.T) {
2730
t.Parallel()
2831

29-
env, err := cel.NewEnv(cel.Lib(NewLibrary()))
32+
testValue := cases.StringConst_builder{Val: "test_string"}.Build()
33+
34+
activation, err := interpreter.NewActivation(map[string]any{
35+
"test": testValue,
36+
})
37+
require.NoError(t, err)
38+
39+
env, err := cel.NewEnv(
40+
cel.Lib(NewLibrary()),
41+
cel.Variable(
42+
"test",
43+
cel.ObjectType(
44+
string(testValue.ProtoReflect().Descriptor().FullName()),
45+
),
46+
),
47+
)
48+
3049
require.NoError(t, err)
3150

3251
t.Run("ext", func(t *testing.T) {
3352
t.Parallel()
3453

3554
tests := []struct {
3655
expr string
37-
ex bool
56+
ex any
3857
}{
3958
{"0.0.isInf()", false},
4059
{"0.0.isNan()", false},
@@ -197,18 +216,34 @@ func TestCELLib(t *testing.T) {
197216
"'[email protected] '.isEmail()",
198217
false,
199218
},
219+
{
220+
"getField(test, 'val')",
221+
"test_string",
222+
},
223+
{
224+
"getField(test, 'lav')",
225+
types.NewErrFromString("no such field"),
226+
},
227+
{
228+
"getField(0, 'val')",
229+
types.NewErrFromString("unsupported conversion"),
230+
},
200231
}
201232

202233
for _, tc := range tests {
203234
test := tc
204235
t.Run(test.expr, func(t *testing.T) {
205236
t.Parallel()
206237
prog := buildTestProgram(t, env, test.expr)
207-
val, _, err := prog.Eval(interpreter.EmptyActivation())
208-
require.NoError(t, err)
209-
isUnique, ok := val.Value().(bool)
210-
require.True(t, ok)
211-
assert.Equal(t, test.ex, isUnique)
238+
val, _, err := prog.Eval(activation)
239+
if refEx, ok := test.ex.(ref.Val); ok && types.IsError(refEx) {
240+
refErr, ok := refEx.Value().(error)
241+
require.True(t, ok)
242+
assert.ErrorContains(t, err, refErr.Error())
243+
} else {
244+
require.NoError(t, err)
245+
assert.Equal(t, test.ex, val.Value())
246+
}
212247
})
213248
}
214249
})

0 commit comments

Comments
 (0)