cel_core/eval/proto_registry.rs
1//! Trait abstractions for protobuf type registries.
2//!
3//! This module defines two focused traits for working with protobuf types:
4//!
5//! - `ProtoTypeResolver`: Provides type lookup capabilities used by the checker
6//! - `ProtoRegistry`: Extends ProtoTypeResolver with runtime operations for the evaluator
7//!
8//! These traits decouple cel-core from any specific protobuf implementation.
9
10use std::any::Any;
11use std::fmt;
12
13use super::message::MessageValue;
14use super::Value;
15use crate::types::{CelType, ResolvedProtoType};
16
17/// An evaluated struct field ready for message construction.
18#[derive(Debug, Clone)]
19pub struct StructFieldValue {
20 /// The field name.
21 pub name: String,
22 /// The evaluated value.
23 pub value: Value,
24 /// Whether this is an optional field entry.
25 pub optional: bool,
26}
27
28/// Trait for protobuf descriptor pool access (checker operations).
29///
30/// This trait provides type information needed during type checking,
31/// without requiring the ability to construct or manipulate proto messages.
32///
33/// # Object Safety
34///
35/// This trait is object-safe and can be used with `dyn ProtoTypeResolver`.
36///
37/// # Downcasting
38///
39/// The `as_any()` method enables downcasting to concrete types. Note that
40/// downcasting only works with the exact concrete type - wrapped or newtype
41/// implementations will not match.
42pub trait ProtoTypeResolver: fmt::Debug + Send + Sync {
43 /// Get the CEL type of a message field.
44 fn get_field_type(&self, message: &str, field: &str) -> Option<CelType>;
45
46 /// Check whether a message type is known to this registry.
47 fn has_message(&self, message: &str) -> bool;
48
49 /// Check whether `ext_name` is a known extension field on `message`.
50 fn is_extension(&self, message: &str, ext_name: &str) -> bool;
51
52 /// Get the numeric value of an enum constant by name.
53 fn get_enum_value(&self, enum_name: &str, value_name: &str) -> Option<i32>;
54
55 /// Resolve a qualified name (dot-separated parts) within a container namespace.
56 ///
57 /// Returns the resolved proto type (Message, Enum, or EnumValue).
58 fn resolve_qualified(&self, parts: &[&str], container: &str) -> Option<ResolvedProtoType>;
59
60 /// Resolve a message name within a container namespace using C++ namespace rules.
61 ///
62 /// Returns the fully qualified message name if found.
63 fn resolve_message_name(&self, name: &str, container: &str) -> Option<String>;
64
65 /// List all field names of a message type.
66 ///
67 /// Returns `None` if the message type is unknown to this registry.
68 /// Default implementation returns `None`.
69 fn message_field_names(&self, _message: &str) -> Option<Vec<String>> {
70 None
71 }
72
73 /// Downcast to a concrete type via `Any`.
74 fn as_any(&self) -> &dyn Any;
75}
76
77/// Trait for protobuf runtime operations (evaluator operations).
78///
79/// This trait extends `ProtoTypeResolver` with the ability to construct messages,
80/// access fields at runtime, and work with extensions. It is used by the evaluator.
81///
82/// # Object Safety
83///
84/// This trait is object-safe and can be used with `dyn ProtoRegistry`.
85pub trait ProtoRegistry: ProtoTypeResolver {
86 /// Construct a protobuf message from evaluated field values.
87 ///
88 /// Returns the constructed message as a Value (may be unwrapped to a
89 /// native CEL value for well-known types like Timestamp, Duration, etc.).
90 fn construct_message(
91 &self,
92 type_name: &str,
93 fields: &[StructFieldValue],
94 strong_enums: bool,
95 ) -> Value;
96
97 /// Access a field on a message value.
98 ///
99 /// Returns the field value, or an error/optional-none for missing fields.
100 fn message_field_access(
101 &self,
102 msg: &dyn MessageValue,
103 field: &str,
104 optional: bool,
105 strong_enums: bool,
106 ) -> Value;
107
108 /// Test whether a message has a field set (for `has()` macro).
109 fn message_has_field(&self, msg: &dyn MessageValue, field: &str) -> Value;
110
111 /// Get an extension field value from a message.
112 ///
113 /// Returns `None` if the extension is not applicable to this message type.
114 fn get_extension_value(
115 &self,
116 msg: &dyn MessageValue,
117 ext_name: &str,
118 optional: bool,
119 strong_enums: bool,
120 ) -> Option<Value>;
121
122 /// Test whether a message has an extension field set.
123 ///
124 /// Returns `None` if the extension is not applicable to this message type.
125 fn has_extension(&self, msg: &dyn MessageValue, ext_name: &str) -> Option<bool>;
126}