1use std::fmt;
6use thiserror::Error;
7
8#[derive(Debug, Clone)]
10pub struct ErrorLocation {
11 pub file: &'static str,
13 pub line: u32,
15 pub column: Option<u32>,
17 pub function: Option<&'static str>,
19}
20
21impl ErrorLocation {
22 #[must_use]
24 #[inline]
25 pub const fn new(file: &'static str, line: u32) -> Self {
26 Self {
27 file,
28 line,
29 column: None,
30 function: None,
31 }
32 }
33
34 #[must_use]
36 #[inline]
37 pub const fn new_with_function(file: &'static str, line: u32, function: &'static str) -> Self {
38 Self {
39 file,
40 line,
41 column: None,
42 function: Some(function),
43 }
44 }
45
46 #[must_use]
48 #[inline]
49 pub const fn new_with_column(file: &'static str, line: u32, column: u32) -> Self {
50 Self {
51 file,
52 line,
53 column: Some(column),
54 function: None,
55 }
56 }
57
58 #[must_use]
60 #[inline]
61 pub const fn new_full(
62 file: &'static str,
63 line: u32,
64 column: u32,
65 function: &'static str,
66 ) -> Self {
67 Self {
68 file,
69 line,
70 column: Some(column),
71 function: Some(function),
72 }
73 }
74
75 #[must_use]
77 #[inline]
78 pub fn here() -> Self {
79 Self::new(file!(), line!())
80 }
81}
82
83impl fmt::Display for ErrorLocation {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 write!(f, "{}:{}", self.file, self.line)?;
86 if let Some(column) = self.column {
87 write!(f, ":{column}")?;
88 }
89 if let Some(function) = self.function {
90 write!(f, " in {function}")?;
91 }
92 Ok(())
93 }
94}
95
96#[derive(Debug, Clone)]
98pub struct ErrorContext {
99 pub message: String,
101 pub location: Option<ErrorLocation>,
103 pub cause: Option<Box<CoreError>>,
105}
106
107impl ErrorContext {
108 #[must_use]
110 pub fn new<S: Into<String>>(message: S) -> Self {
111 Self {
112 message: message.into(),
113 location: None,
114 cause: None,
115 }
116 }
117
118 #[must_use]
120 pub fn with_location(mut self, location: ErrorLocation) -> Self {
121 self.location = Some(location);
122 self
123 }
124
125 #[must_use]
127 pub fn with_cause(mut self, cause: CoreError) -> Self {
128 self.cause = Some(Box::new(cause));
129 self
130 }
131}
132
133impl fmt::Display for ErrorContext {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 write!(f, "{}", self.message)?;
136 if let Some(location) = &self.location {
137 write!(f, " at {location}")?;
138 }
139 if let Some(cause) = &self.cause {
140 write!(f, "\nCaused by: {cause}")?;
141 }
142 Ok(())
143 }
144}
145
146#[derive(Error, Debug, Clone)]
148pub enum CoreError {
149 #[error("{0}")]
151 ComputationError(ErrorContext),
152
153 #[error("{0}")]
155 DomainError(ErrorContext),
156
157 #[error("{0}")]
159 DispatchError(ErrorContext),
160
161 #[error("{0}")]
163 ConvergenceError(ErrorContext),
164
165 #[error("{0}")]
167 DimensionError(ErrorContext),
168
169 #[error("{0}")]
171 ShapeError(ErrorContext),
172
173 #[error("{0}")]
175 IndexError(ErrorContext),
176
177 #[error("{0}")]
179 ValueError(ErrorContext),
180
181 #[error("{0}")]
183 TypeError(ErrorContext),
184
185 #[error("{0}")]
187 NotImplementedError(ErrorContext),
188
189 #[error("{0}")]
191 ImplementationError(ErrorContext),
192
193 #[error("{0}")]
195 MemoryError(ErrorContext),
196
197 #[error("{0}")]
199 AllocationError(ErrorContext),
200
201 #[error("{0}")]
203 ConfigError(ErrorContext),
204
205 #[error("{0}")]
207 InvalidArgument(ErrorContext),
208
209 #[error("{0}")]
211 InvalidInput(ErrorContext),
212
213 #[error("{0}")]
215 PermissionError(ErrorContext),
216
217 #[error("{0}")]
219 ValidationError(ErrorContext),
220
221 #[error("{0}")]
223 InvalidState(ErrorContext),
224
225 #[error("{0}")]
227 JITError(ErrorContext),
228
229 #[error("JSON error: {0}")]
231 JSONError(ErrorContext),
232
233 #[error("IO error: {0}")]
235 IoError(ErrorContext),
236
237 #[error("Scheduler error: {0}")]
239 SchedulerError(ErrorContext),
240
241 #[error("Timeout error: {0}")]
243 TimeoutError(ErrorContext),
244
245 #[error("Compression error: {0}")]
247 CompressionError(ErrorContext),
248
249 #[error("Invalid shape: {0}")]
251 InvalidShape(ErrorContext),
252
253 #[error("Device error: {0}")]
255 DeviceError(ErrorContext),
256
257 #[error("Mutex error: {0}")]
259 MutexError(ErrorContext),
260
261 #[error("Thread error: {0}")]
263 ThreadError(ErrorContext),
264
265 #[error("Stream error: {0}")]
267 StreamError(ErrorContext),
268
269 #[error("End of stream: {0}")]
271 EndOfStream(ErrorContext),
272
273 #[error("Resource error: {0}")]
275 ResourceError(ErrorContext),
276
277 #[error("Communication error: {0}")]
279 CommunicationError(ErrorContext),
280
281 #[error("Security error: {0}")]
283 SecurityError(ErrorContext),
284}
285
286pub type CoreResult<T> = Result<T, CoreError>;
288
289impl From<std::io::Error> for CoreError {
291 fn from(err: std::io::Error) -> Self {
292 CoreError::IoError(ErrorContext::new(format!("IO error: {err}")))
293 }
294}
295
296#[cfg(feature = "serialization")]
298impl From<serde_json::Error> for CoreError {
299 fn from(err: serde_json::Error) -> Self {
300 CoreError::JSONError(ErrorContext::new(format!("JSON error: {err}")))
301 }
302}
303
304impl From<String> for CoreError {
306 fn from(err: String) -> Self {
307 CoreError::ValueError(ErrorContext::new(err))
308 }
309}
310
311impl From<crate::array_protocol::OperationError> for CoreError {
313 fn from(err: crate::array_protocol::OperationError) -> Self {
314 use crate::array_protocol::OperationError;
315 match err {
316 OperationError::NotImplemented(msg) => {
319 CoreError::NotImplementedError(ErrorContext::new(msg))
320 }
321 OperationError::ShapeMismatch(msg) => CoreError::ShapeError(ErrorContext::new(msg)),
322 OperationError::TypeMismatch(msg) => CoreError::TypeError(ErrorContext::new(msg)),
323 OperationError::Other(msg) => CoreError::ComputationError(ErrorContext::new(msg)),
324 }
325 }
326}
327
328#[macro_export]
345macro_rules! error_context {
346 ($message:expr) => {
347 $crate::error::ErrorContext::new($message)
348 .with_location($crate::error::ErrorLocation::new(file!(), line!()))
349 };
350 ($message:expr, $function:expr) => {
351 $crate::error::ErrorContext::new($message).with_location(
352 $crate::error::ErrorLocation::new_with_function(file!(), line!(), $function),
353 )
354 };
355}
356
357#[macro_export]
359macro_rules! domainerror {
360 ($message:expr) => {
361 $crate::error::CoreError::DomainError(error_context!($message))
362 };
363 ($message:expr, $function:expr) => {
364 $crate::error::CoreError::DomainError(error_context!($message, $function))
365 };
366}
367
368#[macro_export]
370macro_rules! dimensionerror {
371 ($message:expr) => {
372 $crate::error::CoreError::DimensionError(error_context!($message))
373 };
374 ($message:expr, $function:expr) => {
375 $crate::error::CoreError::DimensionError(error_context!($message, $function))
376 };
377}
378
379#[macro_export]
381macro_rules! valueerror {
382 ($message:expr) => {
383 $crate::error::CoreError::ValueError(error_context!($message))
384 };
385 ($message:expr, $function:expr) => {
386 $crate::error::CoreError::ValueError(error_context!($message, $function))
387 };
388}
389
390#[macro_export]
392macro_rules! computationerror {
393 ($message:expr) => {
394 $crate::error::CoreError::ComputationError(error_context!($message))
395 };
396 ($message:expr, $function:expr) => {
397 $crate::error::CoreError::ComputationError(error_context!($message, $function))
398 };
399}
400
401#[allow(dead_code)]
417pub fn check_domain<S: Into<String>>(condition: bool, message: S) -> CoreResult<()> {
418 if condition {
419 Ok(())
420 } else {
421 Err(CoreError::DomainError(
422 ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
423 ))
424 }
425}
426
427#[allow(dead_code)]
443pub fn check_dimensions<S: Into<String>>(condition: bool, message: S) -> CoreResult<()> {
444 if condition {
445 Ok(())
446 } else {
447 Err(CoreError::DimensionError(
448 ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
449 ))
450 }
451}
452
453#[allow(dead_code)]
469pub fn check_value<S: Into<String>>(condition: bool, message: S) -> CoreResult<()> {
470 if condition {
471 Ok(())
472 } else {
473 Err(CoreError::ValueError(
474 ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
475 ))
476 }
477}
478
479#[allow(dead_code)]
496pub fn validate<T, F, S>(value: T, validator: F, message: S) -> CoreResult<T>
497where
498 F: FnOnce(&T) -> bool,
499 S: Into<String>,
500{
501 if validator(&value) {
502 Ok(value)
503 } else {
504 Err(CoreError::ValidationError(
505 ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
506 ))
507 }
508}
509
510#[must_use]
521#[allow(dead_code)]
522pub fn converterror<E, S>(error: E, message: S) -> CoreError
523where
524 E: std::error::Error + 'static,
525 S: Into<String>,
526{
527 let message_str = message.into();
530 let error_message = format!("{message_str} | Original error: {error}");
531
532 CoreError::ComputationError(
539 ErrorContext::new(error_message).with_location(ErrorLocation::new(file!(), line!())),
540 )
541}
542
543#[must_use]
554#[allow(dead_code)]
555pub fn chainerror<S>(error: CoreError, message: S) -> CoreError
556where
557 S: Into<String>,
558{
559 CoreError::ComputationError(
560 ErrorContext::new(message)
561 .with_location(ErrorLocation::new(file!(), line!()))
562 .with_cause(error),
563 )
564}