cel_cxx/values/opaque.rs
1//use std::{ops::Deref, sync::Arc};
2use super::*;
3
4/// Trait for opaque types that can be stored in CEL values.
5///
6/// This trait allows custom Rust types to be embedded in CEL values as opaque
7/// objects. Opaque types are useful for:
8/// - **Foreign data structures**: Types from other libraries
9/// - **Complex business objects**: Domain-specific data models
10/// - **Native performance**: Keep computations in native Rust
11/// - **Type safety**: Maintain strong typing across CEL boundaries
12///
13/// # Opaque Type Characteristics
14///
15/// Opaque values in CEL:
16/// - Cannot be directly manipulated by CEL expressions
17/// - Can be passed between functions that understand the type
18/// - Support method calls through registered member functions
19/// - Maintain their Rust type identity and behavior
20///
21/// # Required Traits
22///
23/// Implementing `Opaque` requires several standard traits:
24/// - [`Clone`]: For value duplication
25/// - [`Debug`]: For debugging and error messages
26/// - [`std::fmt::Display`]: For string representation
27/// - [`Send + Sync`]: For thread safety
28/// - [`dyn_clone::DynClone`]: For trait object cloning
29///
30/// # Examples
31///
32/// ## Basic Opaque Type
33///
34/// ```rust,no_run
35/// use cel_cxx::{Opaque, Value, IntoValue};
36///
37/// #[derive(Opaque, Debug, Clone, PartialEq)]
38/// #[cel_cxx(display)]
39/// struct UserId(u64);
40///
41/// // All necessary traits are automatically implemented by the derive macro
42///
43/// // Usage in CEL values
44/// let user_id = UserId(12345);
45/// let value = user_id.into_value();
46/// ```
47///
48/// ## Integration with Functions
49///
50/// ```rust,no_run
51/// use cel_cxx::*;
52///
53/// #[derive(Opaque, Debug, Clone, PartialEq)]
54/// #[cel_cxx(display)]
55/// struct BankAccount {
56/// balance: f64,
57/// account_number: String,
58/// }
59///
60/// impl BankAccount {
61/// fn balance(&self) -> f64 {
62/// self.balance
63/// }
64///
65/// fn can_withdraw(&self, amount: f64) -> bool {
66/// amount <= self.balance
67/// }
68///
69/// fn account_number(&self) -> &str {
70/// &self.account_number
71/// }
72/// }
73///
74/// // Register methods for the opaque type
75/// let env = Env::builder()
76/// .register_member_function("balance", BankAccount::balance)?
77/// .register_member_function("can_withdraw", BankAccount::can_withdraw)?
78/// .register_member_function("account_number", BankAccount::account_number)?
79/// .build()?;
80/// # Ok::<(), cel_cxx::Error>(())
81/// ```
82///
83/// # Type Erasure and Downcasting
84///
85/// Opaque values support safe downcasting:
86///
87/// ```rust,no_run
88/// use cel_cxx::{Value, Opaque, IntoValue};
89///
90/// #[derive(Opaque, Debug, Clone, PartialEq)]
91/// #[cel_cxx(display)]
92/// struct UserId(u64);
93///
94/// let user_id = UserId(12345);
95/// let value = user_id.into_value();
96///
97/// // Safe downcasting
98/// if let Value::Opaque(opaque) = &value {
99/// if opaque.is::<UserId>() {
100/// let user_id_ref = opaque.downcast_ref::<UserId>().unwrap();
101/// println!("User ID: {}", user_id_ref.0);
102/// }
103/// }
104/// ```
105pub trait Opaque:
106 dyn_clone::DynClone + std::fmt::Debug + std::fmt::Display + Send + Sync + private::Sealed
107{
108 /// Returns the opaque type information for this value.
109 ///
110 /// This method provides type metadata that can be used for type checking
111 /// and runtime type identification.
112 fn opaque_type(&self) -> OpaqueType;
113}
114dyn_clone::clone_trait_object!(Opaque);
115
116/// Trait for opaque types with additional type constraints.
117///
118/// This trait extends [`Opaque`] with additional requirements that enable
119/// more sophisticated operations on opaque values. It adds:
120/// - **Value equality**: Types can be compared for equality
121/// - **Cloning**: Efficient cloning without heap allocation
122/// - **Enhanced type safety**: Stronger compile-time guarantees
123///
124/// # When to Use TypedOpaque
125///
126/// Use `TypedOpaque` when your opaque type needs:
127/// - Equality comparisons in CEL expressions
128/// - Participation in hash-based collections
129/// - Advanced type checking and validation
130/// - Integration with CEL's type inference system
131///
132/// # Additional Requirements
133///
134/// Beyond [`Opaque`], this trait requires:
135/// - [`Clone`]: Direct cloning (not just through trait objects)
136/// - [`PartialEq`]: Value equality comparison
137/// - All the traits required by [`Opaque`]
138///
139/// # Examples
140///
141/// ## Comparable Opaque Type
142///
143/// ```rust,no_run
144/// use cel_cxx::Opaque;
145///
146/// #[derive(Opaque, Debug, Clone, PartialEq, Eq, Hash)]
147/// #[cel_cxx(display)]
148/// struct ProductId(String);
149///
150/// // All necessary traits are automatically implemented by the derive macro
151/// ```
152pub trait TypedOpaque:
153 Opaque + Clone + PartialEq + std::fmt::Debug + std::fmt::Display + Send + Sync
154{
155 /// Returns the static opaque type for this value type.
156 fn opaque_type() -> OpaqueType;
157}
158
159impl dyn Opaque {
160 /// Checks if this opaque value is of a specific type.
161 ///
162 /// # Type Parameters
163 ///
164 /// - `T`: The type to check for
165 ///
166 /// # Returns
167 ///
168 /// `true` if this value is of type `T`, `false` otherwise
169 pub fn is<T: Opaque>(&self) -> bool {
170 private::Sealed::as_any(self).is::<T>()
171 }
172
173 /// Attempts to downcast this boxed opaque value to a specific type.
174 ///
175 /// # Type Parameters
176 ///
177 /// - `T`: The target type to downcast to
178 ///
179 /// # Returns
180 ///
181 /// - `Ok(T)`: Downcast successful
182 /// - `Err(Box<Self>)`: Downcast failed, returns original boxed value
183 pub fn downcast<T: Opaque>(self: Box<Self>) -> Result<T, Box<Self>> {
184 if self.is::<T>() {
185 Ok(*private::Sealed::into_any(self).downcast::<T>().unwrap())
186 } else {
187 Err(self)
188 }
189 }
190
191 /// Returns a reference to the contained value if it's of the specified type.
192 pub fn downcast_ref<T: Opaque>(&self) -> Option<&T> {
193 private::Sealed::as_any(self).downcast_ref::<T>()
194 }
195
196 /// Returns a mutable reference to the contained value if it's of the specified type.
197 pub fn downcast_mut<T: Opaque>(&mut self) -> Option<&mut T> {
198 private::Sealed::as_any_mut(self).downcast_mut::<T>()
199 }
200}
201
202impl std::cmp::PartialEq for dyn Opaque {
203 fn eq(&self, other: &Self) -> bool {
204 self.dyn_eq(private::Sealed::as_any(other))
205 }
206}
207impl std::cmp::Eq for dyn Opaque {}
208
209impl<T: TypedOpaque> Opaque for T {
210 fn opaque_type(&self) -> OpaqueType {
211 <Self as TypedOpaque>::opaque_type()
212 }
213}
214
215mod private {
216 use super::Opaque;
217 use std::any::Any;
218
219 pub trait Sealed: Any {
220 fn into_any(self: Box<Self>) -> Box<dyn Any>;
221 fn as_any(&self) -> &dyn Any;
222 fn as_any_mut(&mut self) -> &mut dyn Any;
223
224 fn dyn_eq(&self, other: &dyn Any) -> bool;
225 }
226
227 impl<T: Opaque + PartialEq> Sealed for T {
228 fn into_any(self: Box<Self>) -> Box<dyn Any> {
229 self
230 }
231 fn as_any(&self) -> &dyn Any {
232 self
233 }
234 fn as_any_mut(&mut self) -> &mut dyn Any {
235 self
236 }
237
238 fn dyn_eq(&self, other: &dyn Any) -> bool {
239 other.downcast_ref::<T>() == Some(self)
240 }
241 }
242}