Skip to main content

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}