Skip to main content

keel/
error.rs

1use keel_core_api::{ErrorCode, OutcomeError};
2use std::fmt;
3
4/// The error a `#[keel::wrap]`-wrapped function or [`crate::KeelMiddleware`]
5/// surfaces.
6///
7/// `Original` is your own code's error, unchanged, from the attempt the
8/// engine actually ran last (DX invariant 5's "re-raise the original
9/// exception unchanged", ported to a typed enum since Rust has no untyped
10/// exceptions to re-raise). `Keel` is a judgment the engine made *without*
11/// ever calling your code: a circuit breaker fast-fail (`KEEL-E012`), a rate
12/// budget rejection (`KEEL-E013`), or a non-idempotent call correctly left
13/// un-retried (`KEEL-E014`) — see `contracts/error-codes.json` for the full
14/// taxonomy.
15#[derive(Debug)]
16pub enum Error<E> {
17    /// Your code/library's own error from the last attempt.
18    Original(E),
19    /// A judgment the engine made without calling your code.
20    Keel(OutcomeError),
21}
22
23impl<E: fmt::Display> fmt::Display for Error<E> {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        match self {
26            Error::Original(err) => write!(f, "{err}"),
27            Error::Keel(err) => write!(f, "{}: {}", err.code, err.message),
28        }
29    }
30}
31
32impl<E: fmt::Debug + fmt::Display> std::error::Error for Error<E> {}
33
34impl<E> Error<E> {
35    /// The `KEEL-E0NN` code the engine assigned, if this is a [`Error::Keel`]
36    /// judgment rather than your own code's [`Error::Original`] error.
37    #[must_use]
38    pub const fn code(&self) -> Option<ErrorCode> {
39        match self {
40            Error::Original(_) => None,
41            Error::Keel(err) => Some(err.code),
42        }
43    }
44
45    /// Recover your code/library's original error, if this wraps one — an
46    /// engine judgment made without ever calling your code has none to give
47    /// back.
48    pub fn into_original(self) -> Option<E> {
49        match self {
50            Error::Original(err) => Some(err),
51            Error::Keel(_) => None,
52        }
53    }
54}