cel_cxx/values/traits.rs
1use super::*;
2use crate::IntoError;
3use std::hash::Hash;
4
5/// Trait for types that have a known CEL type.
6///
7/// This trait provides type information for values that can be used in CEL expressions.
8/// It is used in conjunction with [`IntoValue`] and [`FromValue`] to provide complete type
9/// information for value conversions.
10///
11/// # Implementation
12///
13/// **Important**: This trait should generally not be implemented manually. For most use cases:
14/// - **Built-in types**: Already have implementations (primitives, collections, etc.)
15/// - **Custom opaque types**: Use the `#[derive(Opaque)]` macro which automatically implements this trait
16/// - **Special cases**: Only implement manually if you have very specific requirements
17///
18/// # Examples
19///
20/// ## Using Built-in Types
21///
22/// ```rust
23/// use cel_cxx::{TypedValue, ValueType};
24///
25/// // Built-in types already implement TypedValue
26/// assert_eq!(i64::value_type(), ValueType::Int);
27/// assert_eq!(String::value_type(), ValueType::String);
28/// assert_eq!(bool::value_type(), ValueType::Bool);
29/// ```
30///
31/// ## Using Derive Macro for Custom Types
32///
33/// ```rust,no_run
34/// use cel_cxx::Opaque;
35///
36/// #[derive(Opaque, Debug, Clone, PartialEq)]
37/// #[cel_cxx(display)]
38/// struct CustomId(u64);
39///
40/// // TypedValue is automatically implemented by the derive macro
41/// ```
42pub trait TypedValue {
43 /// Returns the CEL type for this value type.
44 ///
45 /// This method provides the [`ValueType`] that corresponds to this Rust type
46 /// in the CEL type system. It is used for type checking, error messages,
47 /// and runtime type validation.
48 ///
49 /// # Returns
50 ///
51 /// The [`ValueType`] that represents this type in CEL expressions.
52 fn value_type() -> ValueType;
53}
54
55impl<T: TypedValue + ?Sized> TypedValue for &T {
56 fn value_type() -> ValueType {
57 T::value_type()
58 }
59}
60
61impl<T: TypedValue + ?Sized> TypedValue for &mut T {
62 fn value_type() -> ValueType {
63 T::value_type()
64 }
65}
66
67/// Trait for converting values into CEL values.
68///
69/// This trait enables automatic conversion from Rust types to CEL [`Value`] instances.
70/// It is typically used alongside [`TypedValue`] to provide complete value conversion
71/// functionality.
72///
73/// # Implementation
74///
75/// **Important**: This trait should generally not be implemented manually. For most use cases:
76/// - **Built-in types**: Already have implementations
77/// - **Custom opaque types**: Use the `#[derive(Opaque)]` macro
78/// - **Collection types**: Automatically implemented for compatible element types
79///
80/// # Examples
81///
82/// ## Using Built-in Conversions
83///
84/// ```rust
85/// use cel_cxx::{IntoValue, Value};
86///
87/// // Built-in types can be converted directly
88/// let string_val = "hello".into_value();
89/// let int_val = 42i64.into_value();
90/// let bool_val = true.into_value();
91///
92/// assert_eq!(string_val, Value::String("hello".to_string().into()));
93/// assert_eq!(int_val, Value::Int(42));
94/// assert_eq!(bool_val, Value::Bool(true));
95/// ```
96///
97/// ## Collection Conversions
98///
99/// ```rust
100/// use cel_cxx::{IntoValue, Value};
101///
102/// let list = vec![1i64, 2i64, 3i64].into_value();
103/// let map = std::collections::HashMap::from([
104/// ("key".to_string(), "value".to_string())
105/// ]).into_value();
106/// ```
107pub trait IntoValue: Sized {
108 /// Converts this value into a CEL [`Value`].
109 ///
110 /// This method performs the conversion from a Rust type to its corresponding
111 /// CEL value representation. The conversion should be lossless where possible.
112 ///
113 /// # Returns
114 ///
115 /// A [`Value`] that represents this Rust value in the CEL type system.
116 fn into_value(self) -> Value;
117}
118
119impl<T: IntoValue + Clone> IntoValue for &T {
120 fn into_value(self) -> Value {
121 T::into_value(Clone::clone(self))
122 }
123}
124
125impl<T: IntoValue + Clone> IntoValue for &mut T {
126 fn into_value(self) -> Value {
127 T::into_value(Clone::clone(self))
128 }
129}
130
131/// Trait for converting CEL values into Rust types.
132///
133/// This trait enables conversion from CEL [`Value`] instances back to Rust types,
134/// with support for both owned and borrowed data through Generic Associated Types (GATs).
135/// It is the inverse operation of [`IntoValue`].
136///
137/// # Generic Associated Types (GATs)
138///
139/// The `Output<'a>` associated type allows the trait to return either owned or borrowed
140/// data depending on the source value:
141/// - For `String`: `Output<'a> = String` (always owned)
142/// - For `&str`: `Output<'a> = &'a str` (borrowed from the source Value)
143/// - For primitives: `Output<'a> = Self` (copied)
144///
145/// # Implementation
146///
147/// **Important**: This trait should generally not be implemented manually. For most use cases:
148/// - **Built-in types**: Already have implementations with proper lifetime handling
149/// - **Custom opaque types**: Use the `#[derive(Opaque)]` macro
150/// - **Manual implementation**: Only for very specific requirements and advanced use cases
151///
152/// # Examples
153///
154/// ## Using Built-in Conversions
155///
156/// ```rust
157/// use cel_cxx::{FromValue, Value};
158///
159/// let cel_string = Value::String("hello".to_string().into());
160/// let rust_string = String::from_value(&cel_string)?;
161/// assert_eq!(rust_string, "hello");
162///
163/// let cel_int = Value::Int(42);
164/// let rust_int = i64::from_value(&cel_int)?;
165/// assert_eq!(rust_int, 42);
166/// # Ok::<(), cel_cxx::Error>(())
167/// ```
168///
169/// ## Zero-Copy String Conversion
170///
171/// ```rust
172/// use cel_cxx::{FromValue, Value};
173///
174/// let cel_string = Value::String("hello world".to_string().into());
175///
176/// // Convert to borrowed string slice (zero-copy)
177/// let borrowed_str = <&str>::from_value(&cel_string)?;
178/// assert_eq!(borrowed_str, "hello world");
179/// # Ok::<(), cel_cxx::Error>(())
180/// ```
181pub trait FromValue: Sized {
182 /// The output type for the `from_value` method.
183 ///
184 /// This type is parameterized by the lifetime of the value being converted.
185 /// The lifetime is used to indicate whether the value is borrowed or owned.
186 type Output<'a>;
187
188 /// Attempts to convert a CEL [`Value`] into this type.
189 ///
190 /// This method performs type checking and conversion from a CEL value to the
191 /// target Rust type. It returns an error if the conversion is not possible.
192 ///
193 /// # Arguments
194 ///
195 /// * `value` - The CEL value to convert
196 ///
197 /// # Returns
198 ///
199 /// A [`Result`] containing either the converted value or a [`FromValueError`]
200 /// if the conversion failed.
201 ///
202 /// # Errors
203 ///
204 /// Returns [`FromValueError`] if:
205 /// - The value type doesn't match the expected type
206 /// - The value format is invalid for the target type
207 /// - Type constraints are not satisfied
208 fn from_value<'a>(value: &'a Value) -> Result<Self::Output<'a>, FromValueError>;
209}
210
211/// Error type for failed value conversions.
212///
213/// This error is returned when attempting to convert a CEL [`Value`] to a Rust type
214/// using the [`FromValue`] trait, but the conversion fails due to type mismatch.
215///
216/// # Error Information
217///
218/// The error contains:
219/// - **Source value**: The original CEL value that could not be converted
220/// - **Target type**: String description of the intended conversion target
221/// - **Context**: Additional information about why the conversion failed
222///
223/// # Common Causes
224///
225/// - **Type mismatch**: Trying to convert `Value::String` to `i64`
226/// - **Invalid format**: Trying to convert malformed data
227/// - **Constraint violation**: Value doesn't meet type constraints
228///
229/// # Examples
230///
231/// ```rust
232/// use cel_cxx::{Value, FromValue, FromValueError};
233///
234/// let string_val = Value::String("not_a_number".to_string().into());
235/// let result = i64::from_value(&string_val);
236///
237/// match result {
238/// Ok(num) => println!("Converted: {}", num),
239/// Err(error) => {
240/// println!("Cannot convert value: {}", error);
241/// }
242/// }
243/// ```
244#[derive(thiserror::Error, Debug, Clone)]
245pub struct FromValueError {
246 value: Value,
247 to_type: String,
248}
249
250impl FromValueError {
251 /// Create a new conversion error with a custom target type description.
252 ///
253 /// # Arguments
254 ///
255 /// * `value` - The CEL value that could not be converted
256 /// * `to` - Description of the target type (will be converted to string)
257 ///
258 /// # Examples
259 ///
260 /// ```rust
261 /// use cel_cxx::{Value, FromValueError};
262 ///
263 /// let error = FromValueError::new(Value::String("text".to_string().into()), "integer");
264 /// // Error created successfully with custom type description
265 /// ```
266 pub fn new(value: Value, to: impl ToString) -> Self {
267 Self {
268 value,
269 to_type: to.to_string(),
270 }
271 }
272
273 /// Create a new conversion error with automatic type name detection.
274 ///
275 /// This method uses the [`TypedValue`] trait to automatically determine
276 /// the CEL type information of the target type, providing more consistent error messages.
277 ///
278 /// # Type Parameters
279 ///
280 /// * `T` - The target type that implements [`TypedValue`]
281 ///
282 /// # Arguments
283 ///
284 /// * `value` - The CEL value that could not be converted
285 ///
286 /// # Examples
287 ///
288 /// ```rust
289 /// use cel_cxx::{Value, FromValueError};
290 ///
291 /// let error = FromValueError::new_typed::<i64>(Value::String("text".to_string().into()));
292 /// // error contains type information for the target type
293 /// ```
294 pub fn new_typed<T: TypedValue>(value: Value) -> Self {
295 Self::new(value, T::value_type())
296 }
297}
298
299impl std::fmt::Display for FromValueError {
300 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
301 write!(
302 f,
303 "cannot convert value {} to type {}",
304 self.value, self.to_type
305 )
306 }
307}
308
309impl From<FromValueError> for Error {
310 fn from(error: FromValueError) -> Self {
311 Error::invalid_argument(format!(
312 "cannot convert value {} to type {}",
313 error.value, error.to_type
314 ))
315 }
316}
317
318/// Trait for types that can be used as map keys and have a known CEL map key type.
319///
320/// `TypedMapKey` provides static type information for map key types.
321/// This trait is used alongside [`IntoMapKey`] for complete map key functionality.
322///
323/// # Examples
324///
325/// ```rust,no_run
326/// use cel_cxx::{TypedMapKey, MapKeyType};
327///
328/// // Built-in types implement this automatically
329/// assert_eq!(String::mapkey_type(), MapKeyType::String);
330/// assert_eq!(i64::mapkey_type(), MapKeyType::Int);
331/// ```
332pub trait TypedMapKey: TypedValue {
333 /// Returns the CEL map key type for this type.
334 fn mapkey_type() -> MapKeyType {
335 MapKeyType::try_from(Self::value_type()).expect("invalid map key type")
336 }
337}
338
339impl<T: TypedMapKey + ?Sized> TypedMapKey for &T {
340 fn mapkey_type() -> MapKeyType {
341 T::mapkey_type()
342 }
343}
344
345impl<T: TypedMapKey + ?Sized> TypedMapKey for &mut T {
346 fn mapkey_type() -> MapKeyType {
347 T::mapkey_type()
348 }
349}
350
351/// Trait for converting values into CEL map keys.
352///
353/// `IntoMapKey` provides conversion from Rust types to CEL [`MapKey`] instances.
354/// Only types that are valid CEL map key types can implement this trait.
355///
356/// # Examples
357///
358/// ```rust,no_run
359/// use cel_cxx::{IntoMapKey, MapKey};
360///
361/// // Convert various types to map keys
362/// let string_key = "key".into_mapkey();
363/// let int_key = 42i64.into_mapkey();
364/// let bool_key = true.into_mapkey();
365///
366/// assert_eq!(string_key, MapKey::String("key".to_string().into()));
367/// assert_eq!(int_key, MapKey::Int(42));
368/// assert_eq!(bool_key, MapKey::Bool(true));
369/// ```
370pub trait IntoMapKey: IntoValue + Eq + Hash + Ord {
371 /// Converts this value into a CEL [`MapKey`].
372 fn into_mapkey(self) -> MapKey {
373 <Self as IntoValue>::into_value(self)
374 .try_into()
375 .expect("invalid map key type")
376 }
377}
378
379impl<T: IntoMapKey + Clone> IntoMapKey for &T {
380 fn into_mapkey(self) -> MapKey {
381 T::into_mapkey(Clone::clone(self))
382 }
383}
384
385impl<T: IntoMapKey + Clone> IntoMapKey for &mut T {
386 fn into_mapkey(self) -> MapKey {
387 T::into_mapkey(Clone::clone(self))
388 }
389}
390
391/// Trait for converting CEL map keys into Rust types.
392///
393/// `FromMapKey` provides conversion from CEL [`MapKey`] instances to Rust types.
394/// This is the inverse operation of [`IntoMapKey`].
395///
396/// # Examples
397///
398/// ```rust,no_run
399/// use cel_cxx::{FromMapKey, MapKey};
400///
401/// // Convert map keys back to Rust types
402/// let cel_key = MapKey::String("hello".to_string().into());
403/// let rust_string = String::from_mapkey(&cel_key).unwrap();
404/// assert_eq!(rust_string, "hello");
405///
406/// let cel_key = MapKey::Int(42);
407/// let rust_int = i64::from_mapkey(&cel_key).unwrap();
408/// assert_eq!(rust_int, 42);
409/// ```
410pub trait FromMapKey: FromValue + Eq + Hash + Ord
411where
412 for<'a> Self::Output<'a>: Eq + Hash + Ord,
413{
414 /// Attempts to convert a CEL [`MapKey`] into this type.
415 ///
416 /// # Returns
417 ///
418 /// - `Ok(Self)`: Conversion successful
419 /// - `Err(MapKey)`: Conversion failed, returns original map key
420 fn from_mapkey<'a>(key: &'a MapKey) -> Result<Self::Output<'a>, FromMapKeyError>;
421}
422
423/// Error type for failed map key conversions.
424///
425/// This error is returned when attempting to convert a CEL [`MapKey`] to a Rust type
426/// using the [`FromMapKey`] trait, but the conversion fails due to type mismatch.
427///
428/// # Error Information
429///
430/// The error contains:
431/// - **Source key**: The original CEL map key that could not be converted
432/// - **Target type**: The intended map key type for the conversion
433///
434/// # Map Key Constraints
435///
436/// Only certain types can be used as CEL map keys:
437/// - `bool` - Boolean keys
438/// - `i64` - Signed integer keys
439/// - `u64` - Unsigned integer keys
440/// - `String` - String keys
441///
442/// # Common Causes
443///
444/// - **Type mismatch**: Trying to convert `MapKey::String` to `i64`
445/// - **Invalid key type**: Attempting conversion to unsupported key type
446/// - **Format issues**: Key value doesn't meet target type constraints
447///
448/// # Examples
449///
450/// ```rust
451/// use cel_cxx::{MapKey, FromMapKey, FromMapKeyError};
452///
453/// let string_key = MapKey::String("not_a_number".to_string().into());
454/// let result = i64::from_mapkey(&string_key);
455///
456/// match result {
457/// Ok(num) => println!("Converted key: {}", num),
458/// Err(error) => {
459/// println!("Cannot convert key: {}", error);
460/// }
461/// }
462/// ```
463#[derive(thiserror::Error, Debug, Clone)]
464pub struct FromMapKeyError {
465 key: MapKey,
466 to_type: MapKeyType,
467}
468
469impl FromMapKeyError {
470 /// Create a new map key conversion error.
471 ///
472 /// # Arguments
473 ///
474 /// * `key` - The CEL map key that could not be converted
475 /// * `to` - The target map key type for the conversion
476 ///
477 /// # Examples
478 ///
479 /// ```rust
480 /// use cel_cxx::{MapKey, MapKeyType, FromMapKeyError};
481 ///
482 /// let error = FromMapKeyError::new(
483 /// MapKey::String("text".to_string().into()),
484 /// MapKeyType::Int
485 /// );
486 /// // Error created successfully
487 /// ```
488 pub fn new(key: MapKey, to: MapKeyType) -> Self {
489 Self { key, to_type: to }
490 }
491
492 /// Create a new map key conversion error with automatic type detection.
493 ///
494 /// This method uses the [`TypedMapKey`] trait to automatically determine
495 /// the CEL type information of the target type, providing more consistent error messages.
496 ///
497 /// # Type Parameters
498 ///
499 /// * `T` - The target map key type that implements [`TypedMapKey`]
500 ///
501 /// # Arguments
502 ///
503 /// * `key` - The CEL map key that could not be converted
504 ///
505 /// # Examples
506 ///
507 /// ```rust
508 /// use cel_cxx::{MapKey, FromMapKeyError};
509 ///
510 /// let error = FromMapKeyError::new_typed::<i64>(MapKey::String("text".to_string().into()));
511 /// // error contains type information for the target type
512 /// ```
513 pub fn new_typed<T: TypedMapKey>(key: MapKey) -> Self {
514 Self::new(key, T::mapkey_type())
515 }
516}
517
518impl std::fmt::Display for FromMapKeyError {
519 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
520 write!(
521 f,
522 "cannot convert map key {} to type {}",
523 self.key, self.to_type
524 )
525 }
526}
527
528impl From<FromMapKeyError> for FromValueError {
529 fn from(error: FromMapKeyError) -> Self {
530 FromValueError::new(error.key.into(), error.to_type)
531 }
532}
533
534impl From<FromMapKeyError> for Error {
535 fn from(error: FromMapKeyError) -> Self {
536 Error::invalid_argument(format!(
537 "cannot convert map key {} to type {}",
538 error.key, error.to_type
539 ))
540 }
541}
542
543/// Trait for types that can be converted into compile-time CEL constants.
544///
545/// This trait extends [`IntoValue`] and [`TypedValue`] to provide conversion
546/// to [`Constant`] values, which can be used for compile-time optimization
547/// and type inference in CEL expressions.
548///
549/// # Compile-Time vs Runtime Values
550///
551/// - **Constants**: Known at compile-time, can be folded and optimized
552/// - **Values**: Computed at runtime, require full evaluation
553///
554/// Constants enable the CEL compiler to:
555/// - Perform constant folding optimizations
556/// - Detect type errors earlier
557/// - Generate more efficient evaluation code
558/// - Support constant expression evaluation
559///
560/// # Automatic Implementation
561///
562/// This trait is automatically implemented for all types that implement
563/// both [`IntoValue`] and [`TypedValue`], providing a default conversion
564/// from value to constant.
565///
566/// # Examples
567///
568/// ## Basic Constant Creation
569///
570/// ```rust
571/// use cel_cxx::{IntoConstant, Constant};
572///
573/// // Primitive constants
574/// let null_const = ().into_constant(); // Constant::Null
575/// let bool_const = true.into_constant(); // Constant::Bool(true)
576/// let int_const = 42i64.into_constant(); // Constant::Int(42)
577/// let string_const = "hello".into_constant(); // Constant::String("hello".to_string())
578/// ```
579///
580/// ## Usage in Environment Building
581///
582/// ```rust,no_run
583/// use cel_cxx::{Env, IntoConstant};
584///
585/// let env = Env::builder()
586/// .define_constant("PI", 3.14159)?
587/// .define_constant("APP_NAME", "MyApp")?
588/// .define_constant("MAX_RETRIES", 5i64)?
589/// .build()?;
590///
591/// // These constants can be used in expressions:
592/// // "PI * radius * radius"
593/// // "APP_NAME + ' v1.0'"
594/// // "retries < MAX_RETRIES"
595/// # Ok::<(), cel_cxx::Error>(())
596/// ```
597pub trait IntoConstant: IntoValue + TypedValue {
598 /// Convert this value into a compile-time CEL constant.
599 ///
600 /// This method creates a [`Constant`] from the value, which can be used
601 /// for compile-time optimizations and constant expression evaluation.
602 ///
603 /// # Returns
604 ///
605 /// A [`Constant`] representing this value that can be used at compile-time.
606 ///
607 /// # Implementation Note
608 ///
609 /// The default implementation converts the value using [`IntoValue::into_value`]
610 /// and then wraps it as a constant. Custom implementations can provide
611 /// more efficient constant creation if needed.
612 ///
613 /// # Examples
614 ///
615 /// ```rust
616 /// use cel_cxx::{IntoConstant, Constant};
617 ///
618 /// let const_val = 42i64.into_constant();
619 /// let const_str = "hello".into_constant();
620 /// // Constants created successfully
621 /// ```
622 fn into_constant(self) -> Constant {
623 <Self as IntoValue>::into_value(self)
624 .try_into()
625 .expect("invalid constant type")
626 }
627}
628
629impl<T: IntoConstant + Clone> IntoConstant for &T {
630 fn into_constant(self) -> Constant {
631 T::into_constant(Clone::clone(self))
632 }
633}
634
635impl<T: IntoConstant + Clone> IntoConstant for &mut T {
636 fn into_constant(self) -> Constant {
637 T::into_constant(Clone::clone(self))
638 }
639}
640
641/// Trait for converting return values to CEL results.
642///
643/// This trait provides automatic conversion for function return types,
644/// supporting both direct values and `Result` types for error handling.
645///
646/// # Note
647///
648/// This trait is sealed and cannot be implemented outside this crate.
649///
650/// # Implementations
651///
652/// - `T` where `T: IntoValue + TypedValue` - Direct value conversion
653/// - `Result<T, E>` where `T: IntoValue + TypedValue, E: IntoError` - Error handling
654pub trait IntoResult: private::Sealed {
655 /// Convert the value into a CEL result.
656 fn into_result(self) -> Result<Value, Error>;
657
658 /// Get the CEL type of the resulting value.
659 fn value_type() -> ValueType;
660}
661
662impl<T: IntoValue + TypedValue, E: IntoError> IntoResult for Result<T, E> {
663 fn into_result(self) -> Result<Value, Error> {
664 self.map(|v| v.into_value()).map_err(|e| e.into_error())
665 }
666
667 fn value_type() -> ValueType {
668 <T as TypedValue>::value_type()
669 }
670}
671
672impl<T: IntoValue + TypedValue> IntoResult for T {
673 fn into_result(self) -> Result<Value, Error> {
674 Ok(self.into_value())
675 }
676
677 fn value_type() -> ValueType {
678 <T as TypedValue>::value_type()
679 }
680}
681
682// Sealed implementations for IntoResult
683impl<T: IntoValue + TypedValue, E: IntoError> private::Sealed for Result<T, E> {}
684impl<T: IntoValue + TypedValue> private::Sealed for T {}
685
686mod private {
687 pub trait Sealed {}
688}