Skip to main content

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    Metric,
16    Expr,
17    Other,
18    Io,
19    Python,
20    Multiple,
21    Context,
22}
23
24#[derive(Error, Debug)]
25pub enum RadiateError {
26    #[error("Builder error: {0}")]
27    Builder(String),
28
29    #[error("Engine error: {0}")]
30    Engine(String),
31
32    #[error("Genome error: {0}")]
33    Genome(String),
34
35    #[error("Codec error: {0}")]
36    Codec(String),
37
38    #[error("Evaluation error: {0}")]
39    Evaluation(String),
40
41    #[error("Invalid fitness: {0}")]
42    Fitness(String),
43
44    #[error("Metric error: {0}")]
45    Metric(String),
46
47    #[error("Expression error: {0}")]
48    Expr(String),
49
50    #[cfg(feature = "python")]
51    #[error("Python error: {0}")]
52    Python(#[from] pyo3::PyErr),
53
54    #[error("Multiple errors:\n{0}")]
55    Multiple(String),
56
57    #[error("Other error: {0}")]
58    Other(String),
59
60    #[error("{context}\nCaused by: {source}")]
61    Context {
62        context: String,
63        #[source]
64        source: Box<RadiateError>,
65    },
66}
67
68impl RadiateError {
69    pub fn code(&self) -> Code {
70        match self {
71            RadiateError::Builder { .. } => Code::InvalidConfig,
72            RadiateError::Engine { .. } => Code::Engine,
73            RadiateError::Genome { .. } => Code::Genome,
74            RadiateError::Codec { .. } => Code::Codec,
75            RadiateError::Fitness { .. } => Code::Fitness,
76            RadiateError::Metric { .. } => Code::Metric,
77            RadiateError::Expr { .. } => Code::Expr,
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    (Metric: $fmt:literal $(, $arg:expr)* $(,)?) => {
141        $crate::__private::must_use($crate::RadiateError::Metric(format!($fmt, $($arg),*)))
142    };
143    (Expr: $fmt:literal $(, $arg:expr)* $(,)?) => {
144        $crate::__private::must_use($crate::RadiateError::Expr(format!($fmt, $($arg),*)))
145    };
146
147    // Contextual message
148    (Context: $msg:expr, $source:expr $(,)?) => {
149        $crate::__private::must_use($source.into().context($msg))
150    };
151
152    // Raw string-like message (any expr -> String)
153    (Builder: $msg:expr $(,)?) => {
154        $crate::__private::must_use($crate::RadiateError::Builder($msg.to_string()))
155    };
156    (Engine: $msg:expr $(,)?) => {
157        $crate::__private::must_use($crate::RadiateError::Engine($msg.to_string()))
158    };
159    (Genome: $msg:expr $(,)?) => {
160        $crate::__private::must_use($crate::RadiateError::Genome($msg.to_string()))
161    };
162    (Codec: $msg:expr $(,)?) => {
163        $crate::__private::must_use($crate::RadiateError::Codec($msg.to_string()))
164    };
165    (Evaluation: $msg:expr $(,)?) => {
166        $crate::__private::must_use($crate::RadiateError::Evaluation($msg.to_string()))
167    };
168    (Python: $msg:expr $(,)?) => {
169        $crate::__private::must_use(pyo3::PyErr::new::<pyo3::exceptions::PyException, _>($msg.to_string()))
170    };
171    (Metric: $msg:expr $(,)?) => {
172        $crate::__private::must_use($crate::RadiateError::Metric($msg.to_string()))
173    };
174    (Expr: $msg:expr $(,)?) => {
175        $crate::__private::must_use($crate::RadiateError::Expr($msg.to_string()))
176    };
177
178    // Fallback -> Engine (for now, could be Metric or other)
179    ($msg:expr $(,)?) => {
180        $crate::__private::must_use($crate::RadiateError::Engine($msg.to_string()))
181    };
182}
183
184#[macro_export]
185macro_rules! radiate_bail {
186    ($($tt:tt)+) => { return Err($crate::radiate_err!($($tt)+)) };
187}
188
189#[macro_export]
190macro_rules! ensure {
191    ($cond:expr, $($tt:tt)+) => {
192        if !$cond { $crate::radiate_bail!($($tt)+); }
193    };
194}