radiate_error/
lib.rs

1use thiserror::Error;
2#[cfg(feature = "python")]
3pub mod python;
4
5pub type Result<T> = std::result::Result<T, RadiateError>;
6
7#[derive(Copy, Clone, Debug, PartialEq, Eq)]
8pub enum Code {
9    InvalidConfig,
10    Engine,
11    Codec,
12    Evaluation,
13    Genome,
14    Fitness,
15    Other,
16    Io,
17    Python,
18    Multiple,
19    Context,
20}
21
22#[derive(Error, Debug)]
23pub enum RadiateError {
24    #[error("Builder error: {0}")]
25    Builder(String),
26
27    #[error("Engine error: {0}")]
28    Engine(String),
29
30    #[error("Genome error: {0}")]
31    Genome(String),
32
33    #[error("Codec error: {0}")]
34    Codec(String),
35
36    #[error("Evaluation error: {0}")]
37    Evaluation(String),
38
39    #[error("Invalid fitness: {0}")]
40    Fitness(String),
41
42    #[cfg(feature = "python")]
43    #[error("Python error: {0}")]
44    Python(#[from] pyo3::PyErr),
45
46    #[error("Multiple errors:\n{0}")]
47    Multiple(String),
48
49    #[error("Other error: {0}")]
50    Other(String),
51
52    #[error("{context}\nCaused by: {source}")]
53    Context {
54        context: String,
55        #[source]
56        source: Box<RadiateError>,
57    },
58}
59
60impl RadiateError {
61    pub fn new_builder(msg: impl Into<String>) -> Self {
62        RadiateError::Builder(msg.into())
63    }
64
65    pub fn new_fitness(msg: impl Into<String>) -> Self {
66        RadiateError::Fitness(msg.into())
67    }
68}
69
70impl RadiateError {
71    pub fn code(&self) -> Code {
72        match self {
73            RadiateError::Builder { .. } => Code::InvalidConfig,
74            RadiateError::Engine { .. } => Code::Engine,
75            RadiateError::Genome { .. } => Code::Genome,
76            RadiateError::Codec { .. } => Code::Codec,
77            RadiateError::Fitness { .. } => Code::Fitness,
78            RadiateError::Evaluation { .. } => Code::Evaluation,
79            RadiateError::Other(_) => Code::Other,
80            #[cfg(feature = "python")]
81            RadiateError::Python { .. } => Code::Python,
82            RadiateError::Multiple(_) => Code::Multiple,
83            RadiateError::Context { .. } => Code::Context,
84        }
85    }
86    pub fn context(self, msg: impl Into<String>) -> Self {
87        RadiateError::Context {
88            context: msg.into(),
89            source: Box::new(self),
90        }
91    }
92}
93
94pub trait ResultExt<T> {
95    fn context(self, msg: impl Into<String>) -> Result<T>;
96    fn with_context<F: FnOnce() -> String>(self, f: F) -> Result<T>;
97}
98
99impl<T, E: Into<RadiateError>> ResultExt<T> for std::result::Result<T, E> {
100    fn context(self, msg: impl Into<String>) -> Result<T> {
101        self.map_err(|e| e.into().context(msg))
102    }
103
104    fn with_context<F: FnOnce() -> String>(self, f: F) -> Result<T> {
105        self.map_err(|e| e.into().context(f()))
106    }
107}
108
109#[doc(hidden)]
110pub mod __private {
111    #[inline]
112    #[cold]
113    #[must_use]
114    pub fn must_use<E>(e: E) -> E {
115        e
116    }
117}
118
119#[macro_export]
120macro_rules! radiate_err {
121    // Formatted message
122    (Builder: $fmt:literal $(, $arg:expr)* $(,)?) => {
123        $crate::__private::must_use($crate::RadiateError::Builder(format!($fmt, $($arg),*)))
124    };
125    (Engine: $fmt:literal $(, $arg:expr)* $(,)?) => {
126        $crate::__private::must_use($crate::RadiateError::Engine(format!($fmt, $($arg),*)))
127    };
128    (Genome: $fmt:literal $(, $arg:expr)* $(,)?) => {
129        $crate::__private::must_use($crate::RadiateError::Genome(format!($fmt, $($arg),*)))
130    };
131    (Codec: $fmt:literal $(, $arg:expr)* $(,)?) => {
132        $crate::__private::must_use($crate::RadiateError::Codec(format!($fmt, $($arg),*)))
133    };
134    (Evaluation: $fmt:literal $(, $arg:expr)* $(,)?) => {
135        $crate::__private::must_use($crate::RadiateError::Evaluation(format!($fmt, $($arg),*)))
136    };
137    (Python: $fmt:literal $(, $arg:expr)* $(,)?) => {
138        $crate::__private::must_use(pyo3::PyErr::new::<pyo3::exceptions::PyException, _>(format!($fmt, $($arg),*)))
139    };
140
141    // Raw string-like message (any expr -> String)
142    (Builder: $msg:expr $(,)?) => {
143        $crate::__private::must_use($crate::RadiateError::Builder($msg.to_string()))
144    };
145    (Engine: $msg:expr $(,)?) => {
146        $crate::__private::must_use($crate::RadiateError::Engine($msg.to_string()))
147    };
148    (Genome: $msg:expr $(,)?) => {
149        $crate::__private::must_use($crate::RadiateError::Genome($msg.to_string()))
150    };
151    (Codec: $msg:expr $(,)?) => {
152        $crate::__private::must_use($crate::RadiateError::Codec($msg.to_string()))
153    };
154    (Evaluation: $msg:expr $(,)?) => {
155        $crate::__private::must_use($crate::RadiateError::Evaluation($msg.to_string()))
156    };
157
158    // Fallback -> Engine
159    ($msg:expr $(,)?) => {
160        $crate::__private::must_use($crate::RadiateError::Engine($msg.to_string()))
161    };
162}
163
164#[macro_export]
165macro_rules! radiate_bail {
166    ($($tt:tt)+) => { return Err($crate::radiate_err!($($tt)+)) };
167}
168
169#[macro_export]
170macro_rules! ensure {
171    ($cond:expr, $($tt:tt)+) => {
172        if !$cond { $crate::radiate_bail!($($tt)+); }
173    };
174}