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}