Skip to content

Commit 71b3ca5

Browse files
l46kokcopybara-github
authored andcommitted
Add optional.unwrap
PiperOrigin-RevId: 666904662
1 parent f4292b5 commit 71b3ca5

5 files changed

Lines changed: 59 additions & 0 deletions

File tree

extensions/src/main/java/dev/cel/extensions/CelOptionalLibrary.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import static com.google.common.base.Preconditions.checkArgument;
1818
import static com.google.common.base.Preconditions.checkNotNull;
19+
import static com.google.common.collect.ImmutableList.toImmutableList;
1920

2021
import com.google.common.collect.ImmutableList;
2122
import com.google.common.primitives.UnsignedLong;
@@ -58,6 +59,7 @@ public enum Function {
5859
HAS_VALUE("hasValue"),
5960
OPTIONAL_NONE("optional.none"),
6061
OPTIONAL_OF("optional.of"),
62+
OPTIONAL_UNWRAP("optional.unwrap"),
6163
OPTIONAL_OF_NON_ZERO_VALUE("optional.ofNonZeroValue"),
6264
OR("or"),
6365
OR_VALUE("orValue");
@@ -117,6 +119,10 @@ public void setCheckerOptions(CelCheckerBuilder checkerBuilder) {
117119
CelFunctionDecl.newFunctionDeclaration(
118120
Function.HAS_VALUE.getFunction(),
119121
CelOverloadDecl.newMemberOverload("optional_hasValue", SimpleType.BOOL, optionalTypeV)),
122+
CelFunctionDecl.newFunctionDeclaration(
123+
Function.OPTIONAL_UNWRAP.getFunction(),
124+
CelOverloadDecl.newGlobalOverload(
125+
"optional_unwrap_list", listTypeV, ListType.create(optionalTypeV))),
120126
// Note: Implementation of "or" and "orValue" are special-cased inside the interpreter.
121127
// Hence, their bindings are not provided here.
122128
CelFunctionDecl.newFunctionDeclaration(
@@ -165,6 +171,7 @@ public void setCheckerOptions(CelCheckerBuilder checkerBuilder) {
165171
}
166172

167173
@Override
174+
@SuppressWarnings("unchecked")
168175
public void setRuntimeOptions(CelRuntimeBuilder runtimeBuilder) {
169176
runtimeBuilder.addFunctionBindings(
170177
CelRuntime.CelFunctionBinding.from("optional_of", Object.class, Optional::of),
@@ -177,6 +184,8 @@ public void setRuntimeOptions(CelRuntimeBuilder runtimeBuilder) {
177184
}
178185
return Optional.of(val);
179186
}),
187+
CelRuntime.CelFunctionBinding.from(
188+
"optional_unwrap_list", Collection.class, CelOptionalLibrary::elideOptionalCollection),
180189
CelRuntime.CelFunctionBinding.from(
181190
"optional_none", ImmutableList.of(), val -> Optional.empty()),
182191
CelRuntime.CelFunctionBinding.from(
@@ -185,6 +194,10 @@ public void setRuntimeOptions(CelRuntimeBuilder runtimeBuilder) {
185194
"optional_hasValue", Object.class, val -> ((Optional<?>) val).isPresent()));
186195
}
187196

197+
private static ImmutableList<Object> elideOptionalCollection(Collection<Optional<Object>> list) {
198+
return list.stream().filter(Optional::isPresent).map(Optional::get).collect(toImmutableList());
199+
}
200+
188201
// TODO: This will need to be adapted to handle an intermediate CelValue instead,
189202
// akin to Zeroer interface in Go. Currently, it is unable to handle zero-values for a
190203
// user-defined custom type.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Source: optional.unwrap([])
2+
=====>
3+
bindings: {}
4+
result: []
5+
6+
Source: optional.unwrap([optional.none(), optional.of(1), optional.of(str)])
7+
declare str {
8+
value string
9+
}
10+
=====>
11+
bindings: {str=foo}
12+
result: [1, foo]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Source: optional.unwrap([dyn(1)])
2+
=====>
3+
bindings: {}
4+
error: evaluation error: Function 'optional_unwrap_list' failed with arg(s) '[1]'
5+
error_code: INTERNAL_ERROR

testing/src/main/java/dev/cel/testing/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ java_library(
100100
"//common/internal:file_descriptor_converter",
101101
"//common/resources/testdata/proto3:standalone_global_enum_java_proto",
102102
"//common/types:cel_types",
103+
"//common/types:type_providers",
104+
"//extensions:optional_library",
103105
"//runtime",
104106
"//runtime:runtime_helper",
105107
"@@protobuf~//java/core",

testing/src/main/java/dev/cel/testing/BaseInterpreterTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@
6565
import dev.cel.common.CelProtoAbstractSyntaxTree;
6666
import dev.cel.common.internal.DefaultDescriptorPool;
6767
import dev.cel.common.internal.FileDescriptorSetConverter;
68+
import dev.cel.common.types.CelTypeProvider;
6869
import dev.cel.common.types.CelTypes;
70+
import dev.cel.extensions.CelOptionalLibrary;
6971
import dev.cel.runtime.CelEvaluationException;
7072
import dev.cel.runtime.CelRuntime;
7173
import dev.cel.runtime.CelRuntime.CelFunctionBinding;
@@ -127,11 +129,19 @@ public BaseInterpreterTest(CelOptions celOptions, boolean useNativeCelType) {
127129
this.celOptions = celOptions;
128130
this.celRuntime =
129131
CelRuntimeFactory.standardCelRuntimeBuilder()
132+
.addLibraries(CelOptionalLibrary.INSTANCE)
130133
.addFileTypes(TEST_FILE_DESCRIPTORS)
131134
.setOptions(celOptions)
132135
.build();
133136
}
134137

138+
@Override
139+
protected void prepareCompiler(CelTypeProvider typeProvider) {
140+
super.prepareCompiler(typeProvider);
141+
this.celCompiler =
142+
celCompiler.toCompilerBuilder().addLibraries(CelOptionalLibrary.INSTANCE).build();
143+
}
144+
135145
private CelAbstractSyntaxTree compileTestCase() {
136146
CelAbstractSyntaxTree ast = prepareTest(TEST_FILE_DESCRIPTORS);
137147
if (ast == null) {
@@ -494,6 +504,23 @@ public void messages_error() {
494504
runTest();
495505
}
496506

507+
@Test
508+
public void optional() {
509+
// TODO: Move existing optional tests here to also test CelValue runtime
510+
source = "optional.unwrap([])";
511+
runTest();
512+
513+
declareVariable("str", CelTypes.STRING);
514+
source = "optional.unwrap([optional.none(), optional.of(1), optional.of(str)])";
515+
runTest(ImmutableMap.of("str", "foo"));
516+
}
517+
518+
@Test
519+
public void optional_errors() {
520+
source = "optional.unwrap([dyn(1)])";
521+
runTest();
522+
}
523+
497524
@Test
498525
public void has() throws Exception {
499526
TestAllTypes nestedMessage =

0 commit comments

Comments
 (0)