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    Io,
16    Python,
17    Multiple,
18    Context,
19}
20
21#[derive(Error, Debug)]
22pub enum RadiateError {
23    #[error("Builder error: {0}")]
24    Builder(String),
25
26    #[error("Engine error: {0}")]
27    Engine(String),
28
29    #[error("Genome error: {0}")]
30    Genome(String),
31
32    #[error("Codec error: {0}")]
33    Codec(String),
34
35    #[error("Evaluation error: {0}")]
36    Evaluation(String),
37
38    #[error("Invalid fitness: {0}")]
39    Fitness(String),
40
41    #[error("I/O error: {0}")]
42    Io(#[from] std::io::Error),
43
44    #[cfg(feature = "python")]
45    #[error("Python error: {0}")]
46    Python(#[from] pyo3::PyErr),
47
48    #[error("Multiple errors:\n{0}")]
49    Multiple(String),
50
51    #[error("{context}\nCaused by: {source}")]
52    Context {
53        context: String,
54        #[source]
55        source: Box<RadiateError>,
56    },
57}
58
59impl RadiateError {
60    pub fn new_builder(msg: impl Into<String>) -> Self {
61        RadiateError::Builder(msg.into())
62    }
63
64    pub fn new_fitness(msg: impl Into<String>) -> Self {
65        RadiateError::Fitness(msg.into())
66    }
67}
68
69impl RadiateError {
70    pub fn code(&self) -> Code {
71        match self {
72            RadiateError::Builder { .. } => Code::InvalidConfig,
73            RadiateError::Engine { .. } => Code::Engine,
74            RadiateError::Genome { .. } => Code::Genome,
75            RadiateError::Codec { .. } => Code::Codec,
76            RadiateError::Fitness { .. } => Code::Fitness,
77            RadiateError::Evaluation { .. } => Code::Evaluation,
78            RadiateError::Io { .. } => Code::Io,
79            #[cfg(feature = "python")]
80            RadiateError::Python { .. } => Code::Python,
81            RadiateError::Multiple(_) => Code::Multiple,
82            RadiateError::Context { .. } => Code::Context,
83        }
84    }
85    pub fn context(self, msg: impl Into<String>) -> Self {
86        RadiateError::Context {
87            context: msg.into(),
88            source: Box::new(self),
89        }
90    }
91}
92
93pub trait ResultExt<T> {
94    fn context(self, msg: impl Into<String>) -> Result<T>;
95
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}