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;
11
12/// Unified error type for the entire memscope-rs system
13///
14/// This replaces the complex TrackingError with a simpler, more efficient design
15/// while maintaining all existing error information and backward compatibility.
16#[derive(Debug, Clone)]
17pub enum MemScopeError {
18    /// Memory tracking operations (allocation, deallocation, association)
19    Memory {
20        operation: MemoryOperation,
21        message: Arc<str>,
22        context: Option<Arc<str>>,
23    },
24
25    /// Analysis operations (fragmentation, lifecycle, etc.)
26    Analysis {
27        analyzer: Arc<str>,
28        message: Arc<str>,
29        recoverable: bool,
30    },
31
32    /// Export operations (JSON, binary, HTML, etc.)
33    Export {
34        format: Arc<str>,
35        message: Arc<str>,
36        partial_success: bool,
37    },
38
39    /// Configuration and initialization errors
40    Configuration {
41        component: Arc<str>,
42        message: Arc<str>,
43    },
44
45    /// System-level errors (IO, threading, etc.)
46    System {
47        error_type: SystemErrorType,
48        message: Arc<str>,
49        source_message: Option<Arc<str>>,
50    },
51
52    /// Internal errors that should not normally occur
53    Internal {
54        message: Arc<str>,
55        location: Option<Arc<str>>,
56    },
57}
58
59/// Memory operation types for better error categorization
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub enum MemoryOperation {
62    Allocation,
63    Deallocation,
64    Association,
65    Tracking,
66    Validation,
67}
68
69/// System error types
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum SystemErrorType {
72    Io,
73    Threading,
74    Locking,
75    Channel,
76    Serialization,
77    Network,
78    FileSystem,
79}
80
81/// Result type for all memscope operations
82pub type Result<T> = std::result::Result<T, MemScopeError>;
83
84impl MemScopeError {
85    /// Create a memory operation error
86    pub fn memory(operation: MemoryOperation, message: impl Into<Arc<str>>) -> Self {
87        Self::Memory {
88            operation,
89            message: message.into(),
90            context: None,
91        }
92    }
93
94    /// Create a memory operation error with context
95    pub fn memory_with_context(
96        operation: MemoryOperation,
97        message: impl Into<Arc<str>>,
98        context: impl Into<Arc<str>>,
99    ) -> Self {
100        Self::Memory {
101            operation,
102            message: message.into(),
103            context: Some(context.into()),
104        }
105    }
106
107    /// Create an analysis error
108    pub fn analysis(analyzer: impl Into<Arc<str>>, message: impl Into<Arc<str>>) -> Self {
109        Self::Analysis {
110            analyzer: analyzer.into(),
111            message: message.into(),
112            recoverable: true,
113        }
114    }
115
116    /// Create a non-recoverable analysis error
117    pub fn analysis_fatal(analyzer: impl Into<Arc<str>>, message: impl Into<Arc<str>>) -> Self {
118        Self::Analysis {
119            analyzer: analyzer.into(),
120            message: message.into(),
121            recoverable: false,
122        }
123    }
124
125    /// Create an export error
126    pub fn export(format: impl Into<Arc<str>>, message: impl Into<Arc<str>>) -> Self {
127        Self::Export {
128            format: format.into(),
129            message: message.into(),
130            partial_success: false,
131        }
132    }
133
134    /// Create an export error with partial success
135    pub fn export_partial(format: impl Into<Arc<str>>, message: impl Into<Arc<str>>) -> Self {
136        Self::Export {
137            format: format.into(),
138            message: message.into(),
139            partial_success: true,
140        }
141    }
142
143    /// Create a configuration error
144    pub fn config(component: impl Into<Arc<str>>, message: impl Into<Arc<str>>) -> Self {
145        Self::Configuration {
146            component: component.into(),
147            message: message.into(),
148        }
149    }
150
151    /// Create a system error
152    pub fn system(error_type: SystemErrorType, message: impl Into<Arc<str>>) -> Self {
153        Self::System {
154            error_type,
155            message: message.into(),
156            source_message: None,
157        }
158    }
159
160    /// Create a system error with source
161    pub fn system_with_source(
162        error_type: SystemErrorType,
163        message: impl Into<Arc<str>>,
164        source: impl std::error::Error,
165    ) -> Self {
166        Self::System {
167            error_type,
168            message: message.into(),
169            source_message: Some(Arc::from(source.to_string())),
170        }
171    }
172
173    /// Create an internal error
174    pub fn internal(message: impl Into<Arc<str>>) -> Self {
175        Self::Internal {
176            message: message.into(),
177            location: None,
178        }
179    }
180
181    /// Create an internal error with location
182    pub fn internal_at(message: impl Into<Arc<str>>, location: impl Into<Arc<str>>) -> Self {
183        Self::Internal {
184            message: message.into(),
185            location: Some(location.into()),
186        }
187    }
188
189    /// Check if this error is recoverable
190    pub fn is_recoverable(&self) -> bool {
191        match self {
192            Self::Memory { .. } => true,
193            Self::Analysis { recoverable, .. } => *recoverable,
194            Self::Export {
195                partial_success, ..
196            } => *partial_success,
197            Self::Configuration { .. } => false,
198            Self::System { error_type, .. } => matches!(
199                error_type,
200                SystemErrorType::Io | SystemErrorType::Network | SystemErrorType::Locking
201            ),
202            Self::Internal { .. } => false,
203        }
204    }
205
206    /// Get error severity level
207    pub fn severity(&self) -> ErrorSeverity {
208        match self {
209            Self::Memory { .. } => ErrorSeverity::Medium,
210            Self::Analysis {
211                recoverable: false, ..
212            } => ErrorSeverity::High,
213            Self::Analysis { .. } => ErrorSeverity::Low,
214            Self::Export {
215                partial_success: true,
216                ..
217            } => ErrorSeverity::Low,
218            Self::Export { .. } => ErrorSeverity::Medium,
219            Self::Configuration { .. } => ErrorSeverity::Medium,
220            Self::System { .. } => ErrorSeverity::Medium,
221            Self::Internal { .. } => ErrorSeverity::Critical,
222        }
223    }
224
225    /// Get a user-friendly error message
226    pub fn user_message(&self) -> &str {
227        match self {
228            Self::Memory { message, .. } => message,
229            Self::Analysis { message, .. } => message,
230            Self::Export { message, .. } => message,
231            Self::Configuration { message, .. } => message,
232            Self::System { message, .. } => message,
233            Self::Internal { message, .. } => message,
234        }
235    }
236
237    /// Get error category for logging/metrics
238    pub fn category(&self) -> &'static str {
239        match self {
240            Self::Memory { .. } => "memory",
241            Self::Analysis { .. } => "analysis",
242            Self::Export { .. } => "export",
243            Self::Configuration { .. } => "config",
244            Self::System { .. } => "system",
245            Self::Internal { .. } => "internal",
246        }
247    }
248}
249
250/// Error severity levels
251#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
252pub enum ErrorSeverity {
253    Low,
254    Medium,
255    High,
256    Critical,
257}
258
259impl fmt::Display for MemScopeError {
260    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261        match self {
262            Self::Memory {
263                operation,
264                message,
265                context,
266            } => {
267                write!(
268                    f,
269                    "Memory {} error: {}",
270                    match operation {
271                        MemoryOperation::Allocation => "allocation",
272                        MemoryOperation::Deallocation => "deallocation",
273                        MemoryOperation::Association => "association",
274                        MemoryOperation::Tracking => "tracking",
275                        MemoryOperation::Validation => "validation",
276                    },
277                    message
278                )?;
279                if let Some(ctx) = context {
280                    write!(f, " (context: {ctx})")?;
281                }
282                Ok(())
283            }
284            Self::Analysis {
285                analyzer, message, ..
286            } => {
287                write!(f, "Analysis error in {analyzer}: {message}")
288            }
289            Self::Export {
290                format,
291                message,
292                partial_success,
293            } => {
294                if *partial_success {
295                    write!(f, "Partial export error ({format}): {message}")
296                } else {
297                    write!(f, "Export error ({format}): {message}")
298                }
299            }
300            Self::Configuration { component, message } => {
301                write!(f, "Configuration error in {component}: {message}")
302            }
303            Self::System {
304                error_type,
305                message,
306                source_message,
307            } => {
308                write!(
309                    f,
310                    "{} error: {}",
311                    match error_type {
312                        SystemErrorType::Io => "I/O",
313                        SystemErrorType::Threading => "Threading",
314                        SystemErrorType::Locking => "Locking",
315                        SystemErrorType::Channel => "Channel",
316                        SystemErrorType::Serialization => "Serialization",
317                        SystemErrorType::Network => "Network",
318                        SystemErrorType::FileSystem => "File system",
319                    },
320                    message
321                )?;
322                if let Some(source) = source_message {
323                    write!(f, " (source: {source})")?;
324                }
325                Ok(())
326            }
327            Self::Internal { message, location } => {
328                write!(f, "Internal error: {message}")?;
329                if let Some(loc) = location {
330                    write!(f, " at {loc}")?;
331                }
332                Ok(())
333            }
334        }
335    }
336}
337
338impl std::error::Error for MemScopeError {}
339
340// Conversion from standard library errors
341impl From<std::io::Error> for MemScopeError {
342    fn from(err: std::io::Error) -> Self {
343        Self::system_with_source(
344            SystemErrorType::Io,
345            format!("I/O operation failed: {err}"),
346            err,
347        )
348    }
349}
350
351impl From<serde_json::Error> for MemScopeError {
352    fn from(err: serde_json::Error) -> Self {
353        Self::system_with_source(
354            SystemErrorType::Serialization,
355            format!("JSON serialization failed: {err}"),
356            err,
357        )
358    }
359}
360
361// Backward compatibility: conversion from old TrackingError
362impl From<crate::core::types::TrackingError> for MemScopeError {
363    fn from(err: crate::core::types::TrackingError) -> Self {
364        use crate::core::types::TrackingError as TE;
365
366        match err {
367            TE::AllocationFailed(msg) => Self::memory(MemoryOperation::Allocation, msg),
368            TE::DeallocationFailed(msg) => Self::memory(MemoryOperation::Deallocation, msg),
369            TE::TrackingDisabled => Self::config("tracker", "Memory tracking is disabled"),
370            TE::InvalidPointer(msg) => Self::memory(MemoryOperation::Validation, msg),
371            TE::SerializationError(msg) => Self::system(SystemErrorType::Serialization, msg),
372            TE::VisualizationError(msg) => Self::export("visualization", msg),
373            TE::ThreadSafetyError(msg) => Self::system(SystemErrorType::Threading, msg),
374            TE::ConfigurationError(msg) => Self::config("general", msg),
375            TE::AnalysisError(msg) => Self::analysis("general", msg),
376            TE::ExportError(msg) => Self::export("general", msg),
377            TE::MemoryCorruption(msg) => Self::memory(MemoryOperation::Validation, msg),
378            TE::UnsafeOperationDetected(msg) => Self::analysis("unsafe", msg),
379            TE::FFIError(msg) => Self::analysis("ffi", msg),
380            TE::ScopeError(msg) => Self::memory(MemoryOperation::Tracking, msg),
381            TE::BorrowCheckError(msg) => Self::analysis("borrow", msg),
382            TE::LifetimeError(msg) => Self::analysis("lifetime", msg),
383            TE::TypeInferenceError(msg) => Self::analysis("type", msg),
384            TE::PerformanceError(msg) => Self::system(SystemErrorType::Io, msg),
385            TE::ResourceExhausted(msg) => Self::system(SystemErrorType::Io, msg),
386            TE::InternalError(msg) => Self::internal(msg),
387            TE::IoError(msg) => Self::system(SystemErrorType::Io, msg),
388            TE::LockError(msg) => Self::system(SystemErrorType::Locking, msg),
389            TE::ChannelError(msg) => Self::system(SystemErrorType::Channel, msg),
390            TE::ThreadError(msg) => Self::system(SystemErrorType::Threading, msg),
391            TE::InitializationError(msg) => Self::config("initialization", msg),
392            TE::NotImplemented(msg) => Self::internal(msg),
393            TE::ValidationError(msg) => Self::memory(MemoryOperation::Validation, msg),
394            TE::InvalidOperation(msg) => Self::memory(MemoryOperation::Tracking, msg),
395            TE::LockContention(msg) => Self::memory(MemoryOperation::Tracking, msg),
396            TE::DataError(msg) => Self::memory(MemoryOperation::Tracking, msg),
397        }
398    }
399}
400
401/// Error recovery strategies
402#[derive(Debug, Clone)]
403pub enum RecoveryAction {
404    /// Retry the operation with the same parameters
405    Retry { max_attempts: u32, delay_ms: u64 },
406    /// Use a default value and continue
407    UseDefault { value: String },
408    /// Skip this operation and continue
409    Skip,
410    /// Abort the current operation
411    Abort,
412    /// Try an alternative approach
413    Fallback { strategy: String },
414}
415
416/// Error recovery trait for implementing recovery strategies
417pub trait ErrorRecovery {
418    /// Determine if an error can be recovered from
419    fn can_recover(&self, error: &MemScopeError) -> bool;
420
421    /// Get the recovery action for an error
422    fn get_recovery_action(&self, error: &MemScopeError) -> Option<RecoveryAction>;
423
424    /// Execute recovery action
425    fn execute_recovery(&self, action: &RecoveryAction) -> Result<()>;
426}
427
428/// Default error recovery implementation
429pub struct DefaultErrorRecovery {
430    max_retries: u32,
431    retry_delay_ms: u64,
432}
433
434impl DefaultErrorRecovery {
435    pub fn new() -> Self {
436        Self {
437            max_retries: 3,
438            retry_delay_ms: 100,
439        }
440    }
441
442    pub fn with_config(max_retries: u32, retry_delay_ms: u64) -> Self {
443        Self {
444            max_retries,
445            retry_delay_ms,
446        }
447    }
448}
449
450impl Default for DefaultErrorRecovery {
451    fn default() -> Self {
452        Self::new()
453    }
454}
455
456impl ErrorRecovery for DefaultErrorRecovery {
457    fn can_recover(&self, error: &MemScopeError) -> bool {
458        error.is_recoverable() && error.severity() != ErrorSeverity::Critical
459    }
460
461    fn get_recovery_action(&self, error: &MemScopeError) -> Option<RecoveryAction> {
462        if !self.can_recover(error) {
463            return Some(RecoveryAction::Abort);
464        }
465
466        match error {
467            MemScopeError::Memory { .. } => Some(RecoveryAction::Retry {
468                max_attempts: self.max_retries,
469                delay_ms: self.retry_delay_ms,
470            }),
471            MemScopeError::Analysis { .. } => Some(RecoveryAction::UseDefault {
472                value: "{}".to_string(), // Empty JSON object as default
473            }),
474            MemScopeError::Export {
475                partial_success: true,
476                ..
477            } => Some(RecoveryAction::Skip),
478            MemScopeError::Export { .. } => Some(RecoveryAction::Fallback {
479                strategy: "minimal_export".to_string(),
480            }),
481            MemScopeError::System {
482                error_type: SystemErrorType::Locking,
483                ..
484            } => Some(RecoveryAction::Retry {
485                max_attempts: 1,
486                delay_ms: 50,
487            }),
488            _ => Some(RecoveryAction::Abort),
489        }
490    }
491
492    fn execute_recovery(&self, action: &RecoveryAction) -> Result<()> {
493        match action {
494            RecoveryAction::Retry { delay_ms, .. } => {
495                if *delay_ms > 0 {
496                    std::thread::sleep(std::time::Duration::from_millis(*delay_ms));
497                }
498                Ok(())
499            }
500            RecoveryAction::UseDefault { .. } => Ok(()),
501            RecoveryAction::Skip => Ok(()),
502            RecoveryAction::Fallback { .. } => Ok(()),
503            RecoveryAction::Abort => Err(MemScopeError::internal("Recovery aborted")),
504        }
505    }
506}
507
508#[cfg(test)]
509mod tests {
510    use super::*;
511    use std::io;
512
513    // Test all error creation methods
514    #[test]
515    fn test_memory_error_creation() {
516        let err = MemScopeError::memory(MemoryOperation::Allocation, "allocation failed");
517        assert!(matches!(
518            err,
519            MemScopeError::Memory {
520                operation: MemoryOperation::Allocation,
521                ..
522            }
523        ));
524        assert_eq!(err.category(), "memory");
525        assert_eq!(err.user_message(), "allocation failed");
526        assert!(err.is_recoverable());
527        assert_eq!(err.severity(), ErrorSeverity::Medium);
528
529        let err_with_context = MemScopeError::memory_with_context(
530            MemoryOperation::Deallocation,
531            "deallocation failed",
532            "in cleanup",
533        );
534        assert!(matches!(
535            err_with_context,
536            MemScopeError::Memory {
537                operation: MemoryOperation::Deallocation,
538                ..
539            }
540        ));
541        assert_eq!(err_with_context.category(), "memory");
542    }
543
544    #[test]
545    fn test_analysis_error_creation() {
546        let err = MemScopeError::analysis("fragmentation", "analysis failed");
547        assert!(matches!(
548            err,
549            MemScopeError::Analysis {
550                recoverable: true,
551                ..
552            }
553        ));
554        assert_eq!(err.category(), "analysis");
555        assert!(err.is_recoverable());
556        assert_eq!(err.severity(), ErrorSeverity::Low);
557
558        let fatal_err = MemScopeError::analysis_fatal("lifecycle", "critical analysis failed");
559        assert!(matches!(
560            fatal_err,
561            MemScopeError::Analysis {
562                recoverable: false,
563                ..
564            }
565        ));
566        assert_eq!(fatal_err.category(), "analysis");
567        assert!(!fatal_err.is_recoverable());
568        assert_eq!(fatal_err.severity(), ErrorSeverity::High);
569    }
570
571    #[test]
572    fn test_export_error_creation() {
573        let err = MemScopeError::export("json", "export failed");
574        assert!(matches!(
575            err,
576            MemScopeError::Export {
577                partial_success: false,
578                ..
579            }
580        ));
581        assert_eq!(err.category(), "export");
582        assert!(!err.is_recoverable());
583        assert_eq!(err.severity(), ErrorSeverity::Medium);
584
585        let partial_err = MemScopeError::export_partial("html", "partial export");
586        assert!(matches!(
587            partial_err,
588            MemScopeError::Export {
589                partial_success: true,
590                ..
591            }
592        ));
593        assert_eq!(partial_err.category(), "export");
594        assert!(partial_err.is_recoverable());
595        assert_eq!(partial_err.severity(), ErrorSeverity::Low);
596    }
597
598    #[test]
599    fn test_config_error_creation() {
600        let err = MemScopeError::config("tracker", "invalid configuration");
601        assert!(matches!(err, MemScopeError::Configuration { .. }));
602        assert_eq!(err.category(), "config");
603        assert!(!err.is_recoverable());
604        assert_eq!(err.severity(), ErrorSeverity::Medium);
605    }
606
607    #[test]
608    fn test_system_error_creation() {
609        let err = MemScopeError::system(SystemErrorType::Io, "io error");
610        assert!(matches!(
611            err,
612            MemScopeError::System {
613                error_type: SystemErrorType::Io,
614                ..
615            }
616        ));
617        assert_eq!(err.category(), "system");
618        assert!(err.is_recoverable());
619        assert_eq!(err.severity(), ErrorSeverity::Medium);
620
621        let io_err = io::Error::other("test io error");
622        let converted_err: MemScopeError = io_err.into();
623        assert!(matches!(
624            converted_err,
625            MemScopeError::System {
626                error_type: SystemErrorType::Io,
627                ..
628            }
629        ));
630        assert_eq!(converted_err.category(), "system");
631    }
632
633    #[test]
634    fn test_internal_error_creation() {
635        let err = MemScopeError::internal("internal error");
636        assert!(matches!(err, MemScopeError::Internal { .. }));
637        assert_eq!(err.category(), "internal");
638        assert!(!err.is_recoverable());
639        assert_eq!(err.severity(), ErrorSeverity::Critical);
640
641        let err_with_location = MemScopeError::internal_at("internal error", "test_function");
642        assert!(matches!(err_with_location, MemScopeError::Internal { .. }));
643        assert_eq!(err_with_location.category(), "internal");
644    }
645
646    #[test]
647    fn test_error_display_formatting() {
648        // Test memory error display
649        let mem_err = MemScopeError::memory_with_context(
650            MemoryOperation::Allocation,
651            "allocation failed",
652            "in test function",
653        );
654        let mem_display = format!("{mem_err}");
655        assert!(mem_display.contains("Memory allocation error"));
656        assert!(mem_display.contains("allocation failed"));
657        assert!(mem_display.contains("context: in test function"));
658
659        // Test analysis error display
660        let analysis_err = MemScopeError::analysis("fragmentation", "analysis failed");
661        let analysis_display = format!("{analysis_err}");
662        assert!(analysis_display.contains("Analysis error in fragmentation: analysis failed"));
663
664        // Test export error display
665        let export_err = MemScopeError::export("json", "export failed");
666        let export_display = format!("{export_err}");
667        assert!(export_display.contains("Export error (json): export failed"));
668
669        let partial_export_err = MemScopeError::export_partial("html", "partial export");
670        let partial_export_display = format!("{partial_export_err}");
671        assert!(partial_export_display.contains("Partial export error (html): partial export"));
672
673        // Test configuration error display
674        let config_err = MemScopeError::config("tracker", "invalid config");
675        let config_display = format!("{config_err}");
676        assert!(config_display.contains("Configuration error in tracker: invalid config"));
677
678        // Test system error display
679        let system_err = MemScopeError::system(SystemErrorType::Io, "io error");
680        let system_display = format!("{system_err}");
681        assert!(system_display.contains("I/O error: io error"));
682
683        // Test internal error display
684        let internal_err = MemScopeError::internal_at("internal error", "test_function");
685        let internal_display = format!("{internal_err}");
686        assert!(internal_display.contains("Internal error: internal error at test_function"));
687    }
688
689    #[test]
690    fn test_error_severity_ordering() {
691        assert!(ErrorSeverity::Low < ErrorSeverity::Medium);
692        assert!(ErrorSeverity::Medium < ErrorSeverity::High);
693        assert!(ErrorSeverity::High < ErrorSeverity::Critical);
694    }
695
696    #[test]
697    fn test_memory_operation_variants() {
698        let operations = [
699            MemoryOperation::Allocation,
700            MemoryOperation::Deallocation,
701            MemoryOperation::Association,
702            MemoryOperation::Tracking,
703            MemoryOperation::Validation,
704        ];
705
706        for op in operations {
707            let err = MemScopeError::memory(op, "test");
708            assert_eq!(err.category(), "memory");
709        }
710    }
711
712    #[test]
713    fn test_system_error_type_variants() {
714        let error_types = [
715            SystemErrorType::Io,
716            SystemErrorType::Threading,
717            SystemErrorType::Locking,
718            SystemErrorType::Channel,
719            SystemErrorType::Serialization,
720            SystemErrorType::Network,
721            SystemErrorType::FileSystem,
722        ];
723
724        for error_type in error_types {
725            let err = MemScopeError::system(error_type, "test");
726            assert_eq!(err.category(), "system");
727        }
728    }
729
730    #[test]
731    fn test_backward_compatibility() {
732        use crate::core::types::TrackingError;
733
734        // Test conversion of all TrackingError variants
735        let test_cases = vec![
736            TrackingError::AllocationFailed("alloc".to_string()),
737            TrackingError::DeallocationFailed("dealloc".to_string()),
738            TrackingError::TrackingDisabled,
739            TrackingError::InvalidPointer("invalid ptr".to_string()),
740            TrackingError::SerializationError("serialize".to_string()),
741            TrackingError::VisualizationError("visualize".to_string()),
742            TrackingError::ThreadSafetyError("thread".to_string()),
743            TrackingError::ConfigurationError("config".to_string()),
744            TrackingError::AnalysisError("analysis".to_string()),
745            TrackingError::ExportError("export".to_string()),
746            TrackingError::MemoryCorruption("corruption".to_string()),
747            TrackingError::UnsafeOperationDetected("unsafe".to_string()),
748            TrackingError::FFIError("ffi".to_string()),
749            TrackingError::ScopeError("scope".to_string()),
750            TrackingError::BorrowCheckError("borrow".to_string()),
751            TrackingError::LifetimeError("lifetime".to_string()),
752            TrackingError::TypeInferenceError("type".to_string()),
753            TrackingError::PerformanceError("perf".to_string()),
754            TrackingError::ResourceExhausted("resource".to_string()),
755            TrackingError::InternalError("internal".to_string()),
756            TrackingError::IoError("io".to_string()),
757            TrackingError::LockError("lock".to_string()),
758            TrackingError::ChannelError("channel".to_string()),
759            TrackingError::ThreadError("thread".to_string()),
760            TrackingError::InitializationError("init".to_string()),
761            TrackingError::NotImplemented("not_impl".to_string()),
762            TrackingError::ValidationError("validation".to_string()),
763            TrackingError::InvalidOperation("invalid_op".to_string()),
764            TrackingError::LockContention("contention".to_string()),
765            TrackingError::DataError("data".to_string()),
766        ];
767
768        for old_err in test_cases {
769            let new_err: MemScopeError = old_err.into();
770            // Just ensure conversion doesn't panic and produces a valid error
771            assert!(!new_err.category().is_empty());
772        }
773    }
774
775    #[test]
776    fn test_error_recovery_strategies() {
777        let recovery = DefaultErrorRecovery::new();
778
779        // Test memory error recovery
780        let mem_err = MemScopeError::memory(MemoryOperation::Allocation, "test error");
781        assert!(recovery.can_recover(&mem_err));
782        let action = recovery.get_recovery_action(&mem_err);
783        assert!(matches!(action, Some(RecoveryAction::Retry { .. })));
784
785        // Test analysis error recovery
786        let analysis_err = MemScopeError::analysis("test", "analysis error");
787        assert!(recovery.can_recover(&analysis_err));
788        let action = recovery.get_recovery_action(&analysis_err);
789        assert!(matches!(action, Some(RecoveryAction::UseDefault { .. })));
790
791        // Test partial export recovery
792        let partial_export_err = MemScopeError::export_partial("json", "partial export");
793        assert!(recovery.can_recover(&partial_export_err));
794        let action = recovery.get_recovery_action(&partial_export_err);
795        assert!(matches!(action, Some(RecoveryAction::Skip)));
796
797        // Test full export recovery
798        let export_err = MemScopeError::export("json", "export error");
799        // Export errors are not recoverable according to is_recoverable() implementation
800        assert!(!export_err.is_recoverable());
801        assert!(!recovery.can_recover(&export_err)); // Not recoverable
802        let action = recovery.get_recovery_action(&export_err);
803        assert!(matches!(action, Some(RecoveryAction::Abort)));
804
805        // Test internal error recovery
806        let internal_err = MemScopeError::internal("internal error");
807        assert!(!recovery.can_recover(&internal_err)); // Not recoverable
808        let action = recovery.get_recovery_action(&internal_err);
809        assert!(matches!(action, Some(RecoveryAction::Abort)));
810    }
811
812    #[test]
813    fn test_recovery_execution() {
814        let recovery = DefaultErrorRecovery::new();
815
816        // Test retry execution
817        let retry_action = RecoveryAction::Retry {
818            max_attempts: 1,
819            delay_ms: 1, // Minimal delay for testing
820        };
821        assert!(recovery.execute_recovery(&retry_action).is_ok());
822
823        // Test use default execution
824        let default_action = RecoveryAction::UseDefault {
825            value: "test".to_string(),
826        };
827        assert!(recovery.execute_recovery(&default_action).is_ok());
828
829        // Test skip execution
830        let skip_action = RecoveryAction::Skip;
831        assert!(recovery.execute_recovery(&skip_action).is_ok());
832
833        // Test fallback execution
834        let fallback_action = RecoveryAction::Fallback {
835            strategy: "test".to_string(),
836        };
837        assert!(recovery.execute_recovery(&fallback_action).is_ok());
838
839        // Test abort execution
840        let abort_action = RecoveryAction::Abort;
841        assert!(recovery.execute_recovery(&abort_action).is_err());
842    }
843
844    #[test]
845    fn test_result_type_alias() {
846        fn test_function() -> Result<()> {
847            Ok(())
848        }
849
850        assert!(test_function().is_ok());
851    }
852
853    #[test]
854    fn test_serde_conversion() {
855        let io_err = std::io::Error::other("test io error");
856        let json_err = serde_json::Error::io(io_err);
857        let memscope_err: MemScopeError = json_err.into();
858        assert!(matches!(
859            memscope_err,
860            MemScopeError::System {
861                error_type: SystemErrorType::Serialization,
862                ..
863            }
864        ));
865        assert_eq!(memscope_err.category(), "system");
866    }
867}