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}
565
566#[derive(Debug, Clone, Copy, PartialEq, Eq)]
568pub enum ErrorRecoveryStrategy {
569 FailFast,
571 RetryExponential,
573 RetryLinear,
575 Fallback,
577 Skip,
579 UseDefault,
581 LogAndContinue,
583}
584
585#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
587pub enum ErrorSeverity {
588 Low,
590 Medium,
592 High,
594 Critical,
596}
597
598#[derive(Debug, Clone)]
600pub struct ErrorHandlerConfig {
601 pub max_retries: usize,
603 pub initial_delay_ms: u64,
605 pub max_delay_ms: u64,
607 pub log_errors: bool,
609 pub recovery_strategy: ErrorRecoveryStrategy,
611 pub min_severity: ErrorSeverity,
613}
614
615impl Default for ErrorHandlerConfig {
616 fn default() -> Self {
617 Self {
618 max_retries: 3,
619 initial_delay_ms: 100,
620 max_delay_ms: 5000,
621 log_errors: true,
622 recovery_strategy: ErrorRecoveryStrategy::FailFast,
623 min_severity: ErrorSeverity::Low,
624 }
625 }
626}
627
628#[derive(Debug, Clone)]
630pub struct EnhancedError {
631 pub error: CoreError,
633 pub severity: ErrorSeverity,
635 pub recovery_strategy: ErrorRecoveryStrategy,
637 pub timestamp: std::time::SystemTime,
639 pub category: Option<String>,
641 pub tags: Vec<String>,
643}
644
645impl EnhancedError {
646 pub fn new(error: CoreError, severity: ErrorSeverity) -> Self {
648 Self {
649 error,
650 severity,
651 recovery_strategy: ErrorRecoveryStrategy::FailFast,
652 timestamp: std::time::SystemTime::now(),
653 category: None,
654 tags: Vec::new(),
655 }
656 }
657
658 pub fn with_recovery(mut self, strategy: ErrorRecoveryStrategy) -> Self {
660 self.recovery_strategy = strategy;
661 self
662 }
663
664 pub fn with_category(mut self, category: impl Into<String>) -> Self {
666 self.category = Some(category.into());
667 self
668 }
669
670 pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
672 self.tags.push(tag.into());
673 self
674 }
675
676 pub fn has_tag(&self, tag: &str) -> bool {
678 self.tags.iter().any(|t| t == tag)
679 }
680
681 pub fn is_retryable(&self) -> bool {
683 matches!(
684 self.recovery_strategy,
685 ErrorRecoveryStrategy::RetryExponential | ErrorRecoveryStrategy::RetryLinear
686 )
687 }
688
689 pub fn is_recoverable(&self) -> bool {
691 !matches!(self.recovery_strategy, ErrorRecoveryStrategy::FailFast)
692 }
693}
694
695impl fmt::Display for EnhancedError {
696 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
697 write!(f, "[{:?}] {}", self.severity, self.error)?;
698 if let Some(category) = &self.category {
699 write!(f, " (category: {category})")?;
700 }
701 if !self.tags.is_empty() {
702 write!(f, " [tags: {}]", self.tags.join(", "))?;
703 }
704 Ok(())
705 }
706}
707
708#[derive(Debug, Clone)]
710pub struct BatchError {
711 pub errors: Vec<EnhancedError>,
713 pub total_operations: usize,
715 pub successful_operations: usize,
717}
718
719impl BatchError {
720 pub fn new(total_operations: usize) -> Self {
722 Self {
723 errors: Vec::new(),
724 total_operations,
725 successful_operations: 0,
726 }
727 }
728
729 pub fn add_error(&mut self, error: EnhancedError) {
731 self.errors.push(error);
732 }
733
734 pub fn add_success(&mut self) {
736 self.successful_operations += 1;
737 }
738
739 pub fn error_rate(&self) -> f64 {
741 if self.total_operations == 0 {
742 0.0
743 } else {
744 self.errors.len() as f64 / self.total_operations as f64
745 }
746 }
747
748 pub fn success_rate(&self) -> f64 {
750 if self.total_operations == 0 {
751 0.0
752 } else {
753 self.successful_operations as f64 / self.total_operations as f64
754 }
755 }
756
757 pub fn is_successful(&self, min_success_rate: f64) -> bool {
759 self.success_rate() >= min_success_rate
760 }
761
762 pub fn errors_by_severity(&self, severity: ErrorSeverity) -> Vec<&EnhancedError> {
764 self.errors
765 .iter()
766 .filter(|e| e.severity == severity)
767 .collect()
768 }
769
770 pub fn errors_by_category(&self, category: &str) -> Vec<&EnhancedError> {
772 self.errors
773 .iter()
774 .filter(|e| e.category.as_deref() == Some(category))
775 .collect()
776 }
777
778 pub fn retryable_errors(&self) -> Vec<&EnhancedError> {
780 self.errors.iter().filter(|e| e.is_retryable()).collect()
781 }
782}
783
784impl fmt::Display for BatchError {
785 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
786 write!(
787 f,
788 "Batch operation: {}/{} successful ({:.1}% success rate)",
789 self.successful_operations,
790 self.total_operations,
791 self.success_rate() * 100.0
792 )?;
793 if !self.errors.is_empty() {
794 write!(f, "\nErrors ({})", self.errors.len())?;
795 for (i, error) in self.errors.iter().enumerate() {
796 write!(f, "\n {}: {}", i + 1, error)?;
797 }
798 }
799 Ok(())
800 }
801}
802
803pub type EnhancedResult<T> = Result<T, EnhancedError>;
805
806pub type BatchResult<T> = Result<T, BatchError>;
808
809pub fn enhance_error(error: CoreError, severity: ErrorSeverity) -> EnhancedError {
811 EnhancedError::new(error, severity)
812}
813
814pub fn critical_error(error: CoreError) -> EnhancedError {
816 EnhancedError::new(error, ErrorSeverity::Critical)
817}
818
819pub fn high_error(error: CoreError) -> EnhancedError {
821 EnhancedError::new(error, ErrorSeverity::High)
822}
823
824pub fn medium_error(error: CoreError) -> EnhancedError {
826 EnhancedError::new(error, ErrorSeverity::Medium)
827}
828
829pub fn low_error(error: CoreError) -> EnhancedError {
831 EnhancedError::new(error, ErrorSeverity::Low)
832}
833
834#[macro_export]
836macro_rules! enhanced_error {
837 (critical, $error:expr) => {
838 $crate::error::critical_error($error)
839 };
840 (high, $error:expr) => {
841 $crate::error::high_error($error)
842 };
843 (medium, $error:expr) => {
844 $crate::error::medium_error($error)
845 };
846 (low, $error:expr) => {
847 $crate::error::low_error($error)
848 };
849 ($error:expr) => {
850 $crate::error::medium_error($error)
851 };
852}
853
854#[macro_export]
856macro_rules! batch_operation {
857 ($total:expr, $operations:block) => {{
858 let mut batch_error = $crate::error::BatchError::new($total);
859 let result = $operations;
860 if batch_error.errors.is_empty() {
861 Ok(result)
862 } else {
863 Err(batch_error)
864 }
865 }};
866}
867
868#[macro_export]
870macro_rules! with_retry {
871 ($operation:expr, $max_retries:expr) => {{
872 let mut attempts = 0;
873 let mut last_error = None;
874
875 loop {
876 match $operation {
877 Ok(result) => break Ok(result),
878 Err(error) => {
879 attempts += 1;
880 last_error = Some(error);
881
882 if attempts >= $max_retries {
883 break Err(last_error.unwrap());
884 }
885
886 std::thread::sleep(std::time::Duration::from_millis(attempts * 100));
888 }
889 }
890 }
891 }};
892}