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, 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
673// Conversion from standard library errors
674impl 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/// Error recovery strategies
701#[derive(Debug, Clone)]
702pub enum RecoveryAction {
703    /// Retry the operation with the same parameters
704    Retry { max_attempts: u32, delay_ms: u64 },
705    /// Use a default value and continue
706    UseDefault { value: String },
707    /// Skip this operation and continue
708    Skip,
709    /// Abort the current operation
710    Abort,
711    /// Try an alternative approach
712    Fallback { strategy: String },
713}
714
715/// Error recovery trait for implementing recovery strategies
716pub trait ErrorRecovery {
717    /// Determine if an error can be recovered from
718    fn can_recover(&self, error: &MemScopeError) -> bool;
719
720    /// Get the recovery action for an error
721    fn get_recovery_action(&self, error: &MemScopeError) -> Option<RecoveryAction>;
722
723    /// Execute recovery action
724    fn execute_recovery(&self, action: &RecoveryAction) -> MemScopeResult<()>;
725}
726
727/// Default error recovery implementation
728pub 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(), // Empty JSON object as default
772            }),
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 all error creation methods
813    #[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        // Test memory error display
948        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        // Test analysis error display
959        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        // Test export error display
964        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        // Test configuration error display
973        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        // Test system error display
978        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        // Test internal error display
983        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        // Test memory error recovery
1034        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        // Test analysis error recovery
1040        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        // Test partial export recovery
1046        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        // Test full export recovery
1052        let export_err = MemScopeError::export("json", "export error");
1053        // Export errors are not recoverable according to is_recoverable() implementation
1054        assert!(!export_err.is_recoverable());
1055        assert!(!recovery.can_recover(&export_err)); // Not recoverable
1056        let action = recovery.get_recovery_action(&export_err);
1057        assert!(matches!(action, Some(RecoveryAction::Abort)));
1058
1059        // Test internal error recovery
1060        let internal_err = MemScopeError::internal("internal error");
1061        assert!(!recovery.can_recover(&internal_err)); // Not recoverable
1062        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        // Test retry execution
1071        let retry_action = RecoveryAction::Retry {
1072            max_attempts: 1,
1073            delay_ms: 1, // Minimal delay for testing
1074        };
1075        assert!(recovery.execute_recovery(&retry_action).is_ok());
1076
1077        // Test use default execution
1078        let default_action = RecoveryAction::UseDefault {
1079            value: "test".to_string(),
1080        };
1081        assert!(recovery.execute_recovery(&default_action).is_ok());
1082
1083        // Test skip execution
1084        let skip_action = RecoveryAction::Skip;
1085        assert!(recovery.execute_recovery(&skip_action).is_ok());
1086
1087        // Test fallback execution
1088        let fallback_action = RecoveryAction::Fallback {
1089            strategy: "test".to_string(),
1090        };
1091        assert!(recovery.execute_recovery(&fallback_action).is_ok());
1092
1093        // Test abort execution
1094        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}