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 (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 (Context: $msg:expr, $source:expr $(,)?) => {
149 $crate::__private::must_use($source.into().context($msg))
150 };
151
152 (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 ($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}