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,
605 message,
606 error_kind: _,
607 ..
608 } => {
609 write!(f, "Analysis error in {analyzer}: {message}")
610 }
611 Self::Export {
612 format,
613 message,
614 partial_success,
615 error_kind: _,
616 error_context: _,
617 } => {
618 if *partial_success {
619 write!(f, "Partial export error ({format}): {message}")
620 } else {
621 write!(f, "Export error ({format}): {message}")
622 }
623 }
624 Self::Configuration {
625 component,
626 message,
627 error_kind: _,
628 error_context: _,
629 } => {
630 write!(f, "Configuration error in {component}: {message}")
631 }
632 Self::System {
633 error_type,
634 message,
635 source_message,
636 error_kind: _,
637 error_context: _,
638 } => {
639 write!(
640 f,
641 "{} error: {}",
642 match error_type {
643 SystemErrorType::Io => "I/O",
644 SystemErrorType::Threading => "Threading",
645 SystemErrorType::Locking => "Locking",
646 SystemErrorType::Channel => "Channel",
647 SystemErrorType::Serialization => "Serialization",
648 SystemErrorType::Network => "Network",
649 SystemErrorType::FileSystem => "File system",
650 },
651 message
652 )?;
653 if let Some(source) = source_message {
654 write!(f, " (source: {source})")?;
655 }
656 Ok(())
657 }
658 Self::Internal {
659 message,
660 location,
661 error_kind: _,
662 error_context: _,
663 } => {
664 write!(f, "Internal error: {message}")?;
665 if let Some(loc) = location {
666 write!(f, " at {loc}")?;
667 }
668 Ok(())
669 }
670 }
671 }
672}
673
674impl std::error::Error for MemScopeError {}
675
676impl From<std::io::Error> for MemScopeError {
678 fn from(err: std::io::Error) -> Self {
679 Self::system_with_source(
680 SystemErrorType::Io,
681 format!("I/O operation failed: {err}"),
682 err,
683 )
684 }
685}
686
687impl From<serde_json::Error> for MemScopeError {
688 fn from(err: serde_json::Error) -> Self {
689 Self::system_with_source(
690 SystemErrorType::Serialization,
691 format!("JSON serialization failed: {err}"),
692 err,
693 )
694 }
695}
696
697impl From<crate::render_engine::export::ExportError> for MemScopeError {
698 fn from(err: crate::render_engine::export::ExportError) -> Self {
699 Self::error("export", "unknown", err.to_string())
700 }
701}
702
703#[derive(Debug, Clone)]
705pub enum RecoveryAction {
706 Retry { max_attempts: u32, delay_ms: u64 },
708 UseDefault { value: String },
710 Skip,
712 Abort,
714 Fallback { strategy: String },
716}
717
718pub trait ErrorRecovery {
720 fn can_recover(&self, error: &MemScopeError) -> bool;
722
723 fn get_recovery_action(&self, error: &MemScopeError) -> Option<RecoveryAction>;
725
726 fn execute_recovery(&self, action: &RecoveryAction) -> MemScopeResult<()>;
728}
729
730pub struct DefaultErrorRecovery {
732 max_retries: u32,
733 retry_delay_ms: u64,
734}
735
736impl DefaultErrorRecovery {
737 pub fn new() -> Self {
738 Self {
739 max_retries: 3,
740 retry_delay_ms: 100,
741 }
742 }
743
744 pub fn with_config(max_retries: u32, retry_delay_ms: u64) -> Self {
745 Self {
746 max_retries,
747 retry_delay_ms,
748 }
749 }
750}
751
752impl Default for DefaultErrorRecovery {
753 fn default() -> Self {
754 Self::new()
755 }
756}
757
758impl ErrorRecovery for DefaultErrorRecovery {
759 fn can_recover(&self, error: &MemScopeError) -> bool {
760 error.is_recoverable() && error.severity() != ErrorSeverity::Critical
761 }
762
763 fn get_recovery_action(&self, error: &MemScopeError) -> Option<RecoveryAction> {
764 if !self.can_recover(error) {
765 return Some(RecoveryAction::Abort);
766 }
767
768 match error {
769 MemScopeError::Memory { .. } => Some(RecoveryAction::Retry {
770 max_attempts: self.max_retries,
771 delay_ms: self.retry_delay_ms,
772 }),
773 MemScopeError::Analysis { .. } => Some(RecoveryAction::UseDefault {
774 value: "{}".to_string(), }),
776 MemScopeError::Export {
777 partial_success: true,
778 ..
779 } => Some(RecoveryAction::Skip),
780 MemScopeError::Export { .. } => Some(RecoveryAction::Fallback {
781 strategy: "minimal_export".to_string(),
782 }),
783 MemScopeError::System {
784 error_type: SystemErrorType::Locking,
785 ..
786 } => Some(RecoveryAction::Retry {
787 max_attempts: 1,
788 delay_ms: 50,
789 }),
790 _ => Some(RecoveryAction::Abort),
791 }
792 }
793
794 fn execute_recovery(&self, action: &RecoveryAction) -> MemScopeResult<()> {
795 match action {
796 RecoveryAction::Retry { delay_ms, .. } => {
797 if *delay_ms > 0 {
798 std::thread::sleep(std::time::Duration::from_millis(*delay_ms));
799 }
800 Ok(())
801 }
802 RecoveryAction::UseDefault { .. } => Ok(()),
803 RecoveryAction::Skip => Ok(()),
804 RecoveryAction::Fallback { .. } => Ok(()),
805 RecoveryAction::Abort => Err(MemScopeError::internal("Recovery aborted")),
806 }
807 }
808}
809
810#[cfg(test)]
811mod tests {
812 use super::*;
813 use std::io;
814
815 #[test]
817 fn test_memory_error_creation() {
818 let err = MemScopeError::memory(MemoryOperation::Allocation, "allocation failed");
819 assert!(matches!(
820 err,
821 MemScopeError::Memory {
822 operation: MemoryOperation::Allocation,
823 ..
824 }
825 ));
826 assert_eq!(err.category(), "memory");
827 assert_eq!(err.user_message(), "allocation failed");
828 assert!(err.is_recoverable());
829 assert_eq!(err.severity(), ErrorSeverity::Error);
830
831 let err_with_context = MemScopeError::memory_with_context(
832 MemoryOperation::Deallocation,
833 "deallocation failed",
834 "in cleanup",
835 );
836 assert!(matches!(
837 err_with_context,
838 MemScopeError::Memory {
839 operation: MemoryOperation::Deallocation,
840 ..
841 }
842 ));
843 assert_eq!(err_with_context.category(), "memory");
844 }
845
846 #[test]
847 fn test_analysis_error_creation() {
848 let err = MemScopeError::analysis("fragmentation", "analysis failed");
849 assert!(matches!(
850 err,
851 MemScopeError::Analysis {
852 recoverable: true,
853 ..
854 }
855 ));
856 assert_eq!(err.category(), "analysis");
857 assert!(err.is_recoverable());
858 assert_eq!(err.severity(), ErrorSeverity::Warning);
859
860 let fatal_err = MemScopeError::analysis_fatal("lifecycle", "critical analysis failed");
861 assert!(matches!(
862 fatal_err,
863 MemScopeError::Analysis {
864 recoverable: false,
865 ..
866 }
867 ));
868 assert_eq!(fatal_err.category(), "analysis");
869 assert!(!fatal_err.is_recoverable());
870 assert_eq!(fatal_err.severity(), ErrorSeverity::Critical);
871 }
872
873 #[test]
874 fn test_export_error_creation() {
875 let err = MemScopeError::export("json", "export failed");
876 assert!(matches!(
877 err,
878 MemScopeError::Export {
879 partial_success: false,
880 ..
881 }
882 ));
883 assert_eq!(err.category(), "export");
884 assert!(!err.is_recoverable());
885 assert_eq!(err.severity(), ErrorSeverity::Error);
886
887 let partial_err = MemScopeError::export_partial("html", "partial export");
888 assert!(matches!(
889 partial_err,
890 MemScopeError::Export {
891 partial_success: true,
892 ..
893 }
894 ));
895 assert_eq!(partial_err.category(), "export");
896 assert!(partial_err.is_recoverable());
897 assert_eq!(partial_err.severity(), ErrorSeverity::Warning);
898 }
899
900 #[test]
901 fn test_config_error_creation() {
902 let err = MemScopeError::config("tracker", "invalid configuration");
903 assert!(matches!(err, MemScopeError::Configuration { .. }));
904 assert_eq!(err.category(), "config");
905 assert!(!err.is_recoverable());
906 assert_eq!(err.severity(), ErrorSeverity::Error);
907 }
908
909 #[test]
910 fn test_system_error_creation() {
911 let err = MemScopeError::system(SystemErrorType::Io, "io error");
912 assert!(matches!(
913 err,
914 MemScopeError::System {
915 error_type: SystemErrorType::Io,
916 ..
917 }
918 ));
919 assert_eq!(err.category(), "system");
920 assert!(err.is_recoverable());
921 assert_eq!(err.severity(), ErrorSeverity::Error);
922
923 let io_err = io::Error::other("test io error");
924 let converted_err: MemScopeError = io_err.into();
925 assert!(matches!(
926 converted_err,
927 MemScopeError::System {
928 error_type: SystemErrorType::Io,
929 ..
930 }
931 ));
932 assert_eq!(converted_err.category(), "system");
933 }
934
935 #[test]
936 fn test_internal_error_creation() {
937 let err = MemScopeError::internal("internal error");
938 assert!(matches!(err, MemScopeError::Internal { .. }));
939 assert_eq!(err.category(), "internal");
940 assert!(!err.is_recoverable());
941 assert_eq!(err.severity(), ErrorSeverity::Fatal);
942
943 let err_with_location = MemScopeError::internal_at("internal error", "test_function");
944 assert!(matches!(err_with_location, MemScopeError::Internal { .. }));
945 assert_eq!(err_with_location.category(), "internal");
946 }
947
948 #[test]
949 fn test_error_display_formatting() {
950 let mem_err = MemScopeError::memory_with_context(
952 MemoryOperation::Allocation,
953 "allocation failed",
954 "in test function",
955 );
956 let mem_display = format!("{mem_err}");
957 assert!(mem_display.contains("Memory allocation error"));
958 assert!(mem_display.contains("allocation failed"));
959 assert!(mem_display.contains("context: in test function"));
960
961 let analysis_err = MemScopeError::analysis("fragmentation", "analysis failed");
963 let analysis_display = format!("{analysis_err}");
964 assert!(analysis_display.contains("Analysis error in fragmentation: analysis failed"));
965
966 let export_err = MemScopeError::export("json", "export failed");
968 let export_display = format!("{export_err}");
969 assert!(export_display.contains("Export error (json): export failed"));
970
971 let partial_export_err = MemScopeError::export_partial("html", "partial export");
972 let partial_export_display = format!("{partial_export_err}");
973 assert!(partial_export_display.contains("Partial export error (html): partial export"));
974
975 let config_err = MemScopeError::config("tracker", "invalid config");
977 let config_display = format!("{config_err}");
978 assert!(config_display.contains("Configuration error in tracker: invalid config"));
979
980 let system_err = MemScopeError::system(SystemErrorType::Io, "io error");
982 let system_display = format!("{system_err}");
983 assert!(system_display.contains("I/O error: io error"));
984
985 let internal_err = MemScopeError::internal_at("internal error", "test_function");
987 let internal_display = format!("{internal_err}");
988 assert!(internal_display.contains("Internal error: internal error at test_function"));
989 }
990
991 #[test]
992 fn test_error_severity_ordering() {
993 assert!(ErrorSeverity::Warning < ErrorSeverity::Error);
994 assert!(ErrorSeverity::Error < ErrorSeverity::Critical);
995 assert!(ErrorSeverity::Critical < ErrorSeverity::Fatal);
996 }
997
998 #[test]
999 fn test_memory_operation_variants() {
1000 let operations = [
1001 MemoryOperation::Allocation,
1002 MemoryOperation::Deallocation,
1003 MemoryOperation::Association,
1004 MemoryOperation::Tracking,
1005 MemoryOperation::Validation,
1006 ];
1007
1008 for op in operations {
1009 let err = MemScopeError::memory(op, "test");
1010 assert_eq!(err.category(), "memory");
1011 }
1012 }
1013
1014 #[test]
1015 fn test_system_error_type_variants() {
1016 let error_types = [
1017 SystemErrorType::Io,
1018 SystemErrorType::Threading,
1019 SystemErrorType::Locking,
1020 SystemErrorType::Channel,
1021 SystemErrorType::Serialization,
1022 SystemErrorType::Network,
1023 SystemErrorType::FileSystem,
1024 ];
1025
1026 for error_type in error_types {
1027 let err = MemScopeError::system(error_type, "test");
1028 assert_eq!(err.category(), "system");
1029 }
1030 }
1031
1032 #[test]
1033 fn test_error_recovery_strategies() {
1034 let recovery = DefaultErrorRecovery::new();
1035
1036 let mem_err = MemScopeError::memory(MemoryOperation::Allocation, "test error");
1038 assert!(recovery.can_recover(&mem_err));
1039 let action = recovery.get_recovery_action(&mem_err);
1040 assert!(matches!(action, Some(RecoveryAction::Retry { .. })));
1041
1042 let analysis_err = MemScopeError::analysis("test", "analysis error");
1044 assert!(recovery.can_recover(&analysis_err));
1045 let action = recovery.get_recovery_action(&analysis_err);
1046 assert!(matches!(action, Some(RecoveryAction::UseDefault { .. })));
1047
1048 let partial_export_err = MemScopeError::export_partial("json", "partial export");
1050 assert!(recovery.can_recover(&partial_export_err));
1051 let action = recovery.get_recovery_action(&partial_export_err);
1052 assert!(matches!(action, Some(RecoveryAction::Skip)));
1053
1054 let export_err = MemScopeError::export("json", "export error");
1056 assert!(!export_err.is_recoverable());
1058 assert!(!recovery.can_recover(&export_err)); let action = recovery.get_recovery_action(&export_err);
1060 assert!(matches!(action, Some(RecoveryAction::Abort)));
1061
1062 let internal_err = MemScopeError::internal("internal error");
1064 assert!(!recovery.can_recover(&internal_err)); let action = recovery.get_recovery_action(&internal_err);
1066 assert!(matches!(action, Some(RecoveryAction::Abort)));
1067 }
1068
1069 #[test]
1070 fn test_recovery_execution() {
1071 let recovery = DefaultErrorRecovery::new();
1072
1073 let retry_action = RecoveryAction::Retry {
1075 max_attempts: 1,
1076 delay_ms: 1, };
1078 assert!(recovery.execute_recovery(&retry_action).is_ok());
1079
1080 let default_action = RecoveryAction::UseDefault {
1082 value: "test".to_string(),
1083 };
1084 assert!(recovery.execute_recovery(&default_action).is_ok());
1085
1086 let skip_action = RecoveryAction::Skip;
1088 assert!(recovery.execute_recovery(&skip_action).is_ok());
1089
1090 let fallback_action = RecoveryAction::Fallback {
1092 strategy: "test".to_string(),
1093 };
1094 assert!(recovery.execute_recovery(&fallback_action).is_ok());
1095
1096 let abort_action = RecoveryAction::Abort;
1098 assert!(recovery.execute_recovery(&abort_action).is_err());
1099 }
1100
1101 #[test]
1102 fn test_result_type_alias() {
1103 fn test_function() -> MemScopeResult<()> {
1104 Ok(())
1105 }
1106
1107 assert!(test_function().is_ok());
1108 }
1109
1110 #[test]
1111 fn test_serde_conversion() {
1112 let io_err = std::io::Error::other("test io error");
1113 let json_err = serde_json::Error::io(io_err);
1114 let memscope_err: MemScopeError = json_err.into();
1115 assert!(matches!(
1116 memscope_err,
1117 MemScopeError::System {
1118 error_type: SystemErrorType::Serialization,
1119 ..
1120 }
1121 ));
1122 assert_eq!(memscope_err.category(), "system");
1123 }
1124}