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}