Skip to content

Commit 1963c1e

Browse files
l46kokcopybara-github
authored andcommitted
Add value type parameter in StructValue
PiperOrigin-RevId: 904602834
1 parent 1e1d8ea commit 1963c1e

6 files changed

Lines changed: 53 additions & 21 deletions

File tree

common/src/main/java/dev/cel/common/values/ProtoMessageLiteValue.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
*/
3636
@AutoValue
3737
@Immutable
38-
public abstract class ProtoMessageLiteValue extends StructValue<String> {
38+
public abstract class ProtoMessageLiteValue extends StructValue<String, MessageLite> {
3939

4040
@Override
4141
public abstract MessageLite value();

common/src/main/java/dev/cel/common/values/ProtoMessageValue.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
/** ProtoMessageValue is a struct value with protobuf support. */
2929
@AutoValue
3030
@Immutable
31-
public abstract class ProtoMessageValue extends StructValue<String> {
31+
public abstract class ProtoMessageValue extends StructValue<String, Message> {
3232

3333
@Override
3434
public abstract Message value();

common/src/main/java/dev/cel/common/values/StructValue.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,20 @@
1919
/**
2020
* StructValue is a representation of a structured object with typed properties.
2121
*
22-
* <p>Users may extend from this class to provide a custom struct that CEL can understand (ex:
23-
* POJOs). Custom struct implementations must provide all functionalities denoted in the CEL
24-
* specification, such as field selection, presence testing and new object creation.
22+
* <p>Users may extend from this class to provide a custom struct that CEL can understand by
23+
* wrapping a native Java object (e.g., a POJO or a Map). Custom struct implementations must provide
24+
* all functionalities denoted in the CEL specification, such as field selection, presence testing
25+
* and new object creation.
2526
*
2627
* <p>For an expression `e` selecting a field `f`, `e.f` must throw an exception if `f` does not
2728
* exist in the struct (i.e: hasField returns false). If the field exists but is not set, the
2829
* implementation should return an appropriate default value based on the struct's semantics.
30+
*
31+
* @param <F> The type of the field identifier (typically String).
32+
* @param <V> The type of the wrapped native object.
2933
*/
3034
@Immutable
31-
public abstract class StructValue<T> extends CelValue implements SelectableValue<T> {}
35+
public abstract class StructValue<F, V> extends CelValue implements SelectableValue<F> {
36+
@Override
37+
public abstract V value();
38+
}

common/src/test/java/dev/cel/common/values/OptionalValueTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public void celTypeTest() {
141141
}
142142

143143
@SuppressWarnings("Immutable") // Test only
144-
private static class CelCustomStruct extends StructValue<String> {
144+
private static class CelCustomStruct extends StructValue<String, Long> {
145145
private final long data;
146146

147147
@Override

common/src/test/java/dev/cel/common/values/StructValueTest.java

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,15 @@ public Optional<CelType> findType(String typeName) {
7070
public void emptyStruct() {
7171
CelCustomStructValue celCustomStruct = new CelCustomStructValue(0);
7272

73-
assertThat(celCustomStruct.value()).isEqualTo(celCustomStruct);
73+
assertThat(celCustomStruct.value().getData()).isEqualTo(0L);
7474
assertThat(celCustomStruct.isZeroValue()).isTrue();
7575
}
7676

7777
@Test
7878
public void constructStruct() {
7979
CelCustomStructValue celCustomStruct = new CelCustomStructValue(5);
8080

81-
assertThat(celCustomStruct.value()).isEqualTo(celCustomStruct);
81+
assertThat(celCustomStruct.value().getData()).isEqualTo(5L);
8282
assertThat(celCustomStruct.isZeroValue()).isFalse();
8383
}
8484

@@ -122,9 +122,9 @@ public void evaluate_usingCustomClass_createNewStruct() throws Exception {
122122
.build();
123123
CelAbstractSyntaxTree ast = cel.compile("custom_struct{data: 50}").getAst();
124124

125-
CelCustomStructValue result = (CelCustomStructValue) cel.createProgram(ast).eval();
125+
CustomPojo result = (CustomPojo) cel.createProgram(ast).eval();
126126

127-
assertThat(result.data).isEqualTo(50);
127+
assertThat(result.getData()).isEqualTo(50);
128128
}
129129

130130
@Test
@@ -142,7 +142,7 @@ public void evaluate_usingCustomClass_asVariable() throws Exception {
142142
(CelCustomStructValue)
143143
cel.createProgram(ast).eval(ImmutableMap.of("a", new CelCustomStructValue(10)));
144144

145-
assertThat(result.data).isEqualTo(10);
145+
assertThat(result.value().getData()).isEqualTo(10);
146146
}
147147

148148
@Test
@@ -197,19 +197,45 @@ public void evaluate_usingMultipleProviders_selectFieldFromCustomClass() throws
197197
// TODO: Bring back evaluate_usingMultipleProviders_selectFieldFromProtobufMessage
198198
// once planner is exposed from factory
199199

200+
private static class CustomPojo implements SelectableValue<String> {
201+
private final long data;
202+
203+
CustomPojo(long data) {
204+
this.data = data;
205+
}
206+
207+
long getData() {
208+
return data;
209+
}
210+
211+
@Override
212+
public Object select(String field) {
213+
return find(field)
214+
.orElseThrow(() -> new IllegalArgumentException("Invalid field name: " + field));
215+
}
216+
217+
@Override
218+
public Optional<Object> find(String field) {
219+
if (field.equals("data")) {
220+
return Optional.of(data);
221+
}
222+
return Optional.empty();
223+
}
224+
}
225+
200226
@SuppressWarnings("Immutable") // Test only
201-
private static class CelCustomStructValue extends StructValue<String> {
227+
private static class CelCustomStructValue extends StructValue<String, CustomPojo> {
202228

203-
private final long data;
229+
private final CustomPojo pojo;
204230

205231
@Override
206-
public CelCustomStructValue value() {
207-
return this;
232+
public CustomPojo value() {
233+
return pojo;
208234
}
209235

210236
@Override
211237
public boolean isZeroValue() {
212-
return data == 0;
238+
return pojo.getData() == 0;
213239
}
214240

215241
@Override
@@ -226,7 +252,7 @@ public Object select(String field) {
226252
@Override
227253
public Optional<Object> find(String field) {
228254
if (field.equals("data")) {
229-
return Optional.of(value().data);
255+
return Optional.of(pojo.getData());
230256
}
231257

232258
return Optional.empty();
@@ -237,7 +263,7 @@ private CelCustomStructValue(Map<String, Object> fields) {
237263
}
238264

239265
private CelCustomStructValue(long data) {
240-
this.data = data;
266+
this.pojo = new CustomPojo(data);
241267
}
242268
}
243269
}

runtime/src/main/java/dev/cel/runtime/planner/EvalCreateStruct.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,8 @@ Object evalInternal(GlobalResolver resolver, ExecutionFrame frame) {
8787
.newValue(structType.name(), Collections.unmodifiableMap(fieldValues))
8888
.orElseThrow(
8989
() -> new IllegalArgumentException("Type name not found: " + structType.name()));
90-
9190
if (value instanceof StructValue) {
92-
return ((StructValue<?>) value).value();
91+
return ((StructValue<?, ?>) value).value();
9392
}
9493

9594
return value;

0 commit comments

Comments
 (0)