Skip to main content

trustformers_wasm/
error.rs

1//! Comprehensive error handling for TrustformeRS WASM
2//!
3//! This module provides structured error types, error codes, and utilities
4//! for robust error handling across the entire codebase.
5
6use serde::{Deserialize, Serialize};
7use std::borrow::ToOwned;
8use std::fmt;
9use std::string::{String, ToString};
10use std::vec::Vec;
11use wasm_bindgen::prelude::*;
12
13/// Error codes for categorizing different types of errors
14#[wasm_bindgen]
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
16pub enum ErrorCode {
17    // Model errors (E1xxx)
18    E1001 = 1001, // Invalid model format
19    E1002 = 1002, // Model too large
20    E1003 = 1003, // Unsupported architecture
21    E1004 = 1004, // Model loading timeout
22    E1005 = 1005, // Model corruption detected
23    E1006 = 1006, // Model version mismatch
24
25    // Inference errors (E2xxx)
26    E2001 = 2001, // Input shape mismatch
27    E2002 = 2002, // Inference computation failed
28    E2003 = 2003, // Output buffer overflow
29    E2004 = 2004, // Inference timeout
30    E2005 = 2005, // Invalid input data
31    E2006 = 2006, // Batch size mismatch
32
33    // Device errors (E3xxx)
34    E3001 = 3001, // Device initialization failed
35    E3002 = 3002, // WebGPU unavailable
36    E3003 = 3003, // Device memory allocation failed
37    E3004 = 3004, // Device context lost
38    E3005 = 3005, // Unsupported device feature
39    E3006 = 3006, // Device driver error
40
41    // Memory errors (E4xxx)
42    E4001 = 4001, // Out of memory
43    E4002 = 4002, // Memory allocation failed
44    E4003 = 4003, // Memory access violation
45    E4004 = 4004, // Memory leak detected
46    E4005 = 4005, // Buffer overflow
47    E4006 = 4006, // Stack overflow
48
49    // Configuration errors (E5xxx)
50    E5001 = 5001, // Invalid configuration parameter
51    E5002 = 5002, // Feature not available
52    E5003 = 5003, // Version mismatch
53    E5004 = 5004, // Environment not supported
54    E5005 = 5005, // Missing required dependency
55    E5006 = 5006, // License validation failed,
56
57    // Network/IO errors (E6xxx)
58    E6001 = 6001, // Network connection failed
59    E6002 = 6002, // File not found
60    E6003 = 6003, // Download timeout
61    E6004 = 6004, // Corrupted download
62    E6005 = 6005, // Access denied
63    E6006 = 6006, // Rate limit exceeded
64
65    // Storage errors (E7xxx)
66    E7001 = 7001, // Storage quota exceeded
67    E7002 = 7002, // Storage access denied
68    E7003 = 7003, // Cache corruption
69    E7004 = 7004, // Serialization failed
70    E7005 = 7005, // IndexedDB error
71    E7006 = 7006, // Storage cleanup failed
72
73    // WebAssembly errors (E8xxx)
74    E8001 = 8001, // WASM compilation failed
75    E8002 = 8002, // WASM instantiation failed
76    E8003 = 8003, // WASM memory limit exceeded
77    E8004 = 8004, // WASM table overflow
78    E8005 = 8005, // WASM validation failed
79    E8006 = 8006, // WASM runtime error
80
81    // Quantization errors (E9xxx)
82    E9001 = 9001, // Quantization failed
83    E9002 = 9002, // Unsupported quantization precision
84    E9003 = 9003, // Calibration data insufficient
85    E9004 = 9004, // Quantization accuracy loss
86    E9005 = 9005, // Dequantization error
87    E9006 = 9006, // Quantization metadata invalid
88}
89
90/// Error severity levels
91#[wasm_bindgen]
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
93pub enum ErrorSeverity {
94    /// Fatal errors that prevent further operation
95    Fatal = 0,
96    /// Errors that affect functionality but allow graceful degradation
97    Error = 1,
98    /// Warning conditions that may lead to errors
99    Warning = 2,
100    /// Informational messages about potential issues
101    Info = 3,
102}
103
104/// Error context providing additional information
105#[derive(Debug, Clone, Serialize, Deserialize, Default)]
106pub struct ErrorContext {
107    pub operation: Option<String>,
108    pub component: Option<String>,
109    pub model_id: Option<String>,
110    pub input_shape: Option<Vec<usize>>,
111    pub device_type: Option<String>,
112    pub memory_usage_mb: Option<f64>,
113    pub additional_info: Option<String>,
114}
115
116/// Comprehensive error type for TrustformeRS
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct TrustformersError {
119    pub code: ErrorCode,
120    pub message: String,
121    pub severity: ErrorSeverity,
122    pub context: ErrorContext,
123    pub timestamp: f64,
124    pub stack_trace: Option<String>,
125    pub recovery_suggestion: Option<String>,
126}
127
128impl TrustformersError {
129    /// Create a new error with minimal information
130    pub fn new(code: ErrorCode, message: &str) -> Self {
131        Self {
132            code,
133            message: message.to_owned(),
134            severity: Self::default_severity(code),
135            context: ErrorContext::default(),
136            timestamp: Self::current_timestamp(),
137            stack_trace: None,
138            recovery_suggestion: Self::default_recovery_suggestion(code),
139        }
140    }
141
142    /// Create a new error with full context
143    pub fn with_context(
144        code: ErrorCode,
145        message: &str,
146        severity: ErrorSeverity,
147        context: ErrorContext,
148    ) -> Self {
149        Self {
150            code,
151            message: message.to_owned(),
152            severity,
153            context,
154            timestamp: Self::current_timestamp(),
155            stack_trace: None,
156            recovery_suggestion: Self::default_recovery_suggestion(code),
157        }
158    }
159
160    /// Add stack trace information
161    pub fn with_stack_trace(mut self, stack_trace: String) -> Self {
162        self.stack_trace = Some(stack_trace);
163        self
164    }
165
166    /// Add custom recovery suggestion
167    pub fn with_recovery_suggestion(mut self, suggestion: String) -> Self {
168        self.recovery_suggestion = Some(suggestion);
169        self
170    }
171
172    /// Get default severity for error code
173    fn default_severity(code: ErrorCode) -> ErrorSeverity {
174        match code {
175            // Fatal errors
176            ErrorCode::E4001 | ErrorCode::E4002 | ErrorCode::E8001 | ErrorCode::E8002 => {
177                ErrorSeverity::Fatal
178            },
179
180            // High-severity errors
181            ErrorCode::E1001
182            | ErrorCode::E1002
183            | ErrorCode::E2001
184            | ErrorCode::E2002
185            | ErrorCode::E3001
186            | ErrorCode::E3002
187            | ErrorCode::E6001
188            | ErrorCode::E7001 => ErrorSeverity::Error,
189
190            // Warnings
191            ErrorCode::E1004 | ErrorCode::E2004 | ErrorCode::E3004 | ErrorCode::E4004 => {
192                ErrorSeverity::Warning
193            },
194
195            // Default to error
196            _ => ErrorSeverity::Error,
197        }
198    }
199
200    /// Get default recovery suggestion for error code
201    fn default_recovery_suggestion(code: ErrorCode) -> Option<String> {
202        let suggestion = match code {
203            ErrorCode::E1001 => "Verify model format is supported and file is not corrupted",
204            ErrorCode::E1002 => "Use a smaller model or enable quantization to reduce memory usage",
205            ErrorCode::E2001 => "Check input tensor shape matches model requirements",
206            ErrorCode::E3001 => "Check device drivers and WebGPU support",
207            ErrorCode::E3002 => "Enable WebGPU in browser settings or fallback to CPU",
208            ErrorCode::E4001 => "Enable quantization or use a smaller model",
209            ErrorCode::E4002 => "Close other applications to free memory",
210            ErrorCode::E5002 => "Check browser compatibility and feature availability",
211            ErrorCode::E6001 => "Check network connectivity and try again",
212            ErrorCode::E7001 => "Clear browser storage or increase storage quota",
213            _ => return None,
214        };
215        Some(suggestion.to_owned())
216    }
217
218    /// Get current timestamp
219    fn current_timestamp() -> f64 {
220        js_sys::Date::now()
221    }
222
223    /// Convert to user-friendly message
224    pub fn user_message(&self) -> String {
225        match self.severity {
226            ErrorSeverity::Fatal => format!(
227                "Fatal Error ({code}): {message}",
228                code = self.code as u32,
229                message = self.message
230            ),
231            ErrorSeverity::Error => format!(
232                "Error ({code}): {message}",
233                code = self.code as u32,
234                message = self.message
235            ),
236            ErrorSeverity::Warning => format!(
237                "Warning ({code}): {message}",
238                code = self.code as u32,
239                message = self.message
240            ),
241            ErrorSeverity::Info => format!(
242                "Info ({code}): {message}",
243                code = self.code as u32,
244                message = self.message
245            ),
246        }
247    }
248
249    /// Check if error is recoverable
250    pub fn is_recoverable(&self) -> bool {
251        !matches!(self.severity, ErrorSeverity::Fatal)
252    }
253
254    /// Get error category
255    pub fn category(&self) -> &'static str {
256        let code_num = self.code as u32;
257        match code_num {
258            1001..=1999 => "Model",
259            2001..=2999 => "Inference",
260            3001..=3999 => "Device",
261            4001..=4999 => "Memory",
262            5001..=5999 => "Configuration",
263            6001..=6999 => "Network",
264            7001..=7999 => "Storage",
265            8001..=8999 => "WebAssembly",
266            9001..=9999 => "Quantization",
267            _ => "Unknown",
268        }
269    }
270}
271
272impl fmt::Display for TrustformersError {
273    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274        write!(f, "{}", self.user_message())
275    }
276}
277
278/// Convert TrustformersError to JsValue for WASM interop
279impl From<TrustformersError> for JsValue {
280    fn from(error: TrustformersError) -> Self {
281        // Create JavaScript Error object with enhanced properties
282        let js_error = js_sys::Error::new(&error.user_message());
283
284        // Add custom properties
285        js_sys::Reflect::set(&js_error, &"code".into(), &JsValue::from(error.code as u32)).ok();
286        js_sys::Reflect::set(
287            &js_error,
288            &"severity".into(),
289            &JsValue::from(error.severity as u32),
290        )
291        .ok();
292        js_sys::Reflect::set(
293            &js_error,
294            &"category".into(),
295            &JsValue::from(error.category()),
296        )
297        .ok();
298        js_sys::Reflect::set(
299            &js_error,
300            &"timestamp".into(),
301            &JsValue::from(error.timestamp),
302        )
303        .ok();
304        js_sys::Reflect::set(
305            &js_error,
306            &"isRecoverable".into(),
307            &JsValue::from(error.is_recoverable()),
308        )
309        .ok();
310
311        if let Some(ref suggestion) = error.recovery_suggestion {
312            js_sys::Reflect::set(
313                &js_error,
314                &"recoverySuggestion".into(),
315                &JsValue::from(suggestion),
316            )
317            .ok();
318        }
319
320        if let Some(ref stack) = error.stack_trace {
321            js_sys::Reflect::set(&js_error, &"stackTrace".into(), &JsValue::from(stack)).ok();
322        }
323
324        // Add context as JSON
325        if let Ok(context_json) = serde_json::to_string(&error.context) {
326            js_sys::Reflect::set(&js_error, &"context".into(), &JsValue::from(context_json)).ok();
327        }
328
329        js_error.into()
330    }
331}
332
333/// Convert JsValue to TrustformersError (for error propagation)
334impl From<JsValue> for TrustformersError {
335    fn from(js_value: JsValue) -> Self {
336        let message = if let Some(error) = js_value.dyn_ref::<js_sys::Error>() {
337            error.message().into()
338        } else if js_value.is_string() {
339            js_value.as_string().unwrap_or_else(|| "Unknown error".to_owned())
340        } else {
341            format!("{js_value:?}")
342        };
343
344        TrustformersError::new(ErrorCode::E8006, &message)
345    }
346}
347
348/// Error builder for creating errors with context
349pub struct ErrorBuilder {
350    code: ErrorCode,
351    message: String,
352    severity: Option<ErrorSeverity>,
353    context: ErrorContext,
354}
355
356impl ErrorBuilder {
357    pub fn new(code: ErrorCode, message: &str) -> Self {
358        Self {
359            code,
360            message: message.to_owned(),
361            severity: None,
362            context: ErrorContext::default(),
363        }
364    }
365
366    pub fn severity(mut self, severity: ErrorSeverity) -> Self {
367        self.severity = Some(severity);
368        self
369    }
370
371    pub fn operation(mut self, operation: &str) -> Self {
372        self.context.operation = Some(operation.to_owned());
373        self
374    }
375
376    pub fn component(mut self, component: &str) -> Self {
377        self.context.component = Some(component.to_owned());
378        self
379    }
380
381    pub fn model_id(mut self, model_id: &str) -> Self {
382        self.context.model_id = Some(model_id.to_owned());
383        self
384    }
385
386    pub fn input_shape(mut self, shape: Vec<usize>) -> Self {
387        self.context.input_shape = Some(shape);
388        self
389    }
390
391    pub fn device_type(mut self, device_type: &str) -> Self {
392        self.context.device_type = Some(device_type.to_owned());
393        self
394    }
395
396    pub fn memory_usage_mb(mut self, memory_mb: f64) -> Self {
397        self.context.memory_usage_mb = Some(memory_mb);
398        self
399    }
400
401    pub fn additional_info(mut self, info: &str) -> Self {
402        self.context.additional_info = Some(info.to_owned());
403        self
404    }
405
406    pub fn build(self) -> TrustformersError {
407        if let Some(severity) = self.severity {
408            TrustformersError::with_context(self.code, &self.message, severity, self.context)
409        } else {
410            let mut error = TrustformersError::new(self.code, &self.message);
411            error.context = self.context;
412            error
413        }
414    }
415}
416
417/// Error collection for multiple errors
418#[derive(Debug, Clone, Serialize, Deserialize)]
419pub struct ErrorCollection {
420    pub errors: Vec<TrustformersError>,
421    pub has_fatal: bool,
422}
423
424impl ErrorCollection {
425    pub fn new() -> Self {
426        Self {
427            errors: Vec::new(),
428            has_fatal: false,
429        }
430    }
431
432    pub fn add(&mut self, error: TrustformersError) {
433        if matches!(error.severity, ErrorSeverity::Fatal) {
434            self.has_fatal = true;
435        }
436        self.errors.push(error);
437    }
438
439    pub fn is_empty(&self) -> bool {
440        self.errors.is_empty()
441    }
442
443    pub fn len(&self) -> usize {
444        self.errors.len()
445    }
446
447    pub fn has_fatal_errors(&self) -> bool {
448        self.has_fatal
449    }
450
451    pub fn has_errors(&self) -> bool {
452        self.errors
453            .iter()
454            .any(|e| matches!(e.severity, ErrorSeverity::Error | ErrorSeverity::Fatal))
455    }
456
457    pub fn get_fatal_errors(&self) -> Vec<&TrustformersError> {
458        self.errors
459            .iter()
460            .filter(|e| matches!(e.severity, ErrorSeverity::Fatal))
461            .collect()
462    }
463
464    pub fn get_errors(&self) -> Vec<&TrustformersError> {
465        self.errors
466            .iter()
467            .filter(|e| matches!(e.severity, ErrorSeverity::Error | ErrorSeverity::Fatal))
468            .collect()
469    }
470
471    pub fn get_warnings(&self) -> Vec<&TrustformersError> {
472        self.errors
473            .iter()
474            .filter(|e| matches!(e.severity, ErrorSeverity::Warning))
475            .collect()
476    }
477
478    /// Convert to user-friendly summary
479    pub fn summary(&self) -> String {
480        if self.is_empty() {
481            return "No errors".to_owned();
482        }
483
484        let fatal_count = self.get_fatal_errors().len();
485        let error_count = self.get_errors().len() - fatal_count;
486        let warning_count = self.get_warnings().len();
487
488        let mut parts = Vec::new();
489        if fatal_count > 0 {
490            parts.push(format!("{fatal_count} fatal error(s)"));
491        }
492        if error_count > 0 {
493            parts.push(format!("{error_count} error(s)"));
494        }
495        if warning_count > 0 {
496            parts.push(format!("{warning_count} warning(s)"));
497        }
498
499        parts.join(", ")
500    }
501}
502
503impl Default for ErrorCollection {
504    fn default() -> Self {
505        Self::new()
506    }
507}
508
509/// Result type alias for TrustformeRS operations
510pub type TrustformersResult<T> = Result<T, TrustformersError>;
511
512/// Utility macros for creating errors
513#[macro_export]
514macro_rules! error {
515    ($code:expr, $msg:expr) => {
516        $crate::error::TrustformersError::new($code, $msg)
517    };
518    ($code:expr, $fmt:expr, $($arg:tt)*) => {
519        $crate::error::TrustformersError::new($code, &format!($fmt, $($arg)*))
520    };
521}
522
523#[macro_export]
524macro_rules! error_builder {
525    ($code:expr, $msg:expr) => {
526        $crate::error::ErrorBuilder::new($code, $msg)
527    };
528}
529
530/// WASM bindings for error handling
531#[wasm_bindgen]
532pub struct ErrorHandler {
533    collection: ErrorCollection,
534}
535
536#[wasm_bindgen]
537impl ErrorHandler {
538    #[wasm_bindgen(constructor)]
539    pub fn new() -> Self {
540        Self {
541            collection: ErrorCollection::new(),
542        }
543    }
544
545    /// Add an error to the collection
546    pub fn add_error(&mut self, code: u32, message: &str, severity: u32) {
547        if let (Ok(error_code), Ok(error_severity)) =
548            (Self::code_from_u32(code), Self::severity_from_u32(severity))
549        {
550            let error = TrustformersError::new(error_code, message);
551            let mut error_with_severity = error;
552            error_with_severity.severity = error_severity;
553            self.collection.add(error_with_severity);
554        }
555    }
556
557    /// Check if there are any fatal errors
558    pub fn has_fatal_errors(&self) -> bool {
559        self.collection.has_fatal_errors()
560    }
561
562    /// Check if there are any errors
563    pub fn has_errors(&self) -> bool {
564        self.collection.has_errors()
565    }
566
567    /// Get error count
568    pub fn error_count(&self) -> usize {
569        self.collection.len()
570    }
571
572    /// Get error summary
573    pub fn summary(&self) -> String {
574        self.collection.summary()
575    }
576
577    /// Clear all errors
578    pub fn clear(&mut self) {
579        self.collection = ErrorCollection::new();
580    }
581
582    /// Get all errors as JSON
583    pub fn get_errors_json(&self) -> Result<String, JsValue> {
584        serde_json::to_string(&self.collection.errors)
585            .map_err(|e| JsValue::from_str(&e.to_string()))
586    }
587
588    fn code_from_u32(code: u32) -> Result<ErrorCode, ()> {
589        match code {
590            1001 => Ok(ErrorCode::E1001),
591            1002 => Ok(ErrorCode::E1002),
592            1003 => Ok(ErrorCode::E1003),
593            1004 => Ok(ErrorCode::E1004),
594            1005 => Ok(ErrorCode::E1005),
595            1006 => Ok(ErrorCode::E1006),
596            2001 => Ok(ErrorCode::E2001),
597            2002 => Ok(ErrorCode::E2002),
598            2003 => Ok(ErrorCode::E2003),
599            2004 => Ok(ErrorCode::E2004),
600            2005 => Ok(ErrorCode::E2005),
601            2006 => Ok(ErrorCode::E2006),
602            3001 => Ok(ErrorCode::E3001),
603            3002 => Ok(ErrorCode::E3002),
604            3003 => Ok(ErrorCode::E3003),
605            3004 => Ok(ErrorCode::E3004),
606            3005 => Ok(ErrorCode::E3005),
607            3006 => Ok(ErrorCode::E3006),
608            4001 => Ok(ErrorCode::E4001),
609            4002 => Ok(ErrorCode::E4002),
610            4003 => Ok(ErrorCode::E4003),
611            4004 => Ok(ErrorCode::E4004),
612            4005 => Ok(ErrorCode::E4005),
613            4006 => Ok(ErrorCode::E4006),
614            5001 => Ok(ErrorCode::E5001),
615            5002 => Ok(ErrorCode::E5002),
616            5003 => Ok(ErrorCode::E5003),
617            5004 => Ok(ErrorCode::E5004),
618            5005 => Ok(ErrorCode::E5005),
619            5006 => Ok(ErrorCode::E5006),
620            6001 => Ok(ErrorCode::E6001),
621            6002 => Ok(ErrorCode::E6002),
622            6003 => Ok(ErrorCode::E6003),
623            6004 => Ok(ErrorCode::E6004),
624            6005 => Ok(ErrorCode::E6005),
625            6006 => Ok(ErrorCode::E6006),
626            7001 => Ok(ErrorCode::E7001),
627            7002 => Ok(ErrorCode::E7002),
628            7003 => Ok(ErrorCode::E7003),
629            7004 => Ok(ErrorCode::E7004),
630            7005 => Ok(ErrorCode::E7005),
631            7006 => Ok(ErrorCode::E7006),
632            8001 => Ok(ErrorCode::E8001),
633            8002 => Ok(ErrorCode::E8002),
634            8003 => Ok(ErrorCode::E8003),
635            8004 => Ok(ErrorCode::E8004),
636            8005 => Ok(ErrorCode::E8005),
637            8006 => Ok(ErrorCode::E8006),
638            9001 => Ok(ErrorCode::E9001),
639            9002 => Ok(ErrorCode::E9002),
640            9003 => Ok(ErrorCode::E9003),
641            9004 => Ok(ErrorCode::E9004),
642            9005 => Ok(ErrorCode::E9005),
643            9006 => Ok(ErrorCode::E9006),
644            _ => Err(()),
645        }
646    }
647
648    fn severity_from_u32(severity: u32) -> Result<ErrorSeverity, ()> {
649        match severity {
650            0 => Ok(ErrorSeverity::Fatal),
651            1 => Ok(ErrorSeverity::Error),
652            2 => Ok(ErrorSeverity::Warning),
653            3 => Ok(ErrorSeverity::Info),
654            _ => Err(()),
655        }
656    }
657}
658
659impl Default for ErrorHandler {
660    fn default() -> Self {
661        Self::new()
662    }
663}
664
665/// Advanced Circuit Breaker for preventing cascading failures
666/// Implements sophisticated failure detection and automatic recovery
667#[derive(Debug, Clone)]
668pub struct CircuitBreaker {
669    failure_threshold: u32,
670    recovery_timeout_ms: u64,
671    half_open_max_calls: u32,
672    failure_count: u32,
673    state: CircuitState,
674    last_failure_time: Option<f64>,
675    success_count: u32,
676}
677
678#[derive(Debug, Clone, Copy, PartialEq)]
679pub enum CircuitState {
680    Closed,   // Normal operation
681    Open,     // Failure state, rejecting calls
682    HalfOpen, // Testing recovery
683}
684
685impl CircuitBreaker {
686    pub fn new(failure_threshold: u32, recovery_timeout_ms: u64, half_open_max_calls: u32) -> Self {
687        Self {
688            failure_threshold,
689            recovery_timeout_ms,
690            half_open_max_calls,
691            failure_count: 0,
692            state: CircuitState::Closed,
693            last_failure_time: None,
694            success_count: 0,
695        }
696    }
697
698    /// Execute operation with circuit breaker protection
699    pub fn execute<F, T, E>(&mut self, operation: F) -> Result<T, CircuitBreakerError<E>>
700    where
701        F: FnOnce() -> Result<T, E>,
702    {
703        if !self.can_execute() {
704            return Err(CircuitBreakerError::CircuitOpen);
705        }
706
707        match operation() {
708            Ok(result) => {
709                self.on_success();
710                Ok(result)
711            },
712            Err(error) => {
713                self.on_failure();
714                Err(CircuitBreakerError::OperationFailed(error))
715            },
716        }
717    }
718
719    fn can_execute(&mut self) -> bool {
720        let current_time = Self::current_time();
721
722        match self.state {
723            CircuitState::Closed => true,
724            CircuitState::Open => {
725                if let Some(last_failure) = self.last_failure_time {
726                    if current_time - last_failure >= self.recovery_timeout_ms as f64 {
727                        self.transition_to_half_open();
728                        true
729                    } else {
730                        false
731                    }
732                } else {
733                    false
734                }
735            },
736            CircuitState::HalfOpen => self.success_count < self.half_open_max_calls,
737        }
738    }
739
740    fn on_success(&mut self) {
741        match self.state {
742            CircuitState::HalfOpen => {
743                self.success_count += 1;
744                if self.success_count >= self.half_open_max_calls {
745                    self.transition_to_closed();
746                }
747            },
748            _ => {
749                // Reset failure count on success
750                if self.failure_count > 0 {
751                    self.failure_count = 0;
752                }
753            },
754        }
755    }
756
757    fn on_failure(&mut self) {
758        self.failure_count += 1;
759        self.last_failure_time = Some(Self::current_time());
760
761        match self.state {
762            CircuitState::Closed => {
763                if self.failure_count >= self.failure_threshold {
764                    self.transition_to_open();
765                }
766            },
767            CircuitState::HalfOpen => {
768                self.transition_to_open();
769            },
770            CircuitState::Open => {
771                // Already open, just update timestamp
772            },
773        }
774    }
775
776    fn transition_to_open(&mut self) {
777        self.state = CircuitState::Open;
778        self.success_count = 0;
779    }
780
781    fn transition_to_half_open(&mut self) {
782        self.state = CircuitState::HalfOpen;
783        self.success_count = 0;
784    }
785
786    fn transition_to_closed(&mut self) {
787        self.state = CircuitState::Closed;
788        self.failure_count = 0;
789        self.success_count = 0;
790        self.last_failure_time = None;
791    }
792
793    fn current_time() -> f64 {
794        js_sys::Date::now()
795    }
796
797    pub fn get_state(&self) -> CircuitState {
798        self.state
799    }
800
801    pub fn get_failure_count(&self) -> u32 {
802        self.failure_count
803    }
804}
805
806#[derive(Debug, Clone)]
807pub enum CircuitBreakerError<E> {
808    CircuitOpen,
809    OperationFailed(E),
810}
811
812/// Advanced Retry Strategy with exponential backoff and jitter
813/// Implements sophisticated retry logic based on error types and system load
814#[derive(Debug, Clone)]
815pub struct RetryStrategy {
816    max_attempts: u32,
817    base_delay_ms: u64,
818    max_delay_ms: u64,
819    backoff_multiplier: f64,
820    jitter_factor: f64,
821    retryable_errors: Vec<ErrorCode>,
822}
823
824impl RetryStrategy {
825    pub fn new() -> Self {
826        Self {
827            max_attempts: 3,
828            base_delay_ms: 100,
829            max_delay_ms: 30000,
830            backoff_multiplier: 2.0,
831            jitter_factor: 0.1,
832            retryable_errors: vec![
833                ErrorCode::E6001, // Network connection failed
834                ErrorCode::E6003, // Download timeout
835                ErrorCode::E3004, // Device context lost
836                ErrorCode::E8006, // WASM runtime error
837                ErrorCode::E7005, // IndexedDB error
838            ],
839        }
840    }
841
842    pub fn with_max_attempts(mut self, attempts: u32) -> Self {
843        self.max_attempts = attempts;
844        self
845    }
846
847    pub fn with_base_delay(mut self, delay_ms: u64) -> Self {
848        self.base_delay_ms = delay_ms;
849        self
850    }
851
852    pub fn with_retryable_errors(mut self, errors: Vec<ErrorCode>) -> Self {
853        self.retryable_errors = errors;
854        self
855    }
856
857    /// Execute operation with intelligent retry logic
858    pub async fn execute_with_retry<F, T, Fut>(&self, operation: F) -> Result<T, TrustformersError>
859    where
860        F: Fn() -> Fut,
861        Fut: std::future::Future<Output = Result<T, TrustformersError>>,
862    {
863        let mut last_error: Option<TrustformersError> = None;
864
865        for attempt in 1..=self.max_attempts {
866            match operation().await {
867                Ok(result) => return Ok(result),
868                Err(error) => {
869                    if !self.is_retryable(&error) || attempt == self.max_attempts {
870                        return Err(error);
871                    }
872
873                    let delay = self.calculate_delay(attempt);
874                    self.sleep(delay).await;
875                    last_error = Some(error);
876                },
877            }
878        }
879
880        Err(last_error
881            .unwrap_or_else(|| TrustformersError::new(ErrorCode::E2002, "Retry strategy failed")))
882    }
883
884    fn is_retryable(&self, error: &TrustformersError) -> bool {
885        self.retryable_errors.contains(&error.code)
886    }
887
888    fn calculate_delay(&self, attempt: u32) -> u64 {
889        let exponential_delay =
890            (self.base_delay_ms as f64 * self.backoff_multiplier.powi(attempt as i32 - 1)) as u64;
891
892        let capped_delay = exponential_delay.min(self.max_delay_ms);
893
894        // Add jitter to prevent thundering herd
895        let jitter = (capped_delay as f64 * self.jitter_factor * self.random()) as u64;
896        capped_delay + jitter
897    }
898
899    fn random(&self) -> f64 {
900        js_sys::Math::random()
901    }
902
903    async fn sleep(&self, ms: u64) {
904        let promise = js_sys::Promise::new(&mut |resolve, _| {
905            web_sys::window()
906                .expect("window should be available in browser context")
907                .set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, ms as i32)
908                .expect("set_timeout should succeed with valid callback");
909        });
910        wasm_bindgen_futures::JsFuture::from(promise)
911            .await
912            .expect("sleep promise should resolve successfully");
913    }
914}
915
916impl Default for RetryStrategy {
917    fn default() -> Self {
918        Self::new()
919    }
920}
921
922/// Intelligent Error Recovery System
923/// Implements context-aware recovery strategies based on error patterns and system state
924#[derive(Debug, Clone)]
925pub struct ErrorRecoverySystem {
926    circuit_breaker: CircuitBreaker,
927    retry_strategy: RetryStrategy,
928    recovery_strategies: std::collections::HashMap<ErrorCode, RecoveryAction>,
929    error_history: Vec<TrustformersError>,
930    max_history_size: usize,
931}
932
933#[derive(Debug, Clone)]
934pub enum RecoveryAction {
935    Retry,
936    FallbackToCPU,
937    ClearCache,
938    ReduceMemoryUsage,
939    ReloadModel,
940    RestartWebGL,
941    SwitchToMinimalMode,
942    NoRecovery,
943}
944
945impl ErrorRecoverySystem {
946    pub fn new() -> Self {
947        let mut recovery_strategies = std::collections::HashMap::new();
948
949        // Configure sophisticated recovery strategies
950        recovery_strategies.insert(ErrorCode::E3001, RecoveryAction::FallbackToCPU);
951        recovery_strategies.insert(ErrorCode::E3002, RecoveryAction::FallbackToCPU);
952        recovery_strategies.insert(ErrorCode::E4001, RecoveryAction::ClearCache);
953        recovery_strategies.insert(ErrorCode::E4002, RecoveryAction::ReduceMemoryUsage);
954        recovery_strategies.insert(ErrorCode::E1004, RecoveryAction::ReloadModel);
955        recovery_strategies.insert(ErrorCode::E6001, RecoveryAction::Retry);
956        recovery_strategies.insert(ErrorCode::E7001, RecoveryAction::ClearCache);
957        recovery_strategies.insert(ErrorCode::E8001, RecoveryAction::RestartWebGL);
958
959        Self {
960            circuit_breaker: CircuitBreaker::new(5, 60000, 3),
961            retry_strategy: RetryStrategy::new(),
962            recovery_strategies,
963            error_history: Vec::new(),
964            max_history_size: 100,
965        }
966    }
967
968    /// Execute operation with comprehensive error handling and recovery
969    #[allow(clippy::result_large_err)]
970    pub async fn execute_with_recovery<F, T, Fut>(
971        &mut self,
972        operation: F,
973    ) -> Result<T, TrustformersError>
974    where
975        F: Fn() -> Fut + Clone,
976        Fut: std::future::Future<Output = Result<T, TrustformersError>>,
977    {
978        let circuit_breaker = &mut self.circuit_breaker;
979        let operation_clone = operation.clone();
980
981        #[allow(clippy::result_large_err)]
982        match circuit_breaker.execute(|| -> Result<(), TrustformersError> {
983            // This is a sync wrapper for the async operation
984            // In real implementation, this would need proper async handling
985            Ok(())
986        }) {
987            Ok(_) => match self.retry_strategy.execute_with_retry(operation).await {
988                Ok(result) => Ok(result),
989                Err(error) => {
990                    self.record_error(error.clone());
991                    self.attempt_recovery(error, operation_clone).await
992                },
993            },
994            Err(CircuitBreakerError::CircuitOpen) => Err(TrustformersError::new(
995                ErrorCode::E2004,
996                "Circuit breaker is open - system in failure state",
997            )),
998            Err(CircuitBreakerError::OperationFailed(_)) => {
999                self.retry_strategy.execute_with_retry(operation).await
1000            },
1001        }
1002    }
1003
1004    async fn attempt_recovery<F, T, Fut>(
1005        &mut self,
1006        error: TrustformersError,
1007        operation: F,
1008    ) -> Result<T, TrustformersError>
1009    where
1010        F: Fn() -> Fut,
1011        Fut: std::future::Future<Output = Result<T, TrustformersError>>,
1012    {
1013        if let Some(recovery_action) = self.recovery_strategies.get(&error.code).cloned() {
1014            match self.execute_recovery_action(recovery_action).await {
1015                Ok(_) => {
1016                    // Retry operation after recovery
1017                    match operation().await {
1018                        Ok(result) => Ok(result),
1019                        Err(retry_error) => {
1020                            self.record_error(retry_error.clone());
1021                            Err(retry_error)
1022                        },
1023                    }
1024                },
1025                Err(recovery_error) => {
1026                    // Recovery failed, return original error with additional context
1027                    let mut enhanced_error = error;
1028                    enhanced_error.recovery_suggestion =
1029                        Some(format!("Recovery action failed: {:?}", recovery_error));
1030                    Err(enhanced_error)
1031                },
1032            }
1033        } else {
1034            // No recovery strategy, apply intelligent fallback based on error pattern analysis
1035            self.apply_intelligent_fallback(error, operation).await
1036        }
1037    }
1038
1039    async fn execute_recovery_action(&self, action: RecoveryAction) -> Result<(), String> {
1040        match action {
1041            RecoveryAction::FallbackToCPU => {
1042                // Switch to CPU-only mode
1043                Ok(())
1044            },
1045            RecoveryAction::ClearCache => {
1046                // Clear various caches - implementation would clear model caches
1047                // This is a placeholder for actual cache clearing logic
1048                Ok(())
1049            },
1050            RecoveryAction::ReduceMemoryUsage => {
1051                // Implement memory reduction strategies
1052                if let Some(window) = web_sys::window() {
1053                    if let Some(performance) = window.performance() {
1054                        // Force garbage collection if available
1055                        if js_sys::Reflect::has(&performance, &"gc".into()).unwrap_or(false) {
1056                            if let Ok(gc_fn) = js_sys::Reflect::get(&performance, &"gc".into()) {
1057                                if let Ok(gc_fn) = gc_fn.dyn_into::<js_sys::Function>() {
1058                                    let _ = gc_fn.call0(&performance);
1059                                }
1060                            }
1061                        }
1062                    }
1063                }
1064                Ok(())
1065            },
1066            RecoveryAction::ReloadModel => {
1067                // Reload model with recovery
1068                Ok(())
1069            },
1070            RecoveryAction::RestartWebGL => {
1071                // Restart WebGL context
1072                Ok(())
1073            },
1074            RecoveryAction::SwitchToMinimalMode => {
1075                // Switch to minimal functionality mode
1076                Ok(())
1077            },
1078            RecoveryAction::Retry => {
1079                // Simple retry - handled by retry strategy
1080                Ok(())
1081            },
1082            RecoveryAction::NoRecovery => Err("No recovery action available".to_string()),
1083        }
1084    }
1085
1086    async fn apply_intelligent_fallback<F, T, Fut>(
1087        &self,
1088        error: TrustformersError,
1089        operation: F,
1090    ) -> Result<T, TrustformersError>
1091    where
1092        F: Fn() -> Fut,
1093        Fut: std::future::Future<Output = Result<T, TrustformersError>>,
1094    {
1095        // Analyze error patterns to determine intelligent fallback
1096        let error_pattern = self.analyze_error_patterns(&error);
1097
1098        match error_pattern {
1099            ErrorPattern::MemoryRelated => {
1100                // Apply memory-conscious fallback
1101                self.execute_recovery_action(RecoveryAction::ReduceMemoryUsage).await.ok();
1102                operation().await
1103            },
1104            ErrorPattern::DeviceRelated => {
1105                // Apply device fallback
1106                self.execute_recovery_action(RecoveryAction::FallbackToCPU).await.ok();
1107                operation().await
1108            },
1109            ErrorPattern::NetworkRelated => {
1110                // Apply network fallback with cache
1111                operation().await
1112            },
1113            ErrorPattern::Unknown => Err(error),
1114        }
1115    }
1116
1117    fn analyze_error_patterns(&self, current_error: &TrustformersError) -> ErrorPattern {
1118        // Sophisticated pattern analysis based on error history
1119        let memory_errors = [ErrorCode::E4001, ErrorCode::E4002, ErrorCode::E4003];
1120        let device_errors = [
1121            ErrorCode::E3001,
1122            ErrorCode::E3002,
1123            ErrorCode::E3003,
1124            ErrorCode::E3004,
1125        ];
1126        let network_errors = [ErrorCode::E6001, ErrorCode::E6002, ErrorCode::E6003];
1127
1128        if memory_errors.contains(&current_error.code) {
1129            ErrorPattern::MemoryRelated
1130        } else if device_errors.contains(&current_error.code) {
1131            ErrorPattern::DeviceRelated
1132        } else if network_errors.contains(&current_error.code) {
1133            ErrorPattern::NetworkRelated
1134        } else {
1135            ErrorPattern::Unknown
1136        }
1137    }
1138
1139    fn record_error(&mut self, error: TrustformersError) {
1140        self.error_history.push(error);
1141        if self.error_history.len() > self.max_history_size {
1142            self.error_history.drain(0..self.error_history.len() - self.max_history_size);
1143        }
1144    }
1145
1146    pub fn get_error_statistics(&self) -> ErrorStatistics {
1147        let total_errors = self.error_history.len();
1148        let mut error_counts = std::collections::HashMap::new();
1149
1150        for error in &self.error_history {
1151            *error_counts.entry(error.code).or_insert(0) += 1;
1152        }
1153
1154        let most_common_error = error_counts
1155            .iter()
1156            .max_by_key(|(_, count)| *count)
1157            .map(|(code, count)| (*code, *count));
1158
1159        ErrorStatistics {
1160            total_errors,
1161            error_counts,
1162            most_common_error,
1163            circuit_breaker_state: self.circuit_breaker.get_state(),
1164        }
1165    }
1166}
1167
1168#[derive(Debug, Clone, Copy)]
1169enum ErrorPattern {
1170    MemoryRelated,
1171    DeviceRelated,
1172    NetworkRelated,
1173    Unknown,
1174}
1175
1176#[derive(Debug, Clone)]
1177pub struct ErrorStatistics {
1178    pub total_errors: usize,
1179    pub error_counts: std::collections::HashMap<ErrorCode, u32>,
1180    pub most_common_error: Option<(ErrorCode, u32)>,
1181    pub circuit_breaker_state: CircuitState,
1182}
1183
1184impl Default for ErrorRecoverySystem {
1185    fn default() -> Self {
1186        Self::new()
1187    }
1188}
1189
1190#[cfg(test)]
1191mod tests {
1192    use super::*;
1193
1194    #[test]
1195    #[cfg(target_arch = "wasm32")]
1196    fn test_error_creation() {
1197        let error = TrustformersError::new(ErrorCode::E1001, "Test error");
1198        assert_eq!(error.code, ErrorCode::E1001);
1199        assert_eq!(error.message, "Test error");
1200        assert_eq!(error.severity, ErrorSeverity::Error);
1201    }
1202
1203    #[test]
1204    #[cfg(target_arch = "wasm32")]
1205    fn test_error_builder() {
1206        let error = ErrorBuilder::new(ErrorCode::E2001, "Shape mismatch")
1207            .operation("predict")
1208            .component("inference_session")
1209            .input_shape(vec![1, 2, 3])
1210            .build();
1211
1212        assert_eq!(error.code, ErrorCode::E2001);
1213        assert_eq!(error.context.operation, Some("predict".to_owned()));
1214        assert_eq!(error.context.input_shape, Some(vec![1, 2, 3]));
1215    }
1216
1217    #[test]
1218    #[cfg(target_arch = "wasm32")]
1219    fn test_error_collection() {
1220        let mut collection = ErrorCollection::new();
1221
1222        collection.add(TrustformersError::new(ErrorCode::E1001, "Error 1"));
1223        collection.add(TrustformersError::new(ErrorCode::E4001, "Fatal error"));
1224
1225        assert_eq!(collection.len(), 2);
1226        assert!(collection.has_errors());
1227        assert!(collection.has_fatal_errors());
1228    }
1229
1230    #[test]
1231    #[cfg(target_arch = "wasm32")]
1232    fn test_error_category() {
1233        let error = TrustformersError::new(ErrorCode::E1001, "Model error");
1234        assert_eq!(error.category(), "Model");
1235
1236        let error = TrustformersError::new(ErrorCode::E3001, "Device error");
1237        assert_eq!(error.category(), "Device");
1238    }
1239
1240    #[test]
1241    #[cfg(not(target_arch = "wasm32"))]
1242    fn test_error_codes() {
1243        // Test error code values and categories for non-WASM targets
1244        assert_eq!(ErrorCode::E1001 as u32, 1001);
1245        assert_eq!(ErrorCode::E2001 as u32, 2001);
1246        assert_eq!(ErrorCode::E3001 as u32, 3001);
1247        assert_eq!(ErrorCode::E4001 as u32, 4001);
1248    }
1249
1250    #[test]
1251    #[cfg(not(target_arch = "wasm32"))]
1252    fn test_error_severity() {
1253        // Test error severity values for non-WASM targets
1254        use ErrorSeverity::*;
1255        assert_ne!(Info, Error);
1256        assert_ne!(Warning, Fatal);
1257    }
1258}