Skip to content

Commit bf3c392

Browse files
l46kokcopybara-github
authored andcommitted
Add ProtoMessageLiteValue and ProtoLiteCelValueConverter
PiperOrigin-RevId: 750834204
1 parent 3010082 commit bf3c392

File tree

8 files changed

+345
-0
lines changed

8 files changed

+345
-0
lines changed

common/internal/BUILD.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ java_library(
1616
exports = ["//common/src/main/java/dev/cel/common/internal:comparison_functions"],
1717
)
1818

19+
java_library(
20+
name = "cel_lite_descriptor_pool",
21+
exports = ["//common/src/main/java/dev/cel/common/internal:cel_lite_descriptor_pool"],
22+
)
23+
1924
java_library(
2025
name = "default_lite_descriptor_pool",
2126
exports = ["//common/src/main/java/dev/cel/common/internal:default_lite_descriptor_pool"],

common/src/main/java/dev/cel/common/values/BUILD.bazel

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,29 @@ java_library(
157157
"@maven//:com_google_protobuf_protobuf_java",
158158
],
159159
)
160+
161+
java_library(
162+
name = "proto_message_lite_value",
163+
srcs = [
164+
"ProtoLiteCelValueConverter.java",
165+
"ProtoMessageLiteValue.java",
166+
],
167+
tags = [
168+
],
169+
deps = [
170+
":base_proto_cel_value_converter",
171+
":cel_value",
172+
":values",
173+
"//:auto_value",
174+
"//common/annotations",
175+
"//common/internal:cel_lite_descriptor_pool",
176+
"//common/internal:well_known_proto",
177+
"//common/types",
178+
"//common/types:type_providers",
179+
"//protobuf:cel_lite_descriptor",
180+
"@maven//:com_google_errorprone_error_prone_annotations",
181+
"@maven//:com_google_guava_guava",
182+
"@maven//:org_jspecify_jspecify",
183+
"@maven_android//:com_google_protobuf_protobuf_javalite",
184+
],
185+
)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.common.values;
16+
17+
import static com.google.common.base.Preconditions.checkNotNull;
18+
19+
import com.google.errorprone.annotations.Immutable;
20+
import com.google.protobuf.MessageLite;
21+
import dev.cel.common.annotations.Internal;
22+
import dev.cel.common.internal.CelLiteDescriptorPool;
23+
import dev.cel.common.internal.WellKnownProto;
24+
import dev.cel.protobuf.CelLiteDescriptor.MessageLiteDescriptor;
25+
26+
/**
27+
* {@code ProtoLiteCelValueConverter} handles bidirectional conversion between native Java and
28+
* protobuf objects to {@link CelValue}. This converter is specifically designed for use with
29+
* lite-variants of protobuf messages.
30+
*
31+
* <p>Protobuf semantics take precedence for conversion. For example, CEL's TimestampValue will be
32+
* converted into Protobuf's Timestamp instead of java.time.Instant.
33+
*
34+
* <p>CEL Library Internals. Do Not Use.
35+
*/
36+
@Immutable
37+
@Internal
38+
public final class ProtoLiteCelValueConverter extends BaseProtoCelValueConverter {
39+
private final CelLiteDescriptorPool descriptorPool;
40+
41+
public static ProtoLiteCelValueConverter newInstance(
42+
CelLiteDescriptorPool celLiteDescriptorPool) {
43+
return new ProtoLiteCelValueConverter(celLiteDescriptorPool);
44+
}
45+
46+
@Override
47+
public CelValue fromProtoMessageToCelValue(String protoTypeName, MessageLite msg) {
48+
checkNotNull(msg);
49+
checkNotNull(protoTypeName);
50+
51+
MessageLiteDescriptor descriptor = descriptorPool.getDescriptorOrThrow(protoTypeName);
52+
WellKnownProto wellKnownProto =
53+
WellKnownProto.getByTypeName(descriptor.getProtoTypeName()).orElse(null);
54+
55+
if (wellKnownProto == null) {
56+
return ProtoMessageLiteValue.create(msg, descriptor.getProtoTypeName(), this);
57+
}
58+
59+
return super.fromWellKnownProtoToCelValue(msg, wellKnownProto);
60+
}
61+
62+
private ProtoLiteCelValueConverter(CelLiteDescriptorPool celLiteDescriptorPool) {
63+
this.descriptorPool = celLiteDescriptorPool;
64+
}
65+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.common.values;
16+
17+
import com.google.auto.value.AutoValue;
18+
import com.google.common.base.Preconditions;
19+
import com.google.errorprone.annotations.Immutable;
20+
import com.google.protobuf.MessageLite;
21+
import dev.cel.common.types.CelType;
22+
import dev.cel.common.types.StructTypeReference;
23+
import java.util.Optional;
24+
25+
/**
26+
* ProtoMessageLiteValue is a struct value with protobuf support for {@link MessageLite}.
27+
* Specifically, it does not rely on full message descriptors, thus field selections can be
28+
* performed without the reliance of proto-reflection.
29+
*
30+
* <p>If the codebase has access to full protobuf messages with descriptors, use {@code
31+
* ProtoMessageValue} instead.
32+
*/
33+
@AutoValue
34+
@Immutable
35+
public abstract class ProtoMessageLiteValue extends StructValue<StringValue> {
36+
37+
@Override
38+
public abstract MessageLite value();
39+
40+
@Override
41+
public abstract CelType celType();
42+
43+
abstract ProtoLiteCelValueConverter protoLiteCelValueConverter();
44+
45+
@Override
46+
public boolean isZeroValue() {
47+
return value().getDefaultInstanceForType().equals(value());
48+
}
49+
50+
@Override
51+
public CelValue select(StringValue field) {
52+
throw new UnsupportedOperationException("Not implemented yet");
53+
}
54+
55+
@Override
56+
public Optional<CelValue> find(StringValue field) {
57+
throw new UnsupportedOperationException("Not implemented yet");
58+
}
59+
60+
public static ProtoMessageLiteValue create(
61+
MessageLite value, String typeName, ProtoLiteCelValueConverter protoLiteCelValueConverter) {
62+
Preconditions.checkNotNull(value);
63+
Preconditions.checkNotNull(typeName);
64+
return new AutoValue_ProtoMessageLiteValue(
65+
value, StructTypeReference.create(typeName), protoLiteCelValueConverter);
66+
}
67+
}

common/src/test/java/dev/cel/common/values/BUILD.bazel

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,24 @@ java_library(
1515
"//common:options",
1616
"//common:runtime_exception",
1717
"//common/internal:cel_descriptor_pools",
18+
"//common/internal:cel_lite_descriptor_pool",
19+
"//common/internal:default_lite_descriptor_pool",
1820
"//common/internal:default_message_factory",
1921
"//common/internal:dynamic_proto",
2022
"//common/internal:proto_message_factory",
23+
"//common/internal:well_known_proto",
2124
"//common/types",
2225
"//common/types:type_providers",
2326
"//common/values",
2427
"//common/values:cel_byte_string",
2528
"//common/values:cel_value",
2629
"//common/values:cel_value_provider",
30+
"//common/values:proto_message_lite_value",
2731
"//common/values:proto_message_value",
2832
"//common/values:proto_message_value_provider",
33+
"//testing:test_all_types_cel_java_proto3",
2934
"@cel_spec//proto/cel/expr/conformance/proto2:test_all_types_java_proto",
35+
"@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto",
3036
"@maven//:com_google_guava_guava",
3137
"@maven//:com_google_guava_guava_testlib",
3238
"@maven//:com_google_protobuf_protobuf_java",
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.common.values;
16+
17+
import static com.google.common.truth.Truth.assertThat;
18+
import static java.nio.charset.StandardCharsets.UTF_8;
19+
20+
import com.google.common.collect.ImmutableSet;
21+
import com.google.protobuf.ByteString;
22+
import com.google.protobuf.Duration;
23+
import com.google.protobuf.FloatValue;
24+
import com.google.protobuf.Int32Value;
25+
import com.google.protobuf.Int64Value;
26+
import com.google.protobuf.MessageLite;
27+
import com.google.protobuf.Timestamp;
28+
import com.google.protobuf.UInt32Value;
29+
import com.google.protobuf.UInt64Value;
30+
import com.google.testing.junit.testparameterinjector.TestParameter;
31+
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
32+
import dev.cel.common.internal.CelLiteDescriptorPool;
33+
import dev.cel.common.internal.DefaultLiteDescriptorPool;
34+
import dev.cel.common.internal.WellKnownProto;
35+
import dev.cel.expr.conformance.proto3.TestAllTypes;
36+
import dev.cel.expr.conformance.proto3.TestAllTypesCelDescriptor;
37+
import java.time.Instant;
38+
import org.junit.Test;
39+
import org.junit.runner.RunWith;
40+
41+
@RunWith(TestParameterInjector.class)
42+
public class ProtoLiteCelValueConverterTest {
43+
private static final CelLiteDescriptorPool DESCRIPTOR_POOL =
44+
DefaultLiteDescriptorPool.newInstance(
45+
ImmutableSet.of(TestAllTypesCelDescriptor.getDescriptor()));
46+
47+
private static final ProtoLiteCelValueConverter PROTO_LITE_CEL_VALUE_CONVERTER =
48+
ProtoLiteCelValueConverter.newInstance(DESCRIPTOR_POOL);
49+
50+
@Test
51+
public void fromProtoMessageToCelValue_withTestMessage_convertsToProtoMessageLiteValue() {
52+
ProtoMessageLiteValue protoMessageLiteValue =
53+
(ProtoMessageLiteValue)
54+
PROTO_LITE_CEL_VALUE_CONVERTER.fromProtoMessageToCelValue(
55+
"cel.expr.conformance.proto3.TestAllTypes", TestAllTypes.getDefaultInstance());
56+
57+
assertThat(protoMessageLiteValue.value()).isEqualTo(TestAllTypes.getDefaultInstance());
58+
}
59+
60+
private enum WellKnownProtoTestCase {
61+
BOOL(WellKnownProto.BOOL_VALUE, com.google.protobuf.BoolValue.of(true), BoolValue.create(true)),
62+
BYTES(
63+
WellKnownProto.BYTES_VALUE,
64+
com.google.protobuf.BytesValue.of(ByteString.copyFromUtf8("test")),
65+
BytesValue.create(CelByteString.of("test".getBytes(UTF_8)))),
66+
FLOAT(WellKnownProto.FLOAT_VALUE, FloatValue.of(1.0f), DoubleValue.create(1.0f)),
67+
DOUBLE(
68+
WellKnownProto.DOUBLE_VALUE,
69+
com.google.protobuf.DoubleValue.of(1.0),
70+
DoubleValue.create(1.0)),
71+
INT32(WellKnownProto.INT32_VALUE, Int32Value.of(1), IntValue.create(1)),
72+
INT64(WellKnownProto.INT64_VALUE, Int64Value.of(1L), IntValue.create(1L)),
73+
STRING(
74+
WellKnownProto.STRING_VALUE,
75+
com.google.protobuf.StringValue.of("test"),
76+
StringValue.create("test")),
77+
78+
DURATION(
79+
WellKnownProto.DURATION,
80+
Duration.newBuilder().setSeconds(10).setNanos(50).build(),
81+
DurationValue.create(java.time.Duration.ofSeconds(10, 50))),
82+
TIMESTAMP(
83+
WellKnownProto.TIMESTAMP,
84+
Timestamp.newBuilder().setSeconds(1678886400L).setNanos(123000000).build(),
85+
TimestampValue.create(Instant.ofEpochSecond(1678886400L, 123000000))),
86+
UINT32(WellKnownProto.UINT32_VALUE, UInt32Value.of(1), UintValue.create(1)),
87+
UINT64(WellKnownProto.UINT64_VALUE, UInt64Value.of(1L), UintValue.create(1L)),
88+
;
89+
90+
private final WellKnownProto wellKnownProto;
91+
private final MessageLite msg;
92+
private final CelValue celValue;
93+
94+
WellKnownProtoTestCase(WellKnownProto wellKnownProto, MessageLite msg, CelValue celValue) {
95+
this.wellKnownProto = wellKnownProto;
96+
this.msg = msg;
97+
this.celValue = celValue;
98+
}
99+
}
100+
101+
@Test
102+
public void fromProtoMessageToCelValue_withWellKnownProto_convertsToEquivalentCelValue(
103+
@TestParameter WellKnownProtoTestCase testCase) {
104+
CelValue convertedCelValue =
105+
PROTO_LITE_CEL_VALUE_CONVERTER.fromProtoMessageToCelValue(
106+
testCase.wellKnownProto.typeName(), testCase.msg);
107+
108+
assertThat(convertedCelValue).isEqualTo(testCase.celValue);
109+
}
110+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.common.values;
16+
17+
import static com.google.common.truth.Truth.assertThat;
18+
19+
import com.google.common.collect.ImmutableSet;
20+
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
21+
import dev.cel.common.internal.CelLiteDescriptorPool;
22+
import dev.cel.common.internal.DefaultLiteDescriptorPool;
23+
import dev.cel.expr.conformance.proto3.TestAllTypes;
24+
import dev.cel.expr.conformance.proto3.TestAllTypesCelDescriptor;
25+
import org.junit.Test;
26+
import org.junit.runner.RunWith;
27+
28+
@RunWith(TestParameterInjector.class)
29+
public class ProtoMessageLiteValueTest {
30+
private static final CelLiteDescriptorPool DESCRIPTOR_POOL =
31+
DefaultLiteDescriptorPool.newInstance(
32+
ImmutableSet.of(TestAllTypesCelDescriptor.getDescriptor()));
33+
34+
private static final ProtoLiteCelValueConverter PROTO_LITE_CEL_VALUE_CONVERTER =
35+
ProtoLiteCelValueConverter.newInstance(DESCRIPTOR_POOL);
36+
37+
@Test
38+
public void create_withEmptyMessage() {
39+
ProtoMessageLiteValue messageLiteValue =
40+
ProtoMessageLiteValue.create(
41+
TestAllTypes.getDefaultInstance(),
42+
"cel.expr.conformance.proto3.TestAllTypes",
43+
PROTO_LITE_CEL_VALUE_CONVERTER);
44+
45+
assertThat(messageLiteValue.value()).isEqualTo(TestAllTypes.getDefaultInstance());
46+
assertThat(messageLiteValue.isZeroValue()).isTrue();
47+
}
48+
49+
@Test
50+
public void create_withPopulatedMessage() {
51+
ProtoMessageLiteValue messageLiteValue =
52+
ProtoMessageLiteValue.create(
53+
TestAllTypes.newBuilder().setSingleInt64(1L).build(),
54+
"cel.expr.conformance.proto3.TestAllTypes",
55+
PROTO_LITE_CEL_VALUE_CONVERTER);
56+
57+
assertThat(messageLiteValue.value())
58+
.isEqualTo(TestAllTypes.newBuilder().setSingleInt64(1L).build());
59+
assertThat(messageLiteValue.isZeroValue()).isFalse();
60+
}
61+
}

common/values/BUILD.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,8 @@ java_library(
4040
name = "proto_message_value",
4141
exports = ["//common/src/main/java/dev/cel/common/values:proto_message_value"],
4242
)
43+
44+
java_library(
45+
name = "proto_message_lite_value",
46+
exports = ["//common/src/main/java/dev/cel/common/values:proto_message_lite_value"],
47+
)

0 commit comments

Comments
 (0)