1414
1515package dev .cel .common .values ;
1616
17+ import static com .google .common .base .Preconditions .checkNotNull ;
18+
1719import com .google .auto .value .AutoValue ;
1820import com .google .auto .value .extension .memoized .Memoized ;
19- import com .google .common .base .Preconditions ;
2021import com .google .common .collect .ImmutableListMultimap ;
2122import com .google .common .collect .ImmutableMap ;
2223import com .google .errorprone .annotations .Immutable ;
2324import com .google .protobuf .MessageLite ;
24- import dev .cel .common .annotations .Internal ;
2525import dev .cel .common .types .CelType ;
2626import dev .cel .common .types .StructTypeReference ;
2727import dev .cel .common .values .ProtoLiteCelValueConverter .MessageFields ;
28+ import dev .cel .protobuf .CelLiteDescriptor .FieldLiteDescriptor ;
2829import java .io .IOException ;
2930import java .util .Optional ;
31+ import org .jspecify .annotations .Nullable ;
3032
3133/**
3234 * ProtoMessageLiteValue is a struct value with protobuf support for {@link MessageLite}.
3537 *
3638 * <p>If the codebase has access to full protobuf messages with descriptors, use {@code
3739 * ProtoMessageValue} instead.
40+ *
41+ * <p>Implements {@link OptimizedSelectable} so that select chains can address fields by number:
42+ *
43+ * <ul>
44+ * <li><b>Field renames:</b> If a protobuf field is renamed in schema after an AST was compiled,
45+ * resolving by {@link SelectField#fieldNumber()} maps the number to the runtime descriptor's
46+ * current field name, preventing {@code CelAttributeNotFoundException}.
47+ * <li><b>Version skew / unknown fields:</b> When evaluating payloads serialized by a newer binary
48+ * containing fields absent from the local {@code CelLiteDescriptor}, the unknown wire bytes
49+ * are preserved in {@link #unknownFields()} and decoded on demand using the compile-time wire
50+ * type and default metadata in {@link SelectField}.
51+ * </ul>
3852 */
3953@ AutoValue
4054@ Immutable
41- public abstract class ProtoMessageLiteValue extends StructValue <String , MessageLite > {
55+ public abstract class ProtoMessageLiteValue extends StructValue <String , MessageLite >
56+ implements OptimizedSelectable {
4257
4358 @ Override
4459 public abstract MessageLite value ();
@@ -57,12 +72,11 @@ MessageFields messageFields() {
5772 }
5873 }
5974
60- @ Internal
61- public ImmutableMap <String , Object > fieldValues () {
75+ ImmutableMap <String , Object > fieldValues () {
6276 return messageFields ().values ();
6377 }
6478
65- public ImmutableListMultimap <Integer , Object > unknownFields () {
79+ ImmutableListMultimap <Integer , Object > unknownFields () {
6680 return messageFields ().unknowns ();
6781 }
6882
@@ -84,11 +98,59 @@ public Optional<Object> find(String field) {
8498 .map (value -> protoLiteCelValueConverter ().toRuntimeValue (fieldValue ));
8599 }
86100
101+ @ Override
102+ public Object selectByFieldNumber (SelectField field ) {
103+ FieldLiteDescriptor fd = findFieldDescriptor (field );
104+ Object known = findKnownFieldValue (fd );
105+ if (known != null ) {
106+ return protoLiteCelValueConverter ().toRuntimeValue (known );
107+ }
108+ return RawProtoMessageLiteValue .selectWireOrDefault (
109+ field , fd , unknownFields ().get (field .fieldNumber ()), protoLiteCelValueConverter ());
110+ }
111+
112+ @ Override
113+ public boolean hasFieldByNumber (SelectField field ) {
114+ FieldLiteDescriptor fd = findFieldDescriptor (field );
115+ if (findKnownFieldValue (fd ) != null ) {
116+ return true ;
117+ }
118+ return RawProtoMessageLiteValue .isPresentInWire (
119+ field , fd , unknownFields ().get (field .fieldNumber ()));
120+ }
121+
122+ @ Override
123+ public Optional <Object > findByFieldNumber (SelectField field ) {
124+ FieldLiteDescriptor fd = findFieldDescriptor (field );
125+ Object known = findKnownFieldValue (fd );
126+ if (known != null ) {
127+ return Optional .of (protoLiteCelValueConverter ().toRuntimeValue (known ));
128+ }
129+ return RawProtoMessageLiteValue .navigateWire (
130+ field , fd , unknownFields ().get (field .fieldNumber ()), protoLiteCelValueConverter ());
131+ }
132+
133+ private @ Nullable FieldLiteDescriptor findFieldDescriptor (SelectField field ) {
134+ return protoLiteCelValueConverter ()
135+ .findFieldDescriptor (celType ().name (), field .fieldNumber ())
136+ .orElse (null );
137+ }
138+
139+ private @ Nullable Object findKnownFieldValue (@ Nullable FieldLiteDescriptor fieldDescriptor ) {
140+ if (fieldDescriptor == null ) {
141+ return null ;
142+ }
143+ return fieldValues ().get (fieldDescriptor .getFieldName ());
144+ }
145+
87146 public static ProtoMessageLiteValue create (
88147 MessageLite value , String typeName , ProtoLiteCelValueConverter protoLiteCelValueConverter ) {
89- Preconditions .checkNotNull (value );
90- Preconditions .checkNotNull (typeName );
148+ checkNotNull (value );
149+ checkNotNull (typeName );
150+ checkNotNull (protoLiteCelValueConverter );
91151 return new AutoValue_ProtoMessageLiteValue (
92152 value , StructTypeReference .create (typeName ), protoLiteCelValueConverter );
93153 }
154+
155+ ProtoMessageLiteValue () {}
94156}
0 commit comments