1use thiserror::Error;
6use ruv_fann::{NetworkError, TrainingError};
7
8pub type NeuroDivergentResult<T> = Result<T, NeuroDivergentError>;
10
11#[derive(Error, Debug)]
13pub enum NeuroDivergentError {
14 #[error("Model configuration error: {0}")]
15 ConfigError(String),
16
17 #[error("Data validation error: {0}")]
18 DataError(String),
19
20 #[error("Training error: {0}")]
21 TrainingError(String),
22
23 #[error("Prediction error: {0}")]
24 PredictionError(String),
25
26 #[error("Network error: {0}")]
27 NetworkError(#[from] NetworkError),
28
29 #[error("Internal ruv-FANN training error: {0}")]
30 RuvFannTrainingError(#[from] TrainingError),
31
32 #[error("Time series error: {0}")]
33 TimeSeriesError(String),
34
35 #[error("Sequence processing error: {0}")]
36 SequenceError(String),
37
38 #[error("State management error: {0}")]
39 StateError(String),
40
41 #[error("Dimension mismatch: expected {expected}, got {actual}")]
42 DimensionMismatch { expected: usize, actual: usize },
43
44 #[error("Invalid sequence length: {0}")]
45 InvalidSequenceLength(usize),
46
47 #[error("Missing required feature: {0}")]
48 MissingFeature(String),
49
50 #[error("I/O error: {0}")]
51 IoError(#[from] std::io::Error),
52
53 #[error("Serialization error: {0}")]
54 SerializationError(String),
55
56 #[cfg(feature = "polars")]
57 #[error("Polars error: {0}")]
58 PolarsError(#[from] polars::error::PolarsError),
59
60 #[error("Custom error: {0}")]
61 Custom(String),
62}
63
64impl NeuroDivergentError {
65 pub fn custom<T: Into<String>>(message: T) -> Self {
67 Self::Custom(message.into())
68 }
69
70 pub fn config<T: Into<String>>(message: T) -> Self {
72 Self::ConfigError(message.into())
73 }
74
75 pub fn data<T: Into<String>>(message: T) -> Self {
77 Self::DataError(message.into())
78 }
79
80 pub fn training<T: Into<String>>(message: T) -> Self {
82 Self::TrainingError(message.into())
83 }
84
85 pub fn prediction<T: Into<String>>(message: T) -> Self {
87 Self::PredictionError(message.into())
88 }
89
90 pub fn time_series<T: Into<String>>(message: T) -> Self {
92 Self::TimeSeriesError(message.into())
93 }
94
95 pub fn sequence<T: Into<String>>(message: T) -> Self {
97 Self::SequenceError(message.into())
98 }
99
100 pub fn state<T: Into<String>>(message: T) -> Self {
102 Self::StateError(message.into())
103 }
104
105 pub fn dimension_mismatch(expected: usize, actual: usize) -> Self {
107 Self::DimensionMismatch { expected, actual }
108 }
109
110 pub fn invalid_sequence_length(length: usize) -> Self {
112 Self::InvalidSequenceLength(length)
113 }
114
115 pub fn missing_feature<T: Into<String>>(feature: T) -> Self {
117 Self::MissingFeature(feature.into())
118 }
119
120 pub fn is_recoverable(&self) -> bool {
122 match self {
123 Self::ConfigError(_) => false,
124 Self::DataError(_) => false,
125 Self::TrainingError(_) => true,
126 Self::PredictionError(_) => true,
127 Self::NetworkError(_) => false,
128 Self::RuvFannTrainingError(_) => true,
129 Self::TimeSeriesError(_) => false,
130 Self::SequenceError(_) => true,
131 Self::StateError(_) => true,
132 Self::DimensionMismatch { .. } => false,
133 Self::InvalidSequenceLength(_) => false,
134 Self::MissingFeature(_) => false,
135 Self::IoError(_) => false,
136 Self::SerializationError(_) => false,
137 #[cfg(feature = "polars")]
138 Self::PolarsError(_) => false,
139 Self::Custom(_) => true,
140 }
141 }
142
143 pub fn category(&self) -> ErrorCategory {
145 match self {
146 Self::ConfigError(_) => ErrorCategory::Configuration,
147 Self::DataError(_) => ErrorCategory::Data,
148 Self::TrainingError(_) => ErrorCategory::Training,
149 Self::PredictionError(_) => ErrorCategory::Prediction,
150 Self::NetworkError(_) => ErrorCategory::Network,
151 Self::RuvFannTrainingError(_) => ErrorCategory::Training,
152 Self::TimeSeriesError(_) => ErrorCategory::Data,
153 Self::SequenceError(_) => ErrorCategory::Processing,
154 Self::StateError(_) => ErrorCategory::State,
155 Self::DimensionMismatch { .. } => ErrorCategory::Validation,
156 Self::InvalidSequenceLength(_) => ErrorCategory::Validation,
157 Self::MissingFeature(_) => ErrorCategory::Configuration,
158 Self::IoError(_) => ErrorCategory::IO,
159 Self::SerializationError(_) => ErrorCategory::IO,
160 #[cfg(feature = "polars")]
161 Self::PolarsError(_) => ErrorCategory::Data,
162 Self::Custom(_) => ErrorCategory::Other,
163 }
164 }
165}
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq)]
169pub enum ErrorCategory {
170 Configuration,
171 Data,
172 Training,
173 Prediction,
174 Network,
175 Processing,
176 State,
177 Validation,
178 IO,
179 Other,
180}
181
182impl std::fmt::Display for ErrorCategory {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 match self {
185 Self::Configuration => write!(f, "Configuration"),
186 Self::Data => write!(f, "Data"),
187 Self::Training => write!(f, "Training"),
188 Self::Prediction => write!(f, "Prediction"),
189 Self::Network => write!(f, "Network"),
190 Self::Processing => write!(f, "Processing"),
191 Self::State => write!(f, "State"),
192 Self::Validation => write!(f, "Validation"),
193 Self::IO => write!(f, "IO"),
194 Self::Other => write!(f, "Other"),
195 }
196 }
197}
198
199#[derive(Debug, Clone)]
201pub struct ErrorContext {
202 pub operation: String,
203 pub model_name: Option<String>,
204 pub epoch: Option<usize>,
205 pub batch: Option<usize>,
206 pub additional_info: std::collections::HashMap<String, String>,
207}
208
209impl ErrorContext {
210 pub fn new<T: Into<String>>(operation: T) -> Self {
211 Self {
212 operation: operation.into(),
213 model_name: None,
214 epoch: None,
215 batch: None,
216 additional_info: std::collections::HashMap::new(),
217 }
218 }
219
220 pub fn with_model<T: Into<String>>(mut self, model_name: T) -> Self {
221 self.model_name = Some(model_name.into());
222 self
223 }
224
225 pub fn with_epoch(mut self, epoch: usize) -> Self {
226 self.epoch = Some(epoch);
227 self
228 }
229
230 pub fn with_batch(mut self, batch: usize) -> Self {
231 self.batch = Some(batch);
232 self
233 }
234
235 pub fn with_info<K, V>(mut self, key: K, value: V) -> Self
236 where
237 K: Into<String>,
238 V: Into<String>,
239 {
240 self.additional_info.insert(key.into(), value.into());
241 self
242 }
243}
244
245pub trait ErrorContextExt<T> {
247 fn with_context(self, context: ErrorContext) -> NeuroDivergentResult<T>;
248}
249
250impl<T> ErrorContextExt<T> for NeuroDivergentResult<T> {
251 fn with_context(self, context: ErrorContext) -> NeuroDivergentResult<T> {
252 self.map_err(|e| {
253 let context_info = format!(
254 "Operation: {}, Model: {}, Epoch: {}, Batch: {}",
255 context.operation,
256 context.model_name.unwrap_or_else(|| "Unknown".to_string()),
257 context.epoch.map(|e| e.to_string()).unwrap_or_else(|| "N/A".to_string()),
258 context.batch.map(|b| b.to_string()).unwrap_or_else(|| "N/A".to_string())
259 );
260
261 match e {
262 NeuroDivergentError::Custom(msg) => {
263 NeuroDivergentError::custom(format!("{} | Context: {}", msg, context_info))
264 }
265 other => other,
266 }
267 })
268 }
269}