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 (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 (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 ($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}