memscope_rs/core/
error_adapter.rs

1//! Error adapter for backward compatibility
2//!
3//! This module provides adapters to maintain compatibility with existing code
4//! that uses the old TrackingError type while internally using the new
5//! unified MemScopeError system.
6
7use crate::core::error::{MemScopeError, MemoryOperation, SystemErrorType};
8use crate::core::types::TrackingError;
9
10/// Adapter trait for converting between old and new error types
11pub trait ErrorAdapter {
12    /// Convert from old TrackingError to new MemScopeError
13    fn from_tracking_error(error: TrackingError) -> MemScopeError;
14
15    /// Convert from new MemScopeError to old TrackingError for compatibility
16    fn to_tracking_error(error: &MemScopeError) -> TrackingError;
17}
18
19/// Default implementation of ErrorAdapter
20pub struct DefaultErrorAdapter;
21
22impl ErrorAdapter for DefaultErrorAdapter {
23    fn from_tracking_error(error: TrackingError) -> MemScopeError {
24        match error {
25            TrackingError::DataError(msg) => MemScopeError::memory(MemoryOperation::Tracking, msg),
26            TrackingError::AllocationFailed(msg) => {
27                MemScopeError::memory(MemoryOperation::Allocation, msg)
28            }
29            TrackingError::DeallocationFailed(msg) => {
30                MemScopeError::memory(MemoryOperation::Deallocation, msg)
31            }
32            TrackingError::TrackingDisabled => {
33                MemScopeError::config("tracker", "Memory tracking is disabled")
34            }
35            TrackingError::InvalidPointer(msg) => {
36                MemScopeError::memory(MemoryOperation::Validation, msg)
37            }
38            TrackingError::SerializationError(msg) => {
39                MemScopeError::system(SystemErrorType::Serialization, msg)
40            }
41            TrackingError::VisualizationError(msg) => MemScopeError::export("visualization", msg),
42            TrackingError::ThreadSafetyError(msg) => MemScopeError::analysis("thread_safety", msg),
43            TrackingError::ConfigurationError(msg) => MemScopeError::config("general", msg),
44            TrackingError::AnalysisError(msg) => MemScopeError::analysis("general", msg),
45            TrackingError::ExportError(msg) => MemScopeError::export("general", msg),
46            TrackingError::MemoryCorruption(msg) => {
47                MemScopeError::memory(MemoryOperation::Validation, msg)
48            }
49            TrackingError::UnsafeOperationDetected(msg) => MemScopeError::analysis("unsafe", msg),
50            TrackingError::FFIError(msg) => MemScopeError::analysis("ffi", msg),
51            TrackingError::ScopeError(msg) => MemScopeError::memory(MemoryOperation::Tracking, msg),
52            TrackingError::BorrowCheckError(msg) => MemScopeError::analysis("borrow", msg),
53            TrackingError::LifetimeError(msg) => MemScopeError::analysis("lifetime", msg),
54            TrackingError::TypeInferenceError(msg) => MemScopeError::analysis("type", msg),
55            TrackingError::PerformanceError(msg) => MemScopeError::system(SystemErrorType::Io, msg),
56            TrackingError::ResourceExhausted(msg) => {
57                MemScopeError::system(SystemErrorType::Io, msg)
58            }
59            TrackingError::InternalError(msg) => MemScopeError::internal(msg),
60            TrackingError::IoError(msg) => MemScopeError::system(SystemErrorType::Io, msg),
61            TrackingError::LockError(msg) => MemScopeError::system(SystemErrorType::Locking, msg),
62            TrackingError::ChannelError(msg) => {
63                MemScopeError::system(SystemErrorType::Channel, msg)
64            }
65            TrackingError::ThreadError(msg) => {
66                MemScopeError::system(SystemErrorType::Threading, msg)
67            }
68            TrackingError::InitializationError(msg) => MemScopeError::config("initialization", msg),
69            TrackingError::NotImplemented(msg) => MemScopeError::internal(msg),
70            TrackingError::ValidationError(msg) => {
71                MemScopeError::memory(MemoryOperation::Validation, msg)
72            }
73            TrackingError::InvalidOperation(msg) => {
74                MemScopeError::memory(MemoryOperation::Tracking, msg)
75            }
76            TrackingError::LockContention(msg) => {
77                MemScopeError::memory(MemoryOperation::Tracking, msg)
78            }
79        }
80    }
81
82    fn to_tracking_error(error: &MemScopeError) -> TrackingError {
83        match error {
84            MemScopeError::Memory {
85                operation,
86                message,
87                context,
88            } => {
89                let full_message = if let Some(ctx) = context {
90                    format!("{message} (context: {ctx})")
91                } else {
92                    message.to_string()
93                };
94
95                match operation {
96                    MemoryOperation::Allocation => TrackingError::AllocationFailed(full_message),
97                    MemoryOperation::Deallocation => {
98                        TrackingError::DeallocationFailed(full_message)
99                    }
100                    MemoryOperation::Association => TrackingError::InvalidOperation(full_message),
101                    MemoryOperation::Tracking => TrackingError::ScopeError(full_message),
102                    MemoryOperation::Validation => TrackingError::InvalidPointer(full_message),
103                }
104            }
105            MemScopeError::Analysis {
106                analyzer, message, ..
107            } => {
108                let full_message = format!("{analyzer}: {message}");
109                match analyzer.as_ref() {
110                    "unsafe" => TrackingError::UnsafeOperationDetected(full_message),
111                    "ffi" => TrackingError::FFIError(full_message),
112                    "borrow" => TrackingError::BorrowCheckError(full_message),
113                    "lifetime" => TrackingError::LifetimeError(full_message),
114                    "type" => TrackingError::TypeInferenceError(full_message),
115                    "thread_safety" => TrackingError::ThreadSafetyError(full_message),
116                    _ => TrackingError::AnalysisError(full_message),
117                }
118            }
119            MemScopeError::Export {
120                format, message, ..
121            } => {
122                let full_message = format!("{format}: {message}");
123                match format.as_ref() {
124                    "visualization" => TrackingError::VisualizationError(full_message),
125                    _ => TrackingError::ExportError(full_message),
126                }
127            }
128            MemScopeError::Configuration { component, message } => {
129                let full_message = format!("{component}: {message}");
130                match component.as_ref() {
131                    "tracker" => TrackingError::TrackingDisabled,
132                    "initialization" => TrackingError::InitializationError(full_message),
133                    _ => TrackingError::ConfigurationError(full_message),
134                }
135            }
136            MemScopeError::System {
137                error_type,
138                message,
139                ..
140            } => match error_type {
141                SystemErrorType::Io => TrackingError::IoError(message.to_string()),
142                SystemErrorType::Threading => TrackingError::ThreadSafetyError(message.to_string()),
143                SystemErrorType::Locking => TrackingError::LockError(message.to_string()),
144                SystemErrorType::Channel => TrackingError::ChannelError(message.to_string()),
145                SystemErrorType::Serialization => {
146                    TrackingError::SerializationError(message.to_string())
147                }
148                SystemErrorType::Network => TrackingError::IoError(message.to_string()),
149                SystemErrorType::FileSystem => TrackingError::IoError(message.to_string()),
150            },
151            MemScopeError::Internal { message, .. } => {
152                TrackingError::InternalError(message.to_string())
153            }
154        }
155    }
156}
157
158/// Convenience functions for error conversion
159pub fn from_tracking_error(error: TrackingError) -> MemScopeError {
160    DefaultErrorAdapter::from_tracking_error(error)
161}
162
163pub fn to_tracking_error(error: &MemScopeError) -> TrackingError {
164    DefaultErrorAdapter::to_tracking_error(error)
165}
166
167/// Macro for easy error conversion in existing code
168#[macro_export]
169macro_rules! convert_error {
170    ($error:expr) => {
171        $crate::core::error_adapter::from_tracking_error($error)
172    };
173}
174
175/// Result type adapter for backward compatibility
176pub type AdaptedResult<T> = Result<T, MemScopeError>;
177
178/// Convert old TrackingResult to new Result type
179pub fn adapt_result<T>(result: crate::core::types::TrackingResult<T>) -> AdaptedResult<T> {
180    result.map_err(from_tracking_error)
181}
182
183/// Convert new Result to old TrackingResult for compatibility
184pub fn to_tracking_result<T>(result: AdaptedResult<T>) -> crate::core::types::TrackingResult<T> {
185    result.map_err(|e| to_tracking_error(&e))
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191    use crate::core::ErrorSeverity;
192
193    #[test]
194    fn test_allocation_error_conversion() {
195        let old_error = TrackingError::AllocationFailed("test allocation failed".to_string());
196        let new_error = DefaultErrorAdapter::from_tracking_error(old_error);
197
198        assert_eq!(new_error.category(), "memory");
199        assert!(new_error.user_message().contains("test allocation failed"));
200
201        let converted_back = DefaultErrorAdapter::to_tracking_error(&new_error);
202        assert!(matches!(converted_back, TrackingError::AllocationFailed(_)));
203    }
204
205    #[test]
206    fn test_analysis_error_conversion() {
207        let old_error = TrackingError::BorrowCheckError("borrow check failed".to_string());
208        let new_error = DefaultErrorAdapter::from_tracking_error(old_error);
209
210        assert_eq!(new_error.category(), "analysis");
211
212        let converted_back = DefaultErrorAdapter::to_tracking_error(&new_error);
213        assert!(matches!(converted_back, TrackingError::BorrowCheckError(_)));
214    }
215
216    #[test]
217    fn test_system_error_conversion() {
218        let old_error = TrackingError::IoError("IO operation failed".to_string());
219        let new_error = DefaultErrorAdapter::from_tracking_error(old_error);
220
221        assert_eq!(new_error.category(), "system");
222
223        let converted_back = DefaultErrorAdapter::to_tracking_error(&new_error);
224        assert!(matches!(converted_back, TrackingError::IoError(_)));
225    }
226
227    #[test]
228    fn test_result_adaptation() {
229        let old_result: crate::core::types::TrackingResult<i32> =
230            Err(TrackingError::AllocationFailed("test".to_string()));
231
232        let adapted_result = adapt_result(old_result);
233        assert!(adapted_result.is_err());
234
235        let converted_back = to_tracking_result(adapted_result);
236        assert!(matches!(
237            converted_back,
238            Err(TrackingError::AllocationFailed(_))
239        ));
240    }
241
242    #[test]
243    fn test_all_tracking_error_variants() {
244        // Test all TrackingError variants for comprehensive coverage
245        let test_errors = vec![
246            TrackingError::AllocationFailed("allocation failed".to_string()),
247            TrackingError::DeallocationFailed("deallocation failed".to_string()),
248            TrackingError::BorrowCheckError("borrow check error".to_string()),
249            TrackingError::LifetimeError("lifetime error".to_string()),
250            TrackingError::ThreadSafetyError("thread safety error".to_string()),
251            TrackingError::InvalidPointer("invalid pointer".to_string()),
252            TrackingError::IoError("IO error".to_string()),
253            TrackingError::SerializationError("serialization error".to_string()),
254            TrackingError::ConfigurationError("configuration error".to_string()),
255            TrackingError::InternalError("internal error".to_string()),
256        ];
257
258        for original_error in test_errors {
259            // Convert to new error format
260            let adapted_error = DefaultErrorAdapter::from_tracking_error(original_error.clone());
261
262            // Verify the adapted error has appropriate properties
263            assert!(!adapted_error.category().is_empty());
264            assert!(!adapted_error.user_message().is_empty());
265            // Skip technical_details check as method doesn't exist
266
267            // Convert back to tracking error
268            let converted_back = DefaultErrorAdapter::to_tracking_error(&adapted_error);
269
270            // Verify the conversion preserves the error type
271            match (&original_error, &converted_back) {
272                (TrackingError::AllocationFailed(_), TrackingError::AllocationFailed(_)) => {}
273                (TrackingError::DeallocationFailed(_), TrackingError::DeallocationFailed(_)) => {}
274                (TrackingError::BorrowCheckError(_), TrackingError::BorrowCheckError(_)) => {}
275                (TrackingError::LifetimeError(_), TrackingError::LifetimeError(_)) => {}
276                (TrackingError::ThreadSafetyError(_), TrackingError::ThreadSafetyError(_)) => {}
277                (TrackingError::InvalidPointer(_), TrackingError::InvalidPointer(_)) => {}
278                (TrackingError::IoError(_), TrackingError::IoError(_)) => {}
279                (TrackingError::SerializationError(_), TrackingError::SerializationError(_)) => {}
280                (TrackingError::ConfigurationError(_), TrackingError::ConfigurationError(_)) => {}
281                (TrackingError::InternalError(_), TrackingError::InternalError(_)) => {}
282                _ => panic!(
283                    "Error type mismatch: {:?} -> {:?}",
284                    original_error, converted_back
285                ),
286            }
287        }
288    }
289
290    #[test]
291    fn test_error_categories() {
292        // Test that errors are categorized correctly
293        let memory_errors = vec![
294            TrackingError::AllocationFailed("test".to_string()),
295            TrackingError::DeallocationFailed("test".to_string()),
296            TrackingError::InvalidPointer("test".to_string()),
297        ];
298
299        for error in memory_errors {
300            let adapted = DefaultErrorAdapter::from_tracking_error(error);
301            assert_eq!(adapted.category(), "memory");
302        }
303
304        let analysis_errors = vec![
305            TrackingError::BorrowCheckError("test".to_string()),
306            TrackingError::LifetimeError("test".to_string()),
307            TrackingError::ThreadSafetyError("test".to_string()),
308        ];
309
310        for error in analysis_errors {
311            let adapted = DefaultErrorAdapter::from_tracking_error(error);
312            assert_eq!(adapted.category(), "analysis");
313        }
314
315        let system_errors = vec![
316            TrackingError::IoError("test".to_string()),
317            TrackingError::SerializationError("test".to_string()),
318        ];
319
320        let config_errors = vec![TrackingError::ConfigurationError("test".to_string())];
321
322        let internal_errors = vec![TrackingError::InternalError("test".to_string())];
323
324        for error in system_errors {
325            let adapted = DefaultErrorAdapter::from_tracking_error(error);
326            assert_eq!(adapted.category(), "system");
327        }
328
329        for error in config_errors {
330            let adapted = DefaultErrorAdapter::from_tracking_error(error);
331            assert_eq!(adapted.category(), "config");
332        }
333
334        for error in internal_errors {
335            let adapted = DefaultErrorAdapter::from_tracking_error(error);
336            assert_eq!(adapted.category(), "internal");
337        }
338    }
339
340    #[test]
341    fn test_successful_result_adaptation() {
342        // Test successful results pass through unchanged
343        let success_result: crate::core::types::TrackingResult<String> = Ok("success".to_string());
344        let adapted = adapt_result(success_result);
345        assert!(adapted.is_ok());
346        assert_eq!(adapted.unwrap(), "success");
347
348        // Test conversion back
349        let success_adapted: AdaptedResult<i32> = Ok(42);
350        let converted_back = to_tracking_result(success_adapted);
351        assert!(converted_back.is_ok());
352        assert_eq!(converted_back.unwrap(), 42);
353    }
354
355    #[test]
356    fn test_error_message_preservation() {
357        let test_cases = vec![
358            ("Simple error message", TrackingError::AllocationFailed("Simple error message".to_string())),
359            ("Error with special chars: !@#$%^&*()", TrackingError::IoError("Error with special chars: !@#$%^&*()".to_string())),
360            ("Unicode error: 错误信息 🦀", TrackingError::BorrowCheckError("Unicode error: 错误信息 🦀".to_string())),
361            ("", TrackingError::InternalError("".to_string())), // Empty message
362            ("Very long error message that spans multiple lines and contains lots of details about what went wrong in the system", 
363             TrackingError::ConfigurationError("Very long error message that spans multiple lines and contains lots of details about what went wrong in the system".to_string())),
364        ];
365
366        for (expected_content, error) in test_cases {
367            let adapted = DefaultErrorAdapter::from_tracking_error(error);
368            assert!(adapted.user_message().contains(expected_content));
369            // Skip technical_details check as method doesn't exist
370        }
371    }
372
373    #[test]
374    fn test_error_adapter_trait_methods() {
375        let _adapter = DefaultErrorAdapter;
376
377        // Test with various error types
378        let allocation_error = TrackingError::AllocationFailed("allocation failed".to_string());
379        let adapted = DefaultErrorAdapter::from_tracking_error(allocation_error.clone());
380
381        // Test trait methods
382        assert!(!adapted.category().is_empty());
383        assert!(!adapted.user_message().is_empty());
384        assert!(!adapted.user_message().is_empty());
385        assert!(adapted.severity() <= ErrorSeverity::Critical); // Check severity is valid
386
387        // Test conversion back
388        let converted = DefaultErrorAdapter::to_tracking_error(&adapted);
389        assert!(matches!(converted, TrackingError::AllocationFailed(_)));
390    }
391
392    #[test]
393    fn test_error_severity_levels() {
394        // Test that different error types have appropriate severity levels
395        let critical_errors = vec![
396            TrackingError::InvalidPointer("test".to_string()),
397            TrackingError::AllocationFailed("test".to_string()),
398        ];
399
400        let warning_errors = vec![
401            TrackingError::BorrowCheckError("test".to_string()),
402            TrackingError::LifetimeError("test".to_string()),
403        ];
404
405        let info_errors = vec![TrackingError::ConfigurationError("test".to_string())];
406
407        // Memory errors should have medium severity
408        for error in critical_errors {
409            let adapted = DefaultErrorAdapter::from_tracking_error(error);
410            assert_eq!(
411                adapted.severity(),
412                ErrorSeverity::Medium,
413                "Memory error should have medium severity"
414            );
415        }
416
417        // Analysis errors should have low severity (unless non-recoverable)
418        for error in warning_errors {
419            let adapted = DefaultErrorAdapter::from_tracking_error(error);
420            assert_eq!(
421                adapted.severity(),
422                ErrorSeverity::Low,
423                "Analysis error should have low severity"
424            );
425        }
426
427        // Info errors should have lower severity
428        for error in info_errors {
429            let adapted = DefaultErrorAdapter::from_tracking_error(error);
430            assert!(
431                adapted.severity() <= ErrorSeverity::High,
432                "Info error should have reasonable severity"
433            );
434        }
435    }
436
437    #[test]
438    fn test_error_chain_preservation() {
439        // Test that error chains are preserved through adaptation
440        let original_error = TrackingError::AllocationFailed("root cause".to_string());
441        let adapted = DefaultErrorAdapter::from_tracking_error(original_error);
442
443        // The user message should contain the original error information
444        assert!(adapted.user_message().contains("root cause"));
445    }
446
447    #[test]
448    fn test_concurrent_error_adaptation() {
449        use std::sync::Arc;
450        use std::thread;
451
452        let _adapter = Arc::new(DefaultErrorAdapter);
453        let mut handles = vec![];
454
455        // Test concurrent error adaptation
456        for i in 0..10 {
457            let handle = thread::spawn(move || {
458                let error = TrackingError::AllocationFailed(format!("error_{}", i));
459                let adapted = DefaultErrorAdapter::from_tracking_error(error);
460                let converted_back = DefaultErrorAdapter::to_tracking_error(&adapted);
461
462                match converted_back {
463                    TrackingError::AllocationFailed(msg) => {
464                        assert!(msg.contains(&format!("error_{}", i)))
465                    }
466                    _ => panic!("Unexpected error type"),
467                }
468            });
469            handles.push(handle);
470        }
471
472        for handle in handles {
473            handle.join().expect("Thread should complete successfully");
474        }
475    }
476
477    #[test]
478    fn test_error_formatting() {
479        let error = TrackingError::AllocationFailed("test allocation".to_string());
480        let adapted = DefaultErrorAdapter::from_tracking_error(error);
481
482        // Test that the adapted error can be formatted
483        let user_msg = adapted.user_message();
484        let category = adapted.category();
485
486        assert!(!user_msg.is_empty());
487        assert!(!category.is_empty());
488
489        // Test that formatting doesn't panic
490        let _formatted = format!("Error: {} ({})", user_msg, category);
491    }
492
493    #[test]
494    fn test_result_chain_operations() {
495        // Test chaining operations on adapted results
496        let success: crate::core::types::TrackingResult<i32> = Ok(10);
497        let adapted_success = adapt_result(success);
498
499        // Test mapping operations
500        let mapped = adapted_success.map(|x| x * 2);
501        assert_eq!(mapped.unwrap(), 20);
502
503        // Test error case
504        let error_result: crate::core::types::TrackingResult<i32> =
505            Err(TrackingError::AllocationFailed("test".to_string()));
506        let adapted_error = adapt_result(error_result);
507
508        let mapped_error = adapted_error.map(|x| x * 2);
509        assert!(mapped_error.is_err());
510
511        // Convert back and verify
512        let converted_back = to_tracking_result(mapped_error);
513        assert!(matches!(
514            converted_back,
515            Err(TrackingError::AllocationFailed(_))
516        ));
517    }
518}