cel_cxx/
error.rs

1/// Error type for CEL operations.
2///
3/// This is the main error type used throughout the cel-cxx library. It represents
4/// an error condition in CEL expression compilation or evaluation, modeled after
5/// the `absl::Status` type from the Google Abseil library.
6///
7/// # Examples
8///
9/// ## Creating Errors
10///
11/// ```rust
12/// use cel_cxx::{Error, Code};
13///
14/// let error = Error::invalid_argument("Expression syntax is invalid");
15/// assert_eq!(error.code(), Code::InvalidArgument);
16/// ```
17///
18/// ## Handling Errors
19///
20/// ```rust,no_run
21/// use cel_cxx::*;
22///
23/// match Env::builder().build()?.compile("invalid expression!!") {
24///     Ok(program) => println!("Compiled successfully"),
25///     Err(e) => {
26///         eprintln!("Error: {}", e);
27///         eprintln!("Error code: {:?}", e.code());
28///     }
29/// }
30/// # Ok::<(), cel_cxx::Error>(())
31/// ```
32#[derive(Debug, Clone, PartialEq, Eq, Hash)]
33pub struct Error {
34    /// The error code.
35    code: Code,
36
37    /// The error message.
38    message: String,
39}
40
41/// Status codes for CEL errors.
42///
43/// These codes are based on the Google RPC status codes and provide
44/// standardized error categorization for different types of failures
45/// that can occur during CEL operations.
46///
47/// # Examples
48///
49/// ```rust
50/// use cel_cxx::Code;
51///
52/// let code = Code::InvalidArgument;
53/// println!("Description: {}", code.description());
54/// println!("Display: {}", code);
55/// ```
56#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
57pub enum Code {
58    /// The operation completed successfully.
59    ///
60    /// This is not typically used for errors, but may appear in some contexts.
61    Ok = 0,
62
63    /// The operation was cancelled.
64    ///
65    /// This typically indicates that the operation was cancelled by the caller
66    /// or due to a timeout.
67    Cancelled = 1,
68
69    /// Unknown error.
70    ///
71    /// This is used when the specific error category cannot be determined,
72    /// often when converting from other error types.
73    Unknown = 2,
74
75    /// Client specified an invalid argument.
76    ///
77    /// This indicates that the input provided to the operation was invalid.
78    /// For CEL, this often means syntax errors or invalid expression structure.
79    InvalidArgument = 3,
80
81    /// Deadline expired before operation could complete.
82    ///
83    /// This indicates that the operation took too long to complete.
84    DeadlineExceeded = 4,
85
86    /// Some requested entity was not found.
87    ///
88    /// This indicates that a referenced entity (like a variable or function)
89    /// was not found in the current context.
90    NotFound = 5,
91
92    /// Some entity that we attempted to create already exists.
93    ///
94    /// This indicates that an operation tried to create something that already exists.
95    AlreadyExists = 6,
96
97    /// The caller does not have permission to execute the specified operation.
98    ///
99    /// This indicates insufficient permissions to perform the operation.
100    PermissionDenied = 7,
101
102    /// Some resource has been exhausted.
103    ///
104    /// This indicates that a resource limit has been exceeded, such as memory
105    /// or computation limits.
106    ResourceExhausted = 8,
107
108    /// The system is not in a state required for the operation's execution.
109    ///
110    /// This indicates that the operation cannot be performed in the current state.
111    FailedPrecondition = 9,
112
113    /// The operation was aborted.
114    ///
115    /// This indicates that the operation was aborted, typically due to a
116    /// concurrency issue.
117    Aborted = 10,
118
119    /// Operation was attempted past the valid range.
120    ///
121    /// This indicates that the operation exceeded valid bounds, such as
122    /// accessing an array out of bounds.
123    OutOfRange = 11,
124
125    /// Operation is not implemented or not supported.
126    ///
127    /// This indicates that the requested operation is not implemented or
128    /// not supported in the current context.
129    Unimplemented = 12,
130
131    /// Internal error.
132    ///
133    /// This indicates an internal error that should not normally occur.
134    /// It often indicates a bug in the implementation.
135    Internal = 13,
136
137    /// The service is currently unavailable.
138    ///
139    /// This indicates that the service is temporarily unavailable and the
140    /// operation should be retried later.
141    Unavailable = 14,
142
143    /// Unrecoverable data loss or corruption.
144    ///
145    /// This indicates that data has been lost or corrupted in an unrecoverable way.
146    DataLoss = 15,
147
148    /// The request does not have valid authentication credentials.
149    ///
150    /// This indicates that the operation requires authentication credentials
151    /// that are missing or invalid.
152    Unauthenticated = 16,
153}
154
155impl Code {
156    /// Returns a human-readable description of this error code.
157    ///
158    /// This method provides a detailed description of what the error code means.
159    ///
160    /// # Examples
161    ///
162    /// ```rust
163    /// use cel_cxx::Code;
164    ///
165    /// let code = Code::InvalidArgument;
166    /// println!("Description: {}", code.description());
167    /// // Output: Description: Client specified an invalid argument
168    /// ```
169    ///
170    /// # Note
171    ///
172    /// If you only need the description for display purposes (such as in
173    /// `println!`, `format!`, or logging), you can use the `Display` trait
174    /// implementation instead, which is more concise.
175    pub fn description(&self) -> &'static str {
176        match self {
177            Code::Ok => "The operation completed successfully",
178            Code::Cancelled => "The operation was cancelled",
179            Code::Unknown => "Unknown error",
180            Code::InvalidArgument => "Client specified an invalid argument",
181            Code::DeadlineExceeded => "Deadline expired before operation could complete",
182            Code::NotFound => "Some requested entity was not found",
183            Code::AlreadyExists => "Some entity that we attempted to create already exists",
184            Code::PermissionDenied => {
185                "The caller does not have permission to execute the specified operation"
186            }
187            Code::ResourceExhausted => "Some resource has been exhausted",
188            Code::FailedPrecondition => {
189                "The system is not in a state required for the operation's execution"
190            }
191            Code::Aborted => "The operation was aborted",
192            Code::OutOfRange => "Operation was attempted past the valid range",
193            Code::Unimplemented => "Operation is not implemented or not supported",
194            Code::Internal => "Internal error",
195            Code::Unavailable => "The service is currently unavailable",
196            Code::DataLoss => "Unrecoverable data loss or corruption",
197            Code::Unauthenticated => "The request does not have valid authentication credentials",
198        }
199    }
200}
201
202impl std::fmt::Display for Code {
203    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204        std::fmt::Display::fmt(self.description(), f)
205    }
206}
207
208// ===== impl Error =====
209
210impl Error {
211    /// Creates a new error with the specified code and message.
212    ///
213    /// This is the fundamental constructor for creating errors. Most users
214    /// should prefer the specific constructor methods like [`invalid_argument`],
215    /// [`not_found`], etc., which are more descriptive.
216    ///
217    /// # Arguments
218    ///
219    /// * `code` - The error code indicating the type of error
220    /// * `message` - A descriptive message explaining the error
221    ///
222    /// # Examples
223    ///
224    /// ```rust
225    /// use cel_cxx::{Error, Code};
226    ///
227    /// let error = Error::new(Code::InvalidArgument, "Invalid input provided");
228    /// assert_eq!(error.code(), Code::InvalidArgument);
229    /// assert_eq!(error.message(), "Invalid input provided");
230    /// ```
231    ///
232    /// [`invalid_argument`]: Self::invalid_argument
233    /// [`not_found`]: Self::not_found
234    pub fn new(code: Code, message: impl Into<String>) -> Self {
235        Self {
236            code,
237            message: message.into(),
238        }
239    }
240
241    /// Creates an "ok" status with the given message.
242    ///
243    /// This is rarely used for actual errors, but may be useful in some
244    /// contexts where a status needs to indicate success with additional information.
245    ///
246    /// # Examples
247    ///
248    /// ```rust
249    /// use cel_cxx::Error;
250    ///
251    /// let status = Error::ok("Operation completed successfully");
252    /// ```
253    pub fn ok(message: impl Into<String>) -> Self {
254        Self::new(Code::Ok, message)
255    }
256
257    /// Creates a "cancelled" error with the given message.
258    ///
259    /// # Examples
260    ///
261    /// ```rust
262    /// use cel_cxx::Error;
263    ///
264    /// let error = Error::cancelled("Operation was cancelled by user");
265    /// ```
266    pub fn cancelled(message: impl Into<String>) -> Self {
267        Self::new(Code::Cancelled, message)
268    }
269
270    /// Creates an "unknown" error with the given message.
271    ///
272    /// This is useful when converting from other error types where the specific
273    /// category cannot be determined.
274    ///
275    /// # Examples
276    ///
277    /// ```rust
278    /// use cel_cxx::Error;
279    ///
280    /// let error = Error::unknown("An unexpected error occurred");
281    /// ```
282    pub fn unknown(message: impl Into<String>) -> Self {
283        Self::new(Code::Unknown, message)
284    }
285
286    /// Creates an "invalid argument" error with the given message.
287    ///
288    /// This is commonly used for CEL compilation errors due to invalid syntax
289    /// or malformed expressions.
290    ///
291    /// # Examples
292    ///
293    /// ```rust
294    /// use cel_cxx::Error;
295    ///
296    /// let error = Error::invalid_argument("Expression contains invalid syntax");
297    /// ```
298    pub fn invalid_argument(message: impl Into<String>) -> Self {
299        Self::new(Code::InvalidArgument, message)
300    }
301
302    /// Creates a "deadline exceeded" error with the given message.
303    ///
304    /// # Examples
305    ///
306    /// ```rust
307    /// use cel_cxx::Error;
308    ///
309    /// let error = Error::deadline_exceeded("Operation took too long to complete");
310    /// ```
311    pub fn deadline_exceeded(message: impl Into<String>) -> Self {
312        Self::new(Code::DeadlineExceeded, message)
313    }
314
315    /// Creates a "not found" error with the given message.
316    ///
317    /// This is commonly used when a referenced variable or function is not
318    /// found in the current environment.
319    ///
320    /// # Examples
321    ///
322    /// ```rust
323    /// use cel_cxx::Error;
324    ///
325    /// let error = Error::not_found("Variable 'x' is not declared");
326    /// ```
327    pub fn not_found(message: impl Into<String>) -> Self {
328        Self::new(Code::NotFound, message)
329    }
330
331    /// Creates an "already exists" error with the given message.
332    ///
333    /// # Examples
334    ///
335    /// ```rust
336    /// use cel_cxx::Error;
337    ///
338    /// let error = Error::already_exists("Function 'myFunc' is already registered");
339    /// ```
340    pub fn already_exists(message: impl Into<String>) -> Self {
341        Self::new(Code::AlreadyExists, message)
342    }
343
344    /// Creates a "permission denied" error with the given message.
345    ///
346    /// # Examples
347    ///
348    /// ```rust
349    /// use cel_cxx::Error;
350    ///
351    /// let error = Error::permission_denied("Access to this resource is denied");
352    /// ```
353    pub fn permission_denied(message: impl Into<String>) -> Self {
354        Self::new(Code::PermissionDenied, message)
355    }
356
357    /// Creates a "resource exhausted" error with the given message.
358    ///
359    /// This can be used when evaluation hits resource limits such as memory
360    /// or computation time.
361    ///
362    /// # Examples
363    ///
364    /// ```rust
365    /// use cel_cxx::Error;
366    ///
367    /// let error = Error::resource_exhausted("Memory limit exceeded during evaluation");
368    /// ```
369    pub fn resource_exhausted(message: impl Into<String>) -> Self {
370        Self::new(Code::ResourceExhausted, message)
371    }
372
373    /// Creates a "failed precondition" error with the given message.
374    ///
375    /// This indicates that the operation cannot be performed because the system
376    /// is not in the required state. See the documentation for [`Code::FailedPrecondition`]
377    /// for guidance on when to use this vs. other error codes.
378    ///
379    /// # Examples
380    ///
381    /// ```rust
382    /// use cel_cxx::Error;
383    ///
384    /// let error = Error::failed_precondition("Environment must be built before compilation");
385    /// ```
386    pub fn failed_precondition(message: impl Into<String>) -> Self {
387        Self::new(Code::FailedPrecondition, message)
388    }
389
390    /// Creates an "aborted" error with the given message.
391    ///
392    /// This indicates that the operation was aborted, typically due to a
393    /// concurrency issue or transaction conflict.
394    ///
395    /// # Examples
396    ///
397    /// ```rust
398    /// use cel_cxx::Error;
399    ///
400    /// let error = Error::aborted("Transaction was aborted due to conflict");
401    /// ```
402    pub fn aborted(message: impl Into<String>) -> Self {
403        Self::new(Code::Aborted, message)
404    }
405
406    /// Creates an "out of range" error with the given message.
407    ///
408    /// This indicates that the operation attempted to access something outside
409    /// of valid bounds, such as array indices or valid value ranges.
410    ///
411    /// # Examples
412    ///
413    /// ```rust
414    /// use cel_cxx::Error;
415    ///
416    /// let error = Error::out_of_range("Index 10 is out of range for array of length 5");
417    /// ```
418    pub fn out_of_range(message: impl Into<String>) -> Self {
419        Self::new(Code::OutOfRange, message)
420    }
421
422    /// Creates an "unimplemented" error with the given message.
423    ///
424    /// This indicates that the requested operation is not implemented or
425    /// not supported in the current context.
426    ///
427    /// # Examples
428    ///
429    /// ```rust
430    /// use cel_cxx::Error;
431    ///
432    /// let error = Error::unimplemented("This feature is not yet implemented");
433    /// ```
434    pub fn unimplemented(message: impl Into<String>) -> Self {
435        Self::new(Code::Unimplemented, message)
436    }
437
438    /// Creates an "internal" error with the given message.
439    ///
440    /// This indicates an internal error that should not normally occur.
441    /// It typically indicates a bug in the implementation.
442    ///
443    /// # Examples
444    ///
445    /// ```rust
446    /// use cel_cxx::Error;
447    ///
448    /// let error = Error::internal("Internal invariant violated");
449    /// ```
450    pub fn internal(message: impl Into<String>) -> Self {
451        Self::new(Code::Internal, message)
452    }
453
454    /// Creates an "unavailable" error with the given message.
455    ///
456    /// This indicates that the service is temporarily unavailable and the
457    /// operation should be retried later.
458    ///
459    /// # Examples
460    ///
461    /// ```rust
462    /// use cel_cxx::Error;
463    ///
464    /// let error = Error::unavailable("Service is temporarily unavailable");
465    /// ```
466    pub fn unavailable(message: impl Into<String>) -> Self {
467        Self::new(Code::Unavailable, message)
468    }
469
470    /// Creates a "data loss" error with the given message.
471    ///
472    /// This indicates that data has been lost or corrupted in an unrecoverable way.
473    ///
474    /// # Examples
475    ///
476    /// ```rust
477    /// use cel_cxx::Error;
478    ///
479    /// let error = Error::data_loss("Critical data has been corrupted");
480    /// ```
481    pub fn data_loss(message: impl Into<String>) -> Self {
482        Self::new(Code::DataLoss, message)
483    }
484
485    /// Creates an "unauthenticated" error with the given message.
486    ///
487    /// This indicates that the operation requires authentication credentials
488    /// that are missing or invalid.
489    ///
490    /// # Examples
491    ///
492    /// ```rust
493    /// use cel_cxx::Error;
494    ///
495    /// let error = Error::unauthenticated("Valid authentication credentials required");
496    /// ```
497    pub fn unauthenticated(message: impl Into<String>) -> Self {
498        Self::new(Code::Unauthenticated, message)
499    }
500
501    /// Returns the error code for this error.
502    ///
503    /// # Examples
504    ///
505    /// ```rust
506    /// use cel_cxx::{Error, Code};
507    ///
508    /// let error = Error::invalid_argument("Bad input");
509    /// assert_eq!(error.code(), Code::InvalidArgument);
510    /// ```
511    pub fn code(&self) -> Code {
512        self.code
513    }
514
515    /// Returns the error message for this error.
516    ///
517    /// # Examples
518    ///
519    /// ```rust
520    /// use cel_cxx::Error;
521    ///
522    /// let error = Error::invalid_argument("Bad input");
523    /// assert_eq!(error.message(), "Bad input");
524    /// ```
525    pub fn message(&self) -> &str {
526        &self.message
527    }
528
529    pub(crate) fn from_std_error_generic(
530        err: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
531    ) -> Self {
532        Self::from_std_error(err.into())
533    }
534
535    /// Creates a CEL error from a standard library error.
536    ///
537    /// This method attempts to extract meaningful error information from
538    /// standard library errors and convert them to CEL errors. It inspects
539    /// the error source chain for recognizable error types.
540    ///
541    /// # Arguments
542    ///
543    /// * `err` - The standard library error to convert
544    ///
545    /// # Returns
546    ///
547    /// A CEL `Error` with an appropriate error code and message.
548    /// If the error type is not recognized, it will be mapped to `Code::Unknown`.
549    ///
550    /// # Examples
551    ///
552    /// ```rust
553    /// use cel_cxx::Error;
554    /// use std::io;
555    ///
556    /// let io_error = io::Error::new(io::ErrorKind::NotFound, "File not found");
557    /// let cel_error = Error::from_std_error(Box::new(io_error));
558    /// ```
559    pub fn from_std_error(err: Box<dyn std::error::Error + Send + Sync + 'static>) -> Self {
560        Self::try_from_std_error(err)
561            .unwrap_or_else(|err| Self::new(Code::Unknown, err.to_string()))
562    }
563
564    /// Attempts to create a CEL error from a standard library error.
565    ///
566    /// This method is similar to [`from_std_error`] but returns the original
567    /// error if it cannot be converted to a CEL error.
568    ///
569    /// # Arguments
570    ///
571    /// * `err` - The standard library error to convert
572    ///
573    /// # Returns
574    ///
575    /// `Ok(Error)` if the conversion was successful, or `Err(original_error)`
576    /// if the error type could not be recognized.
577    ///
578    /// # Downcast Stability
579    ///
580    /// This function does not provide any stability guarantees around how it
581    /// will downcast errors into status codes. The conversion logic may change
582    /// in future versions.
583    ///
584    /// [`from_std_error`]: Self::from_std_error
585    pub fn try_from_std_error(
586        err: Box<dyn std::error::Error + Send + Sync + 'static>,
587    ) -> Result<Self, Box<dyn std::error::Error + Send + Sync + 'static>> {
588        let err = match err.downcast::<Error>() {
589            Ok(error) => return Ok(*error),
590            Err(err) => err,
591        };
592
593        if let Some(error) = Self::find_in_source_chain(&*err) {
594            return Ok(error);
595        }
596        Err(err)
597    }
598
599    fn find_in_source_chain(err: &(dyn std::error::Error + 'static)) -> Option<Self> {
600        use crate::types::InvalidMapKeyType;
601
602        let mut source = Some(err);
603
604        while let Some(err) = source {
605            if let Some(error) = err.downcast_ref::<Error>() {
606                return Some((*error).clone());
607            }
608
609            if let Some(invalid_mapkey_type) = err.downcast_ref::<InvalidMapKeyType>() {
610                return Some(Self::invalid_argument(invalid_mapkey_type.0.to_string()));
611            }
612
613            // TODO: Add more error types here
614
615            source = err.source();
616        }
617
618        None
619    }
620}
621
622impl std::fmt::Display for Error {
623    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
624        write!(f, "code: {:?}, message: {:?}", self.code(), self.message(),)
625    }
626}
627
628impl std::error::Error for Error {}
629
630/// Trait for converting values into CEL errors.
631///
632/// This trait provides a standardized way to convert different error types
633/// into CEL [`Error`] instances. It is automatically implemented for all
634/// types that implement the standard library's `Error` trait.
635///
636/// # Examples
637///
638/// ```rust
639/// use cel_cxx::{Error, IntoError};
640/// use std::io;
641///
642/// let io_error = io::Error::new(io::ErrorKind::NotFound, "File not found");
643/// let cel_error = io_error.into_error();
644/// ```
645///
646/// # Automatic Implementation
647///
648/// This trait is automatically implemented for any type that implements
649/// `std::error::Error + Send + Sync + 'static`, so you typically don't
650/// need to implement it manually.
651pub trait IntoError {
652    /// Converts this value into a CEL error.
653    ///
654    /// # Returns
655    ///
656    /// A CEL [`Error`] representing this error condition.
657    fn into_error(self) -> Error;
658}
659
660impl<E: std::error::Error + Send + Sync + 'static> IntoError for E {
661    fn into_error(self) -> Error {
662        Error::from_std_error_generic(Box::new(self))
663    }
664}