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)]
153#[non_exhaustive]
154pub enum CoreError {
155 #[error("{0}")]
157 ComputationError(ErrorContext),
158
159 #[error("{0}")]
161 DomainError(ErrorContext),
162
163 #[error("{0}")]
165 DispatchError(ErrorContext),
166
167 #[error("{0}")]
169 ConvergenceError(ErrorContext),
170
171 #[error("{0}")]
173 DimensionError(ErrorContext),
174
175 #[error("{0}")]
177 ShapeError(ErrorContext),
178
179 #[error("{0}")]
181 IndexError(ErrorContext),
182
183 #[error("{0}")]
185 ValueError(ErrorContext),
186
187 #[error("{0}")]
189 TypeError(ErrorContext),
190
191 #[error("{0}")]
193 NotImplementedError(ErrorContext),
194
195 #[error("{0}")]
197 ImplementationError(ErrorContext),
198
199 #[error("{0}")]
201 MemoryError(ErrorContext),
202
203 #[error("{0}")]
205 AllocationError(ErrorContext),
206
207 #[error("{0}")]
209 ConfigError(ErrorContext),
210
211 #[error("{0}")]
213 InvalidArgument(ErrorContext),
214
215 #[error("{0}")]
217 InvalidInput(ErrorContext),
218
219 #[error("{0}")]
221 PermissionError(ErrorContext),
222
223 #[error("{0}")]
225 ValidationError(ErrorContext),
226
227 #[error("{0}")]
229 InvalidState(ErrorContext),
230
231 #[error("{0}")]
233 JITError(ErrorContext),
234
235 #[error("JSON error: {0}")]
237 JSONError(ErrorContext),
238
239 #[error("IO error: {0}")]
241 IoError(ErrorContext),
242
243 #[error("Scheduler error: {0}")]
245 SchedulerError(ErrorContext),
246
247 #[error("Timeout error: {0}")]
249 TimeoutError(ErrorContext),
250
251 #[error("Compression error: {0}")]
253 CompressionError(ErrorContext),
254
255 #[error("Invalid shape: {0}")]
257 InvalidShape(ErrorContext),
258
259 #[error("Device error: {0}")]
261 DeviceError(ErrorContext),
262
263 #[error("Mutex error: {0}")]
265 MutexError(ErrorContext),
266
267 #[error("Thread error: {0}")]
269 ThreadError(ErrorContext),
270
271 #[error("Stream error: {0}")]
273 StreamError(ErrorContext),
274
275 #[error("End of stream: {0}")]
277 EndOfStream(ErrorContext),
278
279 #[error("Resource error: {0}")]
281 ResourceError(ErrorContext),
282
283 #[error("Communication error: {0}")]
285 CommunicationError(ErrorContext),
286
287 #[error("Security error: {0}")]
289 SecurityError(ErrorContext),
290}
291
292pub type CoreResult<T> = Result<T, CoreError>;
294
295impl From<std::io::Error> for CoreError {
297 fn from(err: std::io::Error) -> Self {
298 CoreError::IoError(ErrorContext::new(format!("IO error: {err}")))
299 }
300}
301
302#[cfg(feature = "serialization")]
304impl From<serde_json::Error> for CoreError {
305 fn from(err: serde_json::Error) -> Self {
306 CoreError::JSONError(ErrorContext::new(format!("JSON error: {err}")))
307 }
308}
309
310impl From<String> for CoreError {
312 fn from(err: String) -> Self {
313 CoreError::ValueError(ErrorContext::new(err))
314 }
315}
316
317impl From<crate::array_protocol::OperationError> for CoreError {
319 fn from(err: crate::array_protocol::OperationError) -> Self {
320 use crate::array_protocol::OperationError;
321 match err {
322 OperationError::NotImplemented(msg) => {
325 CoreError::NotImplementedError(ErrorContext::new(msg))
326 }
327 OperationError::ShapeMismatch(msg) => CoreError::ShapeError(ErrorContext::new(msg)),
328 OperationError::TypeMismatch(msg) => CoreError::TypeError(ErrorContext::new(msg)),
329 OperationError::Other(msg) => CoreError::ComputationError(ErrorContext::new(msg)),
330 }
331 }
332}
333
334#[macro_export]
351macro_rules! error_context {
352 ($message:expr) => {
353 $crate::error::ErrorContext::new($message)
354 .with_location($crate::error::ErrorLocation::new(file!(), line!()))
355 };
356 ($message:expr, $function:expr) => {
357 $crate::error::ErrorContext::new($message).with_location(
358 $crate::error::ErrorLocation::new_with_function(file!(), line!(), $function),
359 )
360 };
361}
362
363#[macro_export]
365macro_rules! domainerror {
366 ($message:expr) => {
367 $crate::error::CoreError::DomainError(error_context!($message))
368 };
369 ($message:expr, $function:expr) => {
370 $crate::error::CoreError::DomainError(error_context!($message, $function))
371 };
372}
373
374#[macro_export]
376macro_rules! dimensionerror {
377 ($message:expr) => {
378 $crate::error::CoreError::DimensionError(error_context!($message))
379 };
380 ($message:expr, $function:expr) => {
381 $crate::error::CoreError::DimensionError(error_context!($message, $function))
382 };
383}
384
385#[macro_export]
387macro_rules! valueerror {
388 ($message:expr) => {
389 $crate::error::CoreError::ValueError(error_context!($message))
390 };
391 ($message:expr, $function:expr) => {
392 $crate::error::CoreError::ValueError(error_context!($message, $function))
393 };
394}
395
396#[macro_export]
398macro_rules! computationerror {
399 ($message:expr) => {
400 $crate::error::CoreError::ComputationError(error_context!($message))
401 };
402 ($message:expr, $function:expr) => {
403 $crate::error::CoreError::ComputationError(error_context!($message, $function))
404 };
405}
406
407#[allow(dead_code)]
423pub fn check_domain<S: Into<String>>(condition: bool, message: S) -> CoreResult<()> {
424 if condition {
425 Ok(())
426 } else {
427 Err(CoreError::DomainError(
428 ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
429 ))
430 }
431}
432
433#[allow(dead_code)]
449pub fn check_dimensions<S: Into<String>>(condition: bool, message: S) -> CoreResult<()> {
450 if condition {
451 Ok(())
452 } else {
453 Err(CoreError::DimensionError(
454 ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
455 ))
456 }
457}
458
459#[allow(dead_code)]
475pub fn check_value<S: Into<String>>(condition: bool, message: S) -> CoreResult<()> {
476 if condition {
477 Ok(())
478 } else {
479 Err(CoreError::ValueError(
480 ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
481 ))
482 }
483}
484
485#[allow(dead_code)]
502pub fn validate<T, F, S>(value: T, validator: F, message: S) -> CoreResult<T>
503where
504 F: FnOnce(&T) -> bool,
505 S: Into<String>,
506{
507 if validator(&value) {
508 Ok(value)
509 } else {
510 Err(CoreError::ValidationError(
511 ErrorContext::new(message).with_location(ErrorLocation::new(file!(), line!())),
512 ))
513 }
514}
515
516#[must_use]
527#[allow(dead_code)]
528pub fn converterror<E, S>(error: E, message: S) -> CoreError
529where
530 E: std::error::Error + 'static,
531 S: Into<String>,
532{
533 let message_str = message.into();
536 let error_message = format!("{message_str} | Original error: {error}");
537
538 CoreError::ComputationError(
545 ErrorContext::new(error_message).with_location(ErrorLocation::new(file!(), line!())),
546 )
547}
548
549#[must_use]
560#[allow(dead_code)]
561pub fn chainerror<S>(error: CoreError, message: S) -> CoreError
562where
563 S: Into<String>,
564{
565 CoreError::ComputationError(
566 ErrorContext::new(message)
567 .with_location(ErrorLocation::new(file!(), line!()))
568 .with_cause(error),
569 )
570}
571
572#[derive(Debug, Clone, Copy, PartialEq, Eq)]
574pub enum ErrorRecoveryStrategy {
575 FailFast,
577 RetryExponential,
579 RetryLinear,
581 Fallback,
583 Skip,
585 UseDefault,
587 LogAndContinue,
589}
590
591#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
593pub enum ErrorSeverity {
594 Low,
596 Medium,
598 High,
600 Critical,
602}
603
604#[derive(Debug, Clone)]
606pub struct ErrorHandlerConfig {
607 pub max_retries: usize,
609 pub initial_delay_ms: u64,
611 pub max_delay_ms: u64,
613 pub log_errors: bool,
615 pub recovery_strategy: ErrorRecoveryStrategy,
617 pub min_severity: ErrorSeverity,
619}
620
621impl Default for ErrorHandlerConfig {
622 fn default() -> Self {
623 Self {
624 max_retries: 3,
625 initial_delay_ms: 100,
626 max_delay_ms: 5000,
627 log_errors: true,
628 recovery_strategy: ErrorRecoveryStrategy::FailFast,
629 min_severity: ErrorSeverity::Low,
630 }
631 }
632}
633
634#[derive(Debug, Clone)]
636pub struct EnhancedError {
637 pub error: CoreError,
639 pub severity: ErrorSeverity,
641 pub recovery_strategy: ErrorRecoveryStrategy,
643 pub timestamp: std::time::SystemTime,
645 pub category: Option<String>,
647 pub tags: Vec<String>,
649}
650
651impl EnhancedError {
652 pub fn new(error: CoreError, severity: ErrorSeverity) -> Self {
654 Self {
655 error,
656 severity,
657 recovery_strategy: ErrorRecoveryStrategy::FailFast,
658 timestamp: std::time::SystemTime::now(),
659 category: None,
660 tags: Vec::new(),
661 }
662 }
663
664 pub fn with_recovery(mut self, strategy: ErrorRecoveryStrategy) -> Self {
666 self.recovery_strategy = strategy;
667 self
668 }
669
670 pub fn with_category(mut self, category: impl Into<String>) -> Self {
672 self.category = Some(category.into());
673 self
674 }
675
676 pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
678 self.tags.push(tag.into());
679 self
680 }
681
682 pub fn has_tag(&self, tag: &str) -> bool {
684 self.tags.iter().any(|t| t == tag)
685 }
686
687 pub fn is_retryable(&self) -> bool {
689 matches!(
690 self.recovery_strategy,
691 ErrorRecoveryStrategy::RetryExponential | ErrorRecoveryStrategy::RetryLinear
692 )
693 }
694
695 pub fn is_recoverable(&self) -> bool {
697 !matches!(self.recovery_strategy, ErrorRecoveryStrategy::FailFast)
698 }
699}
700
701impl fmt::Display for EnhancedError {
702 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
703 write!(f, "[{:?}] {}", self.severity, self.error)?;
704 if let Some(category) = &self.category {
705 write!(f, " (category: {category})")?;
706 }
707 if !self.tags.is_empty() {
708 write!(f, " [tags: {}]", self.tags.join(", "))?;
709 }
710 Ok(())
711 }
712}
713
714#[derive(Debug, Clone)]
716pub struct BatchError {
717 pub errors: Vec<EnhancedError>,
719 pub total_operations: usize,
721 pub successful_operations: usize,
723}
724
725impl BatchError {
726 pub fn new(total_operations: usize) -> Self {
728 Self {
729 errors: Vec::new(),
730 total_operations,
731 successful_operations: 0,
732 }
733 }
734
735 pub fn add_error(&mut self, error: EnhancedError) {
737 self.errors.push(error);
738 }
739
740 pub fn add_success(&mut self) {
742 self.successful_operations += 1;
743 }
744
745 pub fn error_rate(&self) -> f64 {
747 if self.total_operations == 0 {
748 0.0
749 } else {
750 self.errors.len() as f64 / self.total_operations as f64
751 }
752 }
753
754 pub fn success_rate(&self) -> f64 {
756 if self.total_operations == 0 {
757 0.0
758 } else {
759 self.successful_operations as f64 / self.total_operations as f64
760 }
761 }
762
763 pub fn is_successful(&self, min_success_rate: f64) -> bool {
765 self.success_rate() >= min_success_rate
766 }
767
768 pub fn errors_by_severity(&self, severity: ErrorSeverity) -> Vec<&EnhancedError> {
770 self.errors
771 .iter()
772 .filter(|e| e.severity == severity)
773 .collect()
774 }
775
776 pub fn errors_by_category(&self, category: &str) -> Vec<&EnhancedError> {
778 self.errors
779 .iter()
780 .filter(|e| e.category.as_deref() == Some(category))
781 .collect()
782 }
783
784 pub fn retryable_errors(&self) -> Vec<&EnhancedError> {
786 self.errors.iter().filter(|e| e.is_retryable()).collect()
787 }
788}
789
790impl fmt::Display for BatchError {
791 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
792 write!(
793 f,
794 "Batch operation: {}/{} successful ({:.1}% success rate)",
795 self.successful_operations,
796 self.total_operations,
797 self.success_rate() * 100.0
798 )?;
799 if !self.errors.is_empty() {
800 write!(f, "\nErrors ({})", self.errors.len())?;
801 for (i, error) in self.errors.iter().enumerate() {
802 write!(f, "\n {}: {}", i + 1, error)?;
803 }
804 }
805 Ok(())
806 }
807}
808
809pub type EnhancedResult<T> = Result<T, EnhancedError>;
811
812pub type BatchResult<T> = Result<T, BatchError>;
814
815pub fn enhance_error(error: CoreError, severity: ErrorSeverity) -> EnhancedError {
817 EnhancedError::new(error, severity)
818}
819
820pub fn critical_error(error: CoreError) -> EnhancedError {
822 EnhancedError::new(error, ErrorSeverity::Critical)
823}
824
825pub fn high_error(error: CoreError) -> EnhancedError {
827 EnhancedError::new(error, ErrorSeverity::High)
828}
829
830pub fn medium_error(error: CoreError) -> EnhancedError {
832 EnhancedError::new(error, ErrorSeverity::Medium)
833}
834
835pub fn low_error(error: CoreError) -> EnhancedError {
837 EnhancedError::new(error, ErrorSeverity::Low)
838}
839
840#[macro_export]
842macro_rules! enhanced_error {
843 (critical, $error:expr) => {
844 $crate::error::critical_error($error)
845 };
846 (high, $error:expr) => {
847 $crate::error::high_error($error)
848 };
849 (medium, $error:expr) => {
850 $crate::error::medium_error($error)
851 };
852 (low, $error:expr) => {
853 $crate::error::low_error($error)
854 };
855 ($error:expr) => {
856 $crate::error::medium_error($error)
857 };
858}
859
860#[macro_export]
862macro_rules! batch_operation {
863 ($total:expr, $operations:block) => {{
864 let mut batch_error = $crate::error::BatchError::new($total);
865 let result = $operations;
866 if batch_error.errors.is_empty() {
867 Ok(result)
868 } else {
869 Err(batch_error)
870 }
871 }};
872}
873
874#[macro_export]
876macro_rules! with_retry {
877 ($operation:expr, $max_retries:expr) => {{
878 let mut attempts = 0;
879 let mut last_error = None;
880
881 loop {
882 match $operation {
883 Ok(result) => break Ok(result),
884 Err(error) => {
885 attempts += 1;
886 last_error = Some(error);
887
888 if attempts >= $max_retries {
889 break Err(last_error.expect("Operation failed"));
890 }
891
892 std::thread::sleep(std::time::Duration::from_millis(attempts * 100));
894 }
895 }
896 }
897 }};
898}