1use std::fmt;
10use std::sync::Arc;
11
12#[derive(Debug, Clone)]
17pub enum MemScopeError {
18 Memory {
20 operation: MemoryOperation,
21 message: Arc<str>,
22 context: Option<Arc<str>>,
23 },
24
25 Analysis {
27 analyzer: Arc<str>,
28 message: Arc<str>,
29 recoverable: bool,
30 },
31
32 Export {
34 format: Arc<str>,
35 message: Arc<str>,
36 partial_success: bool,
37 },
38
39 Configuration {
41 component: Arc<str>,
42 message: Arc<str>,
43 },
44
45 System {
47 error_type: SystemErrorType,
48 message: Arc<str>,
49 source_message: Option<Arc<str>>,
50 },
51
52 Internal {
54 message: Arc<str>,
55 location: Option<Arc<str>>,
56 },
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub enum MemoryOperation {
62 Allocation,
63 Deallocation,
64 Association,
65 Tracking,
66 Validation,
67}
68
69#[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
81pub type Result<T> = std::result::Result<T, MemScopeError>;
83
84impl MemScopeError {
85 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 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 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 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 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 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 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 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 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 pub fn internal(message: impl Into<Arc<str>>) -> Self {
175 Self::Internal {
176 message: message.into(),
177 location: None,
178 }
179 }
180
181 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 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 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 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 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#[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
340impl 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
361impl 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#[derive(Debug, Clone)]
403pub enum RecoveryAction {
404 Retry { max_attempts: u32, delay_ms: u64 },
406 UseDefault { value: String },
408 Skip,
410 Abort,
412 Fallback { strategy: String },
414}
415
416pub trait ErrorRecovery {
418 fn can_recover(&self, error: &MemScopeError) -> bool;
420
421 fn get_recovery_action(&self, error: &MemScopeError) -> Option<RecoveryAction>;
423
424 fn execute_recovery(&self, action: &RecoveryAction) -> Result<()>;
426}
427
428pub 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(), }),
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]
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 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 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 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 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 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 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 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 assert!(!new_err.category().is_empty());
772 }
773 }
774
775 #[test]
776 fn test_error_recovery_strategies() {
777 let recovery = DefaultErrorRecovery::new();
778
779 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 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 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 let export_err = MemScopeError::export("json", "export error");
799 assert!(!export_err.is_recoverable());
801 assert!(!recovery.can_recover(&export_err)); let action = recovery.get_recovery_action(&export_err);
803 assert!(matches!(action, Some(RecoveryAction::Abort)));
804
805 let internal_err = MemScopeError::internal("internal error");
807 assert!(!recovery.can_recover(&internal_err)); 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 let retry_action = RecoveryAction::Retry {
818 max_attempts: 1,
819 delay_ms: 1, };
821 assert!(recovery.execute_recovery(&retry_action).is_ok());
822
823 let default_action = RecoveryAction::UseDefault {
825 value: "test".to_string(),
826 };
827 assert!(recovery.execute_recovery(&default_action).is_ok());
828
829 let skip_action = RecoveryAction::Skip;
831 assert!(recovery.execute_recovery(&skip_action).is_ok());
832
833 let fallback_action = RecoveryAction::Fallback {
835 strategy: "test".to_string(),
836 };
837 assert!(recovery.execute_recovery(&fallback_action).is_ok());
838
839 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}