1use std::fmt;
10use std::sync::Arc;
11use std::time::SystemTime;
12
13#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
15pub struct ErrorContext {
16 pub timestamp: SystemTime,
18 pub file: Option<String>,
20 pub line: Option<u32>,
22 pub extra: Option<String>,
24}
25
26impl Default for ErrorContext {
27 fn default() -> Self {
28 Self {
29 timestamp: SystemTime::now(),
30 file: None,
31 line: None,
32 extra: None,
33 }
34 }
35}
36
37impl ErrorContext {
38 pub fn new() -> Self {
40 Self::default()
41 }
42
43 pub fn with_location(file: impl Into<String>, line: u32) -> Self {
45 Self {
46 timestamp: SystemTime::now(),
47 file: Some(file.into()),
48 line: Some(line),
49 extra: None,
50 }
51 }
52
53 pub fn with_extra(mut self, extra: impl Into<String>) -> Self {
55 self.extra = Some(extra.into());
56 self
57 }
58}
59
60#[derive(Debug, Clone)]
65pub enum MemScopeError {
66 Memory {
68 operation: MemoryOperation,
69 message: Arc<str>,
70 context: Option<Arc<str>>,
71 error_kind: ErrorKind,
72 error_context: ErrorContext,
73 },
74
75 Analysis {
77 analyzer: Arc<str>,
78 message: Arc<str>,
79 recoverable: bool,
80 error_kind: ErrorKind,
81 error_context: ErrorContext,
82 },
83
84 Export {
86 format: Arc<str>,
87 message: Arc<str>,
88 partial_success: bool,
89 error_kind: ErrorKind,
90 error_context: ErrorContext,
91 },
92
93 Configuration {
95 component: Arc<str>,
96 message: Arc<str>,
97 error_kind: ErrorKind,
98 error_context: ErrorContext,
99 },
100
101 System {
103 error_type: SystemErrorType,
104 message: Arc<str>,
105 source_message: Option<Arc<str>>,
106 error_kind: ErrorKind,
107 error_context: ErrorContext,
108 },
109
110 Internal {
112 message: Arc<str>,
113 location: Option<Arc<str>>,
114 error_kind: ErrorKind,
115 error_context: ErrorContext,
116 },
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub enum MemoryOperation {
122 Allocation,
123 Deallocation,
124 Association,
125 Tracking,
126 Validation,
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131pub enum SystemErrorType {
132 Io,
133 Threading,
134 Locking,
135 Channel,
136 Serialization,
137 Network,
138 FileSystem,
139}
140
141pub type Result<T> = std::result::Result<T, MemScopeError>;
143
144pub type MemScopeResult<T> = std::result::Result<T, MemScopeError>;
146
147impl MemScopeError {
148 pub fn error(module: &str, method: &str, message: impl Into<Arc<str>>) -> Self {
162 let kind = match module {
163 "tracker" | "allocator" | "variable_registry" | "memory_tracker" | "capture"
165 | "global_tracking" | "async_tracker" | "unified_tracker" | "core_tracker"
166 | "backends" | "platform" | "stack_walker" | "symbol_resolver" | "memory_info"
167 | "async_types" => ErrorKind::MemoryError,
168
169 "export" | "render_engine" | "snapshot" => ErrorKind::ExportError,
171
172 "ffi_function_resolver"
174 | "call_stack_normalizer"
175 | "analysis"
176 | "lifecycle"
177 | "lifecycle_analysis"
178 | "rule_engine"
179 | "pattern_matcher"
180 | "detector"
181 | "unsafe_ffi_tracker"
182 | "classification"
183 | "estimation"
184 | "metrics"
185 | "quality"
186 | "circular_reference"
187 | "memory_passport_tracker"
188 | "borrow_analysis"
189 | "async_analysis" => ErrorKind::AnalysisError,
190
191 "config" | "initialization" => ErrorKind::ConfigurationError,
193
194 "system" | "io" | "threading" | "locking" | "network" | "filesystem"
196 | "serialization" => ErrorKind::IoError,
197
198 _ => ErrorKind::InternalError,
200 };
201 let full_message = format!("{}::{} - {}", module, method, message.into());
202 Self::new(kind, full_message)
203 }
204
205 pub fn new(kind: ErrorKind, message: impl Into<Arc<str>>) -> Self {
207 match kind {
208 ErrorKind::MemoryError => Self::Memory {
209 operation: MemoryOperation::Tracking,
210 message: message.into(),
211 context: None,
212 error_kind: kind,
213 error_context: ErrorContext::new(),
214 },
215 ErrorKind::ValidationError => Self::Memory {
216 operation: MemoryOperation::Validation,
217 message: message.into(),
218 context: None,
219 error_kind: kind,
220 error_context: ErrorContext::new(),
221 },
222 ErrorKind::ConfigurationError => Self::Configuration {
223 component: "general".into(),
224 message: message.into(),
225 error_kind: kind,
226 error_context: ErrorContext::new(),
227 },
228 ErrorKind::IoError | ErrorKind::CacheError => Self::System {
229 error_type: SystemErrorType::Io,
230 message: message.into(),
231 source_message: None,
232 error_kind: kind,
233 error_context: ErrorContext::new(),
234 },
235 ErrorKind::SymbolResolutionError
236 | ErrorKind::StackTraceError
237 | ErrorKind::AnalysisError => Self::Analysis {
238 analyzer: match kind {
239 ErrorKind::SymbolResolutionError => "symbol_resolution",
240 ErrorKind::StackTraceError => "stack_trace",
241 _ => "general",
242 }
243 .into(),
244 message: message.into(),
245 recoverable: true,
246 error_kind: kind,
247 error_context: ErrorContext::new(),
248 },
249 ErrorKind::ExportError => Self::Export {
250 format: "general".into(),
251 message: message.into(),
252 partial_success: false,
253 error_kind: kind,
254 error_context: ErrorContext::new(),
255 },
256 ErrorKind::InternalError => Self::Internal {
257 message: message.into(),
258 location: None,
259 error_kind: kind,
260 error_context: ErrorContext::new(),
261 },
262 }
263 }
264
265 pub fn with_context(
267 kind: ErrorKind,
268 _severity: ErrorSeverity,
269 message: impl Into<Arc<str>>,
270 context: impl Into<Arc<str>>,
271 ) -> Self {
272 let mut error = Self::new(kind, message);
273 match &mut error {
275 Self::Memory { error_context, .. }
276 | Self::Analysis { error_context, .. }
277 | Self::Export { error_context, .. }
278 | Self::Configuration { error_context, .. }
279 | Self::System { error_context, .. }
280 | Self::Internal { error_context, .. } => {
281 error_context.extra = Some(context.into().to_string());
282 }
283 }
284 error
285 }
286
287 pub fn memory(operation: MemoryOperation, message: impl Into<Arc<str>>) -> Self {
289 Self::Memory {
290 operation,
291 message: message.into(),
292 context: None,
293 error_kind: ErrorKind::MemoryError,
294 error_context: ErrorContext::new(),
295 }
296 }
297
298 pub fn memory_with_context(
300 operation: MemoryOperation,
301 message: impl Into<Arc<str>>,
302 context: impl Into<Arc<str>>,
303 ) -> Self {
304 Self::Memory {
305 operation,
306 message: message.into(),
307 context: Some(context.into()),
308 error_kind: ErrorKind::MemoryError,
309 error_context: ErrorContext::new(),
310 }
311 }
312
313 pub fn analysis(analyzer: impl Into<Arc<str>>, message: impl Into<Arc<str>>) -> Self {
315 Self::Analysis {
316 analyzer: analyzer.into(),
317 message: message.into(),
318 recoverable: true,
319 error_kind: ErrorKind::AnalysisError,
320 error_context: ErrorContext::new(),
321 }
322 }
323
324 pub fn analysis_fatal(analyzer: impl Into<Arc<str>>, message: impl Into<Arc<str>>) -> Self {
326 Self::Analysis {
327 analyzer: analyzer.into(),
328 message: message.into(),
329 recoverable: false,
330 error_kind: ErrorKind::AnalysisError,
331 error_context: ErrorContext::new(),
332 }
333 }
334
335 pub fn export(format: impl Into<Arc<str>>, message: impl Into<Arc<str>>) -> Self {
337 Self::Export {
338 format: format.into(),
339 message: message.into(),
340 partial_success: false,
341 error_kind: ErrorKind::ExportError,
342 error_context: ErrorContext::new(),
343 }
344 }
345
346 pub fn export_partial(format: impl Into<Arc<str>>, message: impl Into<Arc<str>>) -> Self {
348 Self::Export {
349 format: format.into(),
350 message: message.into(),
351 partial_success: true,
352 error_kind: ErrorKind::ExportError,
353 error_context: ErrorContext::new(),
354 }
355 }
356
357 pub fn export_with_source(
359 format: impl Into<Arc<str>>,
360 message: impl Into<Arc<str>>,
361 source: impl AsRef<str>,
362 ) -> Self {
363 Self::Export {
364 format: format.into(),
365 message: format!("{} (source: {})", message.into(), source.as_ref()).into(),
366 partial_success: false,
367 error_kind: ErrorKind::ExportError,
368 error_context: ErrorContext::new(),
369 }
370 }
371
372 pub fn config(component: impl Into<Arc<str>>, message: impl Into<Arc<str>>) -> Self {
374 Self::Configuration {
375 component: component.into(),
376 message: message.into(),
377 error_kind: ErrorKind::ConfigurationError,
378 error_context: ErrorContext::new(),
379 }
380 }
381
382 pub fn system(error_type: SystemErrorType, message: impl Into<Arc<str>>) -> Self {
384 Self::System {
385 error_type,
386 message: message.into(),
387 source_message: None,
388 error_kind: ErrorKind::IoError,
389 error_context: ErrorContext::new(),
390 }
391 }
392
393 pub fn system_with_source(
395 error_type: SystemErrorType,
396 message: impl Into<Arc<str>>,
397 source: impl std::error::Error,
398 ) -> Self {
399 Self::System {
400 error_type,
401 message: message.into(),
402 source_message: Some(Arc::from(source.to_string())),
403 error_kind: ErrorKind::IoError,
404 error_context: ErrorContext::new(),
405 }
406 }
407
408 pub fn internal(message: impl Into<Arc<str>>) -> Self {
410 Self::Internal {
411 message: message.into(),
412 location: None,
413 error_kind: ErrorKind::InternalError,
414 error_context: ErrorContext::new(),
415 }
416 }
417
418 pub fn internal_at(message: impl Into<Arc<str>>, location: impl Into<Arc<str>>) -> Self {
420 Self::Internal {
421 message: message.into(),
422 location: Some(location.into()),
423 error_kind: ErrorKind::InternalError,
424 error_context: ErrorContext::new(),
425 }
426 }
427
428 pub fn is_recoverable(&self) -> bool {
430 match self {
431 Self::Memory { .. } => true,
432 Self::Analysis { recoverable, .. } => *recoverable,
433 Self::Export {
434 partial_success, ..
435 } => *partial_success,
436 Self::Configuration { .. } => false,
437 Self::System { error_type, .. } => matches!(
438 error_type,
439 SystemErrorType::Io | SystemErrorType::Network | SystemErrorType::Locking
440 ),
441 Self::Internal { .. } => false,
442 }
443 }
444
445 pub fn kind(&self) -> ErrorKind {
447 match self {
448 Self::Memory { error_kind, .. } => *error_kind,
449 Self::Analysis { error_kind, .. } => *error_kind,
450 Self::Export { error_kind, .. } => *error_kind,
451 Self::Configuration { error_kind, .. } => *error_kind,
452 Self::System { error_kind, .. } => *error_kind,
453 Self::Internal { error_kind, .. } => *error_kind,
454 }
455 }
456
457 pub fn severity(&self) -> ErrorSeverity {
459 match self {
460 Self::Memory { .. } => ErrorSeverity::Error,
461 Self::Analysis {
462 recoverable: false, ..
463 } => ErrorSeverity::Critical,
464 Self::Analysis { .. } => ErrorSeverity::Warning,
465 Self::Export {
466 partial_success: true,
467 ..
468 } => ErrorSeverity::Warning,
469 Self::Export { .. } => ErrorSeverity::Error,
470 Self::Configuration { .. } => ErrorSeverity::Error,
471 Self::System { .. } => ErrorSeverity::Error,
472 Self::Internal { .. } => ErrorSeverity::Fatal,
473 }
474 }
475
476 pub fn user_message(&self) -> &str {
478 match self {
479 Self::Memory { message, .. } => message,
480 Self::Analysis { message, .. } => message,
481 Self::Export { message, .. } => message,
482 Self::Configuration { message, .. } => message,
483 Self::System { message, .. } => message,
484 Self::Internal { message, .. } => message,
485 }
486 }
487
488 pub fn category(&self) -> &'static str {
490 match self {
491 Self::Memory { .. } => "memory",
492 Self::Analysis { .. } => "analysis",
493 Self::Export { .. } => "export",
494 Self::Configuration { .. } => "config",
495 Self::System { .. } => "system",
496 Self::Internal { .. } => "internal",
497 }
498 }
499
500 pub fn context(&self) -> &ErrorContext {
502 match self {
503 Self::Memory { error_context, .. } => error_context,
504 Self::Analysis { error_context, .. } => error_context,
505 Self::Export { error_context, .. } => error_context,
506 Self::Configuration { error_context, .. } => error_context,
507 Self::System { error_context, .. } => error_context,
508 Self::Internal { error_context, .. } => error_context,
509 }
510 }
511
512 pub fn description(&self) -> String {
514 format!("{}", self)
515 }
516}
517
518#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
520pub enum ErrorKind {
521 MemoryError,
523 ConfigurationError,
525 IoError,
527 SymbolResolutionError,
529 StackTraceError,
531 CacheError,
533 InternalError,
535 ValidationError,
537 AnalysisError,
539 ExportError,
541}
542
543impl ErrorKind {
544 pub fn default_severity(self) -> ErrorSeverity {
546 match self {
547 ErrorKind::MemoryError => ErrorSeverity::Error,
548 ErrorKind::ConfigurationError => ErrorSeverity::Error,
549 ErrorKind::IoError => ErrorSeverity::Error,
550 ErrorKind::SymbolResolutionError => ErrorSeverity::Warning,
551 ErrorKind::StackTraceError => ErrorSeverity::Warning,
552 ErrorKind::CacheError => ErrorSeverity::Warning,
553 ErrorKind::InternalError => ErrorSeverity::Fatal,
554 ErrorKind::ValidationError => ErrorSeverity::Error,
555 ErrorKind::AnalysisError => ErrorSeverity::Warning,
556 ErrorKind::ExportError => ErrorSeverity::Error,
557 }
558 }
559}
560
561#[derive(
563 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
564)]
565pub enum ErrorSeverity {
566 Warning,
568 Error,
570 Critical,
572 Fatal,
574}
575
576impl fmt::Display for MemScopeError {
577 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
578 match self {
579 Self::Memory {
580 operation,
581 message,
582 context,
583 error_kind: _,
584 error_context: _,
585 } => {
586 write!(
587 f,
588 "Memory {} error: {}",
589 match operation {
590 MemoryOperation::Allocation => "allocation",
591 MemoryOperation::Deallocation => "deallocation",
592 MemoryOperation::Association => "association",
593 MemoryOperation::Tracking => "tracking",
594 MemoryOperation::Validation => "validation",
595 },
596 message
597 )?;
598 if let Some(ctx) = context {
599 write!(f, " (context: {ctx})")?;
600 }
601 Ok(())
602 }
603 Self::Analysis {
604 analyzer, message, ..
605 } => {
606 write!(f, "Analysis error in {analyzer}: {message}")
607 }
608 Self::Export {
609 format,
610 message,
611 partial_success,
612 error_kind: _,
613 error_context: _,
614 } => {
615 if *partial_success {
616 write!(f, "Partial export error ({format}): {message}")
617 } else {
618 write!(f, "Export error ({format}): {message}")
619 }
620 }
621 Self::Configuration {
622 component,
623 message,
624 error_kind: _,
625 error_context: _,
626 } => {
627 write!(f, "Configuration error in {component}: {message}")
628 }
629 Self::System {
630 error_type,
631 message,
632 source_message,
633 error_kind: _,
634 error_context: _,
635 } => {
636 write!(
637 f,
638 "{} error: {}",
639 match error_type {
640 SystemErrorType::Io => "I/O",
641 SystemErrorType::Threading => "Threading",
642 SystemErrorType::Locking => "Locking",
643 SystemErrorType::Channel => "Channel",
644 SystemErrorType::Serialization => "Serialization",
645 SystemErrorType::Network => "Network",
646 SystemErrorType::FileSystem => "File system",
647 },
648 message
649 )?;
650 if let Some(source) = source_message {
651 write!(f, " (source: {source})")?;
652 }
653 Ok(())
654 }
655 Self::Internal {
656 message,
657 location,
658 error_kind: _,
659 error_context: _,
660 } => {
661 write!(f, "Internal error: {message}")?;
662 if let Some(loc) = location {
663 write!(f, " at {loc}")?;
664 }
665 Ok(())
666 }
667 }
668 }
669}
670
671impl std::error::Error for MemScopeError {}
672
673impl From<std::io::Error> for MemScopeError {
675 fn from(err: std::io::Error) -> Self {
676 Self::system_with_source(
677 SystemErrorType::Io,
678 format!("I/O operation failed: {err}"),
679 err,
680 )
681 }
682}
683
684impl From<serde_json::Error> for MemScopeError {
685 fn from(err: serde_json::Error) -> Self {
686 Self::system_with_source(
687 SystemErrorType::Serialization,
688 format!("JSON serialization failed: {err}"),
689 err,
690 )
691 }
692}
693
694impl From<crate::render_engine::export::ExportError> for MemScopeError {
695 fn from(err: crate::render_engine::export::ExportError) -> Self {
696 Self::error("export", "unknown", err.to_string())
697 }
698}
699
700#[derive(Debug, Clone)]
702pub enum RecoveryAction {
703 Retry { max_attempts: u32, delay_ms: u64 },
705 UseDefault { value: String },
707 Skip,
709 Abort,
711 Fallback { strategy: String },
713}
714
715pub trait ErrorRecovery {
717 fn can_recover(&self, error: &MemScopeError) -> bool;
719
720 fn get_recovery_action(&self, error: &MemScopeError) -> Option<RecoveryAction>;
722
723 fn execute_recovery(&self, action: &RecoveryAction) -> MemScopeResult<()>;
725}
726
727pub struct DefaultErrorRecovery {
729 max_retries: u32,
730 retry_delay_ms: u64,
731}
732
733impl DefaultErrorRecovery {
734 pub fn new() -> Self {
735 Self {
736 max_retries: 3,
737 retry_delay_ms: 100,
738 }
739 }
740
741 pub fn with_config(max_retries: u32, retry_delay_ms: u64) -> Self {
742 Self {
743 max_retries,
744 retry_delay_ms,
745 }
746 }
747}
748
749impl Default for DefaultErrorRecovery {
750 fn default() -> Self {
751 Self::new()
752 }
753}
754
755impl ErrorRecovery for DefaultErrorRecovery {
756 fn can_recover(&self, error: &MemScopeError) -> bool {
757 error.is_recoverable() && error.severity() != ErrorSeverity::Critical
758 }
759
760 fn get_recovery_action(&self, error: &MemScopeError) -> Option<RecoveryAction> {
761 if !self.can_recover(error) {
762 return Some(RecoveryAction::Abort);
763 }
764
765 match error {
766 MemScopeError::Memory { .. } => Some(RecoveryAction::Retry {
767 max_attempts: self.max_retries,
768 delay_ms: self.retry_delay_ms,
769 }),
770 MemScopeError::Analysis { .. } => Some(RecoveryAction::UseDefault {
771 value: "{}".to_string(), }),
773 MemScopeError::Export {
774 partial_success: true,
775 ..
776 } => Some(RecoveryAction::Skip),
777 MemScopeError::Export { .. } => Some(RecoveryAction::Fallback {
778 strategy: "minimal_export".to_string(),
779 }),
780 MemScopeError::System {
781 error_type: SystemErrorType::Locking,
782 ..
783 } => Some(RecoveryAction::Retry {
784 max_attempts: 1,
785 delay_ms: 50,
786 }),
787 _ => Some(RecoveryAction::Abort),
788 }
789 }
790
791 fn execute_recovery(&self, action: &RecoveryAction) -> MemScopeResult<()> {
792 match action {
793 RecoveryAction::Retry { delay_ms, .. } => {
794 if *delay_ms > 0 {
795 std::thread::sleep(std::time::Duration::from_millis(*delay_ms));
796 }
797 Ok(())
798 }
799 RecoveryAction::UseDefault { .. } => Ok(()),
800 RecoveryAction::Skip => Ok(()),
801 RecoveryAction::Fallback { .. } => Ok(()),
802 RecoveryAction::Abort => Err(MemScopeError::internal("Recovery aborted")),
803 }
804 }
805}
806
807#[cfg(test)]
808mod tests {
809 use super::*;
810 use std::io;
811
812 #[test]
814 fn test_memory_error_creation() {
815 let err = MemScopeError::memory(MemoryOperation::Allocation, "allocation failed");
816 assert!(matches!(
817 err,
818 MemScopeError::Memory {
819 operation: MemoryOperation::Allocation,
820 ..
821 }
822 ));
823 assert_eq!(err.category(), "memory");
824 assert_eq!(err.user_message(), "allocation failed");
825 assert!(err.is_recoverable());
826 assert_eq!(err.severity(), ErrorSeverity::Error);
827
828 let err_with_context = MemScopeError::memory_with_context(
829 MemoryOperation::Deallocation,
830 "deallocation failed",
831 "in cleanup",
832 );
833 assert!(matches!(
834 err_with_context,
835 MemScopeError::Memory {
836 operation: MemoryOperation::Deallocation,
837 ..
838 }
839 ));
840 assert_eq!(err_with_context.category(), "memory");
841 }
842
843 #[test]
844 fn test_analysis_error_creation() {
845 let err = MemScopeError::analysis("fragmentation", "analysis failed");
846 assert!(matches!(
847 err,
848 MemScopeError::Analysis {
849 recoverable: true,
850 ..
851 }
852 ));
853 assert_eq!(err.category(), "analysis");
854 assert!(err.is_recoverable());
855 assert_eq!(err.severity(), ErrorSeverity::Warning);
856
857 let fatal_err = MemScopeError::analysis_fatal("lifecycle", "critical analysis failed");
858 assert!(matches!(
859 fatal_err,
860 MemScopeError::Analysis {
861 recoverable: false,
862 ..
863 }
864 ));
865 assert_eq!(fatal_err.category(), "analysis");
866 assert!(!fatal_err.is_recoverable());
867 assert_eq!(fatal_err.severity(), ErrorSeverity::Critical);
868 }
869
870 #[test]
871 fn test_export_error_creation() {
872 let err = MemScopeError::export("json", "export failed");
873 assert!(matches!(
874 err,
875 MemScopeError::Export {
876 partial_success: false,
877 ..
878 }
879 ));
880 assert_eq!(err.category(), "export");
881 assert!(!err.is_recoverable());
882 assert_eq!(err.severity(), ErrorSeverity::Error);
883
884 let partial_err = MemScopeError::export_partial("html", "partial export");
885 assert!(matches!(
886 partial_err,
887 MemScopeError::Export {
888 partial_success: true,
889 ..
890 }
891 ));
892 assert_eq!(partial_err.category(), "export");
893 assert!(partial_err.is_recoverable());
894 assert_eq!(partial_err.severity(), ErrorSeverity::Warning);
895 }
896
897 #[test]
898 fn test_config_error_creation() {
899 let err = MemScopeError::config("tracker", "invalid configuration");
900 assert!(matches!(err, MemScopeError::Configuration { .. }));
901 assert_eq!(err.category(), "config");
902 assert!(!err.is_recoverable());
903 assert_eq!(err.severity(), ErrorSeverity::Error);
904 }
905
906 #[test]
907 fn test_system_error_creation() {
908 let err = MemScopeError::system(SystemErrorType::Io, "io error");
909 assert!(matches!(
910 err,
911 MemScopeError::System {
912 error_type: SystemErrorType::Io,
913 ..
914 }
915 ));
916 assert_eq!(err.category(), "system");
917 assert!(err.is_recoverable());
918 assert_eq!(err.severity(), ErrorSeverity::Error);
919
920 let io_err = io::Error::other("test io error");
921 let converted_err: MemScopeError = io_err.into();
922 assert!(matches!(
923 converted_err,
924 MemScopeError::System {
925 error_type: SystemErrorType::Io,
926 ..
927 }
928 ));
929 assert_eq!(converted_err.category(), "system");
930 }
931
932 #[test]
933 fn test_internal_error_creation() {
934 let err = MemScopeError::internal("internal error");
935 assert!(matches!(err, MemScopeError::Internal { .. }));
936 assert_eq!(err.category(), "internal");
937 assert!(!err.is_recoverable());
938 assert_eq!(err.severity(), ErrorSeverity::Fatal);
939
940 let err_with_location = MemScopeError::internal_at("internal error", "test_function");
941 assert!(matches!(err_with_location, MemScopeError::Internal { .. }));
942 assert_eq!(err_with_location.category(), "internal");
943 }
944
945 #[test]
946 fn test_error_display_formatting() {
947 let mem_err = MemScopeError::memory_with_context(
949 MemoryOperation::Allocation,
950 "allocation failed",
951 "in test function",
952 );
953 let mem_display = format!("{mem_err}");
954 assert!(mem_display.contains("Memory allocation error"));
955 assert!(mem_display.contains("allocation failed"));
956 assert!(mem_display.contains("context: in test function"));
957
958 let analysis_err = MemScopeError::analysis("fragmentation", "analysis failed");
960 let analysis_display = format!("{analysis_err}");
961 assert!(analysis_display.contains("Analysis error in fragmentation: analysis failed"));
962
963 let export_err = MemScopeError::export("json", "export failed");
965 let export_display = format!("{export_err}");
966 assert!(export_display.contains("Export error (json): export failed"));
967
968 let partial_export_err = MemScopeError::export_partial("html", "partial export");
969 let partial_export_display = format!("{partial_export_err}");
970 assert!(partial_export_display.contains("Partial export error (html): partial export"));
971
972 let config_err = MemScopeError::config("tracker", "invalid config");
974 let config_display = format!("{config_err}");
975 assert!(config_display.contains("Configuration error in tracker: invalid config"));
976
977 let system_err = MemScopeError::system(SystemErrorType::Io, "io error");
979 let system_display = format!("{system_err}");
980 assert!(system_display.contains("I/O error: io error"));
981
982 let internal_err = MemScopeError::internal_at("internal error", "test_function");
984 let internal_display = format!("{internal_err}");
985 assert!(internal_display.contains("Internal error: internal error at test_function"));
986 }
987
988 #[test]
989 fn test_error_severity_ordering() {
990 assert!(ErrorSeverity::Warning < ErrorSeverity::Error);
991 assert!(ErrorSeverity::Error < ErrorSeverity::Critical);
992 assert!(ErrorSeverity::Critical < ErrorSeverity::Fatal);
993 }
994
995 #[test]
996 fn test_memory_operation_variants() {
997 let operations = [
998 MemoryOperation::Allocation,
999 MemoryOperation::Deallocation,
1000 MemoryOperation::Association,
1001 MemoryOperation::Tracking,
1002 MemoryOperation::Validation,
1003 ];
1004
1005 for op in operations {
1006 let err = MemScopeError::memory(op, "test");
1007 assert_eq!(err.category(), "memory");
1008 }
1009 }
1010
1011 #[test]
1012 fn test_system_error_type_variants() {
1013 let error_types = [
1014 SystemErrorType::Io,
1015 SystemErrorType::Threading,
1016 SystemErrorType::Locking,
1017 SystemErrorType::Channel,
1018 SystemErrorType::Serialization,
1019 SystemErrorType::Network,
1020 SystemErrorType::FileSystem,
1021 ];
1022
1023 for error_type in error_types {
1024 let err = MemScopeError::system(error_type, "test");
1025 assert_eq!(err.category(), "system");
1026 }
1027 }
1028
1029 #[test]
1030 fn test_error_recovery_strategies() {
1031 let recovery = DefaultErrorRecovery::new();
1032
1033 let mem_err = MemScopeError::memory(MemoryOperation::Allocation, "test error");
1035 assert!(recovery.can_recover(&mem_err));
1036 let action = recovery.get_recovery_action(&mem_err);
1037 assert!(matches!(action, Some(RecoveryAction::Retry { .. })));
1038
1039 let analysis_err = MemScopeError::analysis("test", "analysis error");
1041 assert!(recovery.can_recover(&analysis_err));
1042 let action = recovery.get_recovery_action(&analysis_err);
1043 assert!(matches!(action, Some(RecoveryAction::UseDefault { .. })));
1044
1045 let partial_export_err = MemScopeError::export_partial("json", "partial export");
1047 assert!(recovery.can_recover(&partial_export_err));
1048 let action = recovery.get_recovery_action(&partial_export_err);
1049 assert!(matches!(action, Some(RecoveryAction::Skip)));
1050
1051 let export_err = MemScopeError::export("json", "export error");
1053 assert!(!export_err.is_recoverable());
1055 assert!(!recovery.can_recover(&export_err)); let action = recovery.get_recovery_action(&export_err);
1057 assert!(matches!(action, Some(RecoveryAction::Abort)));
1058
1059 let internal_err = MemScopeError::internal("internal error");
1061 assert!(!recovery.can_recover(&internal_err)); let action = recovery.get_recovery_action(&internal_err);
1063 assert!(matches!(action, Some(RecoveryAction::Abort)));
1064 }
1065
1066 #[test]
1067 fn test_recovery_execution() {
1068 let recovery = DefaultErrorRecovery::new();
1069
1070 let retry_action = RecoveryAction::Retry {
1072 max_attempts: 1,
1073 delay_ms: 1, };
1075 assert!(recovery.execute_recovery(&retry_action).is_ok());
1076
1077 let default_action = RecoveryAction::UseDefault {
1079 value: "test".to_string(),
1080 };
1081 assert!(recovery.execute_recovery(&default_action).is_ok());
1082
1083 let skip_action = RecoveryAction::Skip;
1085 assert!(recovery.execute_recovery(&skip_action).is_ok());
1086
1087 let fallback_action = RecoveryAction::Fallback {
1089 strategy: "test".to_string(),
1090 };
1091 assert!(recovery.execute_recovery(&fallback_action).is_ok());
1092
1093 let abort_action = RecoveryAction::Abort;
1095 assert!(recovery.execute_recovery(&abort_action).is_err());
1096 }
1097
1098 #[test]
1099 fn test_result_type_alias() {
1100 fn test_function() -> MemScopeResult<()> {
1101 Ok(())
1102 }
1103
1104 assert!(test_function().is_ok());
1105 }
1106
1107 #[test]
1108 fn test_serde_conversion() {
1109 let io_err = std::io::Error::other("test io error");
1110 let json_err = serde_json::Error::io(io_err);
1111 let memscope_err: MemScopeError = json_err.into();
1112 assert!(matches!(
1113 memscope_err,
1114 MemScopeError::System {
1115 error_type: SystemErrorType::Serialization,
1116 ..
1117 }
1118 ));
1119 assert_eq!(memscope_err.category(), "system");
1120 }
1121}