Skip to main content

memscope_rs/core/
error.rs

1//! Unified error handling system for memscope-rs
2//!
3//! This module provides a simplified, efficient error handling system that:
4//! - Reduces string cloning overhead using `Arc<str>`
5//! - Maintains all existing error information
6//! - Provides error recovery mechanisms
7//! - Ensures backward compatibility
8
9use std::fmt;
10use std::sync::Arc;
11use std::time::SystemTime;
12
13/// Error context information for tracking and debugging
14#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
15pub struct ErrorContext {
16    /// Timestamp when the error occurred
17    pub timestamp: SystemTime,
18    /// File where the error originated
19    pub file: Option<String>,
20    /// Line number where the error originated
21    pub line: Option<u32>,
22    /// Additional context information
23    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    /// Create new error context
39    pub fn new() -> Self {
40        Self::default()
41    }
42
43    /// Create error context with file and line information
44    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    /// Add extra context information
54    pub fn with_extra(mut self, extra: impl Into<String>) -> Self {
55        self.extra = Some(extra.into());
56        self
57    }
58}
59
60/// Unified error type for the entire memscope-rs system
61///
62/// This replaces the complex TrackingError with a simpler, more efficient design
63/// while maintaining all existing error information and backward compatibility.
64#[derive(Debug, Clone)]
65pub enum MemScopeError {
66    /// Memory tracking operations (allocation, deallocation, association)
67    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 operations (fragmentation, lifecycle, etc.)
76    Analysis {
77        analyzer: Arc<str>,
78        message: Arc<str>,
79        recoverable: bool,
80        error_kind: ErrorKind,
81        error_context: ErrorContext,
82    },
83
84    /// Export operations (JSON, binary, HTML, etc.)
85    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 and initialization errors
94    Configuration {
95        component: Arc<str>,
96        message: Arc<str>,
97        error_kind: ErrorKind,
98        error_context: ErrorContext,
99    },
100
101    /// System-level errors (IO, threading, etc.)
102    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 errors that should not normally occur
111    Internal {
112        message: Arc<str>,
113        location: Option<Arc<str>>,
114        error_kind: ErrorKind,
115        error_context: ErrorContext,
116    },
117}
118
119/// Memory operation types for better error categorization
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub enum MemoryOperation {
122    Allocation,
123    Deallocation,
124    Association,
125    Tracking,
126    Validation,
127}
128
129/// System error types
130#[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
141/// Result type alias for MemScopeError
142pub type Result<T> = std::result::Result<T, MemScopeError>;
143
144/// Result type alias for MemScopeError (alternative name)
145pub type MemScopeResult<T> = std::result::Result<T, MemScopeError>;
146
147impl MemScopeError {
148    /// Unified error creation - external modules only need to provide:
149    /// - module: which module the error occurred in (e.g., "variable_registry")
150    /// - method: which method/function (e.g., "register_variable")
151    /// - message: what went wrong
152    ///
153    /// Internal classification by module:
154    /// - MemoryError: tracker, allocator, variable_registry, memory_tracker, capture
155    /// - ExportError: export, render_engine, snapshot
156    /// - AnalysisError: ffi_function_resolver, call_stack_normalizer, analysis,
157    ///   lifecycle, rule_engine, pattern_matcher, detector, unsafe_ffi_tracker
158    /// - ConfigurationError: config, initialization
159    /// - IoError: system errors (io, threading, locking, network, filesystem)
160    /// - InternalError: fallback for unknown modules
161    pub fn error(module: &str, method: &str, message: impl Into<Arc<str>>) -> Self {
162        let kind = match module {
163            // Memory tracking modules
164            "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 modules
170            "export" | "render_engine" | "snapshot" => ErrorKind::ExportError,
171
172            // Analysis modules
173            "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            // Configuration modules
192            "config" | "initialization" => ErrorKind::ConfigurationError,
193
194            // System errors
195            "system" | "io" | "threading" | "locking" | "network" | "filesystem"
196            | "serialization" => ErrorKind::IoError,
197
198            // Fallback
199            _ => ErrorKind::InternalError,
200        };
201        let full_message = format!("{}::{} - {}", module, method, message.into());
202        Self::new(kind, full_message)
203    }
204
205    /// Create a new error with the specified kind and message
206    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    /// Create a new error with the specified kind, severity, and context
266    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        // Update error context with the provided context information
274        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    /// Create a memory operation error
288    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    /// Create a memory operation error with context
299    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    /// Create an analysis error
314    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    /// Create a non-recoverable analysis error
325    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    /// Create an export error
336    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    /// Create an export error with partial success
347    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    /// Create an export error with source information
358    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    /// Create a configuration error
373    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    /// Create a system error
383    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    /// Create a system error with source
394    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    /// Create an internal error
409    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    /// Create an internal error with location
419    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    /// Check if this error is recoverable
429    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    /// Get error kind for categorization and statistics
446    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    /// Get error severity level
458    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    /// Get a user-friendly error message
477    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    /// Get error category for logging/metrics
489    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    /// Get error context information
501    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    /// Get a detailed error description
513    pub fn description(&self) -> String {
514        format!("{}", self)
515    }
516}
517
518/// Error kind classification for error tracking and statistics
519#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
520pub enum ErrorKind {
521    /// Memory-related errors
522    MemoryError,
523    /// Configuration errors
524    ConfigurationError,
525    /// I/O errors
526    IoError,
527    /// Symbol resolution errors
528    SymbolResolutionError,
529    /// Stack trace errors
530    StackTraceError,
531    /// Cache errors
532    CacheError,
533    /// Internal errors
534    InternalError,
535    /// Validation errors
536    ValidationError,
537    /// Analysis errors
538    AnalysisError,
539    /// Export errors
540    ExportError,
541}
542
543impl ErrorKind {
544    /// Get the default severity for this error kind
545    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/// Error severity levels
562#[derive(
563    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
564)]
565pub enum ErrorSeverity {
566    /// Non-critical issues that don't prevent operation
567    Warning,
568    /// Errors that prevent normal operation but may be recoverable
569    Error,
570    /// Critical errors that require immediate attention
571    Critical,
572    /// Fatal errors that cannot be recovered from
573    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
676// Conversion from standard library errors
677impl 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/// Error recovery strategies
704#[derive(Debug, Clone)]
705pub enum RecoveryAction {
706    /// Retry the operation with the same parameters
707    Retry { max_attempts: u32, delay_ms: u64 },
708    /// Use a default value and continue
709    UseDefault { value: String },
710    /// Skip this operation and continue
711    Skip,
712    /// Abort the current operation
713    Abort,
714    /// Try an alternative approach
715    Fallback { strategy: String },
716}
717
718/// Error recovery trait for implementing recovery strategies
719pub trait ErrorRecovery {
720    /// Determine if an error can be recovered from
721    fn can_recover(&self, error: &MemScopeError) -> bool;
722
723    /// Get the recovery action for an error
724    fn get_recovery_action(&self, error: &MemScopeError) -> Option<RecoveryAction>;
725
726    /// Execute recovery action
727    fn execute_recovery(&self, action: &RecoveryAction) -> MemScopeResult<()>;
728}
729
730/// Default error recovery implementation
731pub 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(), // Empty JSON object as default
775            }),
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 all error creation methods
816    #[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        // Test memory error display
951        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        // Test analysis error display
962        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        // Test export error display
967        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        // Test configuration error display
976        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        // Test system error display
981        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        // Test internal error display
986        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        // Test memory error recovery
1037        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        // Test analysis error recovery
1043        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        // Test partial export recovery
1049        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        // Test full export recovery
1055        let export_err = MemScopeError::export("json", "export error");
1056        // Export errors are not recoverable according to is_recoverable() implementation
1057        assert!(!export_err.is_recoverable());
1058        assert!(!recovery.can_recover(&export_err)); // Not recoverable
1059        let action = recovery.get_recovery_action(&export_err);
1060        assert!(matches!(action, Some(RecoveryAction::Abort)));
1061
1062        // Test internal error recovery
1063        let internal_err = MemScopeError::internal("internal error");
1064        assert!(!recovery.can_recover(&internal_err)); // Not recoverable
1065        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        // Test retry execution
1074        let retry_action = RecoveryAction::Retry {
1075            max_attempts: 1,
1076            delay_ms: 1, // Minimal delay for testing
1077        };
1078        assert!(recovery.execute_recovery(&retry_action).is_ok());
1079
1080        // Test use default execution
1081        let default_action = RecoveryAction::UseDefault {
1082            value: "test".to_string(),
1083        };
1084        assert!(recovery.execute_recovery(&default_action).is_ok());
1085
1086        // Test skip execution
1087        let skip_action = RecoveryAction::Skip;
1088        assert!(recovery.execute_recovery(&skip_action).is_ok());
1089
1090        // Test fallback execution
1091        let fallback_action = RecoveryAction::Fallback {
1092            strategy: "test".to_string(),
1093        };
1094        assert!(recovery.execute_recovery(&fallback_action).is_ok());
1095
1096        // Test abort execution
1097        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}