Skip to main content

pokeys_lib/
error.rs

1//! Error types for PoKeys library
2
3use thiserror::Error;
4
5/// Result type used throughout the library
6pub type Result<T> = std::result::Result<T, PoKeysError>;
7
8/// Main error type for PoKeys operations
9#[derive(Error, Debug)]
10pub enum PoKeysError {
11    #[error("Generic error")]
12    Generic,
13
14    #[error("Device not found")]
15    DeviceNotFound,
16
17    #[error("Device not connected")]
18    NotConnected,
19
20    #[error("Connection failed")]
21    ConnectionFailed,
22
23    #[error("Communication error")]
24    CommunicationError,
25
26    #[error("Transfer error: {0}")]
27    Transfer(String),
28
29    #[error("Invalid parameter")]
30    InvalidParameter,
31
32    #[error("Invalid parameter: {0}")]
33    Parameter(String),
34
35    #[error("Operation not supported")]
36    NotSupported,
37
38    #[error("Unsupported operation")]
39    UnsupportedOperation,
40
41    #[error("Cannot claim USB device")]
42    CannotClaimUsb,
43
44    #[error("Cannot connect to device")]
45    CannotConnect,
46
47    #[error("IO error: {0}")]
48    Io(#[from] std::io::Error),
49
50    #[error("Invalid checksum")]
51    InvalidChecksum,
52
53    #[error("Invalid response")]
54    InvalidResponse,
55
56    #[error("Timeout")]
57    Timeout,
58
59    #[error("Device enumeration failed")]
60    EnumerationFailed,
61
62    #[error("Invalid device handle")]
63    InvalidHandle,
64
65    #[error("Protocol error: {0}")]
66    Protocol(String),
67
68    #[error("Internal error: {0}")]
69    InternalError(String),
70
71    // Model-related errors
72    #[error("Failed to load model from {0}: {1}")]
73    ModelLoadError(String, String),
74
75    #[error("Failed to parse model from {0}: {1}")]
76    ModelParseError(String, String),
77
78    #[error("Model validation error: {0}")]
79    ModelValidationError(String),
80
81    #[error("Failed to create model directory {0}: {1}")]
82    ModelDirCreateError(String, String),
83
84    #[error("Failed to read model directory {0}: {1}")]
85    ModelDirReadError(String, String),
86
87    #[error("Model watcher error: {0}")]
88    ModelWatcherError(String),
89
90    #[error("Pin {0} does not support capability: {1}")]
91    UnsupportedPinCapability(u8, String),
92
93    #[error("Missing related capability: Pin {0} with capability {1} requires {2}")]
94    MissingRelatedCapability(u8, String, String),
95
96    #[error("Related pin {0} with capability {1} is inactive")]
97    RelatedPinInactive(u8, String),
98
99    #[error("Related capability validation failed: {0}")]
100    RelatedCapabilityError(String),
101
102    // Pin management errors
103    #[error("Pin conflict: {0}")]
104    PinConflict(String),
105
106    #[error("Invalid pin: {0}")]
107    InvalidPin(u8),
108
109    #[error("Invalid configuration: {0}")]
110    InvalidConfiguration(String),
111
112    // Enhanced I2C errors (Priority 1)
113    #[error("I2C packet too large: {size} bytes (maximum {max_size} bytes). {suggestion}")]
114    I2cPacketTooLarge {
115        size: usize,
116        max_size: usize,
117        suggestion: String,
118    },
119
120    #[error("I2C timeout")]
121    I2cTimeout,
122
123    #[error("I2C bus error")]
124    I2cBusError,
125
126    #[error("I2C NACK received")]
127    I2cNack,
128
129    #[error("Network timeout")]
130    NetworkTimeout,
131
132    #[error("Maximum retries exceeded")]
133    MaxRetriesExceeded,
134
135    // Enhanced validation errors (Priority 3)
136    #[error("Invalid packet structure: {0}")]
137    InvalidPacketStructure(String),
138
139    #[error("Invalid command: 0x{0:02X}")]
140    InvalidCommand(u8),
141
142    #[error("Invalid device ID: {0}")]
143    InvalidDeviceId(u8),
144
145    #[error("Invalid checksum: expected 0x{expected:02X}, received 0x{received:02X}")]
146    InvalidChecksumDetailed { expected: u8, received: u8 },
147
148    // uSPIBridge-specific errors
149    #[error("Invalid segment mapping: {0}")]
150    InvalidSegmentMapping(String),
151
152    #[error("Segment mapping not supported by device")]
153    SegmentMappingNotSupported,
154
155    #[error("Custom pinout configuration error: {0}")]
156    CustomPinoutError(String),
157
158    #[error("uSPIBridge command failed: {0}")]
159    USPIBridgeCommandFailed(String),
160
161    #[error("Virtual device error: {0}")]
162    VirtualDeviceError(String),
163
164    #[error("Invalid virtual device ID: {id} (maximum: {max})")]
165    InvalidVirtualDeviceId { id: u8, max: u8 },
166}
167
168/// Recovery strategies for different error types
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
170pub enum RecoveryStrategy {
171    Fail,
172    RetryWithDelay(u64), // milliseconds
173    RetryWithBackoff,
174    ResetAndRetry,
175}
176
177impl PoKeysError {
178    /// Check if an error is recoverable through retry mechanisms
179    pub fn is_recoverable(&self) -> bool {
180        matches!(
181            self,
182            PoKeysError::I2cTimeout
183                | PoKeysError::I2cBusError
184                | PoKeysError::I2cNack
185                | PoKeysError::NetworkTimeout
186                | PoKeysError::Timeout
187                | PoKeysError::CommunicationError
188                | PoKeysError::USPIBridgeCommandFailed(_)
189        )
190    }
191
192    /// Get the recommended recovery strategy for this error
193    pub fn recovery_strategy(&self) -> RecoveryStrategy {
194        match self {
195            PoKeysError::I2cTimeout => RecoveryStrategy::RetryWithDelay(100),
196            PoKeysError::I2cBusError => RecoveryStrategy::ResetAndRetry,
197            PoKeysError::I2cNack => RecoveryStrategy::RetryWithBackoff,
198            PoKeysError::NetworkTimeout => RecoveryStrategy::RetryWithDelay(200),
199            PoKeysError::Timeout => RecoveryStrategy::RetryWithDelay(100),
200            PoKeysError::CommunicationError => RecoveryStrategy::RetryWithBackoff,
201            _ => RecoveryStrategy::Fail,
202        }
203    }
204}
205
206impl PartialEq for PoKeysError {
207    fn eq(&self, other: &Self) -> bool {
208        match (self, other) {
209            (Self::Generic, Self::Generic) => true,
210            (Self::DeviceNotFound, Self::DeviceNotFound) => true,
211            (Self::NotConnected, Self::NotConnected) => true,
212            (Self::ConnectionFailed, Self::ConnectionFailed) => true,
213            (Self::CommunicationError, Self::CommunicationError) => true,
214            (Self::Transfer(a), Self::Transfer(b)) => a == b,
215            (Self::InvalidParameter, Self::InvalidParameter) => true,
216            (Self::Parameter(a), Self::Parameter(b)) => a == b,
217            (Self::NotSupported, Self::NotSupported) => true,
218            (Self::UnsupportedOperation, Self::UnsupportedOperation) => true,
219            (Self::CannotClaimUsb, Self::CannotClaimUsb) => true,
220            (Self::CannotConnect, Self::CannotConnect) => true,
221            (Self::InvalidChecksum, Self::InvalidChecksum) => true,
222            (Self::InvalidResponse, Self::InvalidResponse) => true,
223            (Self::Timeout, Self::Timeout) => true,
224            (Self::EnumerationFailed, Self::EnumerationFailed) => true,
225            (Self::InvalidHandle, Self::InvalidHandle) => true,
226            (Self::Protocol(a), Self::Protocol(b)) => a == b,
227            (Self::InternalError(a), Self::InternalError(b)) => a == b,
228            // Model-related errors
229            (Self::ModelLoadError(a1, b1), Self::ModelLoadError(a2, b2)) => a1 == a2 && b1 == b2,
230            (Self::ModelParseError(a1, b1), Self::ModelParseError(a2, b2)) => a1 == a2 && b1 == b2,
231            (Self::ModelValidationError(a), Self::ModelValidationError(b)) => a == b,
232            (Self::ModelDirCreateError(a1, b1), Self::ModelDirCreateError(a2, b2)) => {
233                a1 == a2 && b1 == b2
234            }
235            (Self::ModelDirReadError(a1, b1), Self::ModelDirReadError(a2, b2)) => {
236                a1 == a2 && b1 == b2
237            }
238            (Self::ModelWatcherError(a), Self::ModelWatcherError(b)) => a == b,
239            (Self::UnsupportedPinCapability(a1, b1), Self::UnsupportedPinCapability(a2, b2)) => {
240                a1 == a2 && b1 == b2
241            }
242            (
243                Self::MissingRelatedCapability(a1, b1, c1),
244                Self::MissingRelatedCapability(a2, b2, c2),
245            ) => a1 == a2 && b1 == b2 && c1 == c2,
246            (Self::RelatedPinInactive(a1, b1), Self::RelatedPinInactive(a2, b2)) => {
247                a1 == a2 && b1 == b2
248            }
249            (Self::RelatedCapabilityError(a), Self::RelatedCapabilityError(b)) => a == b,
250            // Enhanced I2C errors
251            (
252                Self::I2cPacketTooLarge {
253                    size: s1,
254                    max_size: m1,
255                    suggestion: sg1,
256                },
257                Self::I2cPacketTooLarge {
258                    size: s2,
259                    max_size: m2,
260                    suggestion: sg2,
261                },
262            ) => s1 == s2 && m1 == m2 && sg1 == sg2,
263            (Self::I2cTimeout, Self::I2cTimeout) => true,
264            (Self::I2cBusError, Self::I2cBusError) => true,
265            (Self::I2cNack, Self::I2cNack) => true,
266            (Self::NetworkTimeout, Self::NetworkTimeout) => true,
267            (Self::MaxRetriesExceeded, Self::MaxRetriesExceeded) => true,
268            // Enhanced validation errors
269            (Self::InvalidPacketStructure(a), Self::InvalidPacketStructure(b)) => a == b,
270            (Self::InvalidCommand(a), Self::InvalidCommand(b)) => a == b,
271            (Self::InvalidDeviceId(a), Self::InvalidDeviceId(b)) => a == b,
272            (
273                Self::InvalidChecksumDetailed {
274                    expected: e1,
275                    received: r1,
276                },
277                Self::InvalidChecksumDetailed {
278                    expected: e2,
279                    received: r2,
280                },
281            ) => e1 == e2 && r1 == r2,
282            // uSPIBridge-specific errors
283            (Self::InvalidSegmentMapping(a), Self::InvalidSegmentMapping(b)) => a == b,
284            (Self::SegmentMappingNotSupported, Self::SegmentMappingNotSupported) => true,
285            (Self::CustomPinoutError(a), Self::CustomPinoutError(b)) => a == b,
286            (Self::USPIBridgeCommandFailed(a), Self::USPIBridgeCommandFailed(b)) => a == b,
287            (Self::VirtualDeviceError(a), Self::VirtualDeviceError(b)) => a == b,
288            (
289                Self::InvalidVirtualDeviceId { id: i1, max: m1 },
290                Self::InvalidVirtualDeviceId { id: i2, max: m2 },
291            ) => i1 == i2 && m1 == m2,
292            // IO errors are not compared
293            (Self::Io(_), Self::Io(_)) => false,
294            _ => false,
295        }
296    }
297}
298
299/// Return codes matching the original library
300#[repr(i32)]
301#[derive(Debug, Clone, Copy, PartialEq, Eq)]
302pub enum ReturnCode {
303    Ok = 0,
304    ErrGeneric = -1,
305    ErrNotConnected = -5,
306    ErrTransfer = -10,
307    ErrParameter = -20,
308    ErrNotSupported = -30,
309    ErrCannotClaimUsb = -100,
310    ErrCannotConnect = -101,
311}
312
313impl From<PoKeysError> for ReturnCode {
314    fn from(error: PoKeysError) -> Self {
315        match error {
316            PoKeysError::Generic => ReturnCode::ErrGeneric,
317            PoKeysError::NotConnected => ReturnCode::ErrNotConnected,
318            PoKeysError::Transfer(_) => ReturnCode::ErrTransfer,
319            PoKeysError::Parameter(_) | PoKeysError::InvalidParameter => ReturnCode::ErrParameter,
320            PoKeysError::NotSupported | PoKeysError::UnsupportedOperation => {
321                ReturnCode::ErrNotSupported
322            }
323            PoKeysError::CannotClaimUsb => ReturnCode::ErrCannotClaimUsb,
324            PoKeysError::CannotConnect | PoKeysError::ConnectionFailed => {
325                ReturnCode::ErrCannotConnect
326            }
327            // Model-related errors map to generic or parameter errors
328            PoKeysError::UnsupportedPinCapability(_, _)
329            | PoKeysError::MissingRelatedCapability(_, _, _)
330            | PoKeysError::RelatedPinInactive(_, _)
331            | PoKeysError::RelatedCapabilityError(_) => ReturnCode::ErrParameter,
332            // Enhanced I2C errors
333            PoKeysError::I2cPacketTooLarge { .. } => ReturnCode::ErrParameter,
334            PoKeysError::I2cTimeout | PoKeysError::I2cBusError | PoKeysError::I2cNack => {
335                ReturnCode::ErrTransfer
336            }
337            PoKeysError::NetworkTimeout => ReturnCode::ErrTransfer,
338            PoKeysError::MaxRetriesExceeded => ReturnCode::ErrTransfer,
339            // Enhanced validation errors
340            PoKeysError::InvalidPacketStructure(_)
341            | PoKeysError::InvalidCommand(_)
342            | PoKeysError::InvalidDeviceId(_)
343            | PoKeysError::InvalidChecksumDetailed { .. } => ReturnCode::ErrParameter,
344            // uSPIBridge-specific errors
345            PoKeysError::InvalidSegmentMapping(_)
346            | PoKeysError::SegmentMappingNotSupported
347            | PoKeysError::CustomPinoutError(_)
348            | PoKeysError::VirtualDeviceError(_)
349            | PoKeysError::InvalidVirtualDeviceId { .. } => ReturnCode::ErrParameter,
350            PoKeysError::USPIBridgeCommandFailed(_) => ReturnCode::ErrTransfer,
351            _ => ReturnCode::ErrGeneric,
352        }
353    }
354}
355
356impl From<ReturnCode> for PoKeysError {
357    fn from(code: ReturnCode) -> Self {
358        match code {
359            ReturnCode::Ok => PoKeysError::Generic, // This shouldn't happen
360            ReturnCode::ErrGeneric => PoKeysError::Generic,
361            ReturnCode::ErrNotConnected => PoKeysError::NotConnected,
362            ReturnCode::ErrTransfer => PoKeysError::Transfer("Transfer failed".to_string()),
363            ReturnCode::ErrParameter => PoKeysError::Parameter("Invalid parameter".to_string()),
364            ReturnCode::ErrNotSupported => PoKeysError::NotSupported,
365            ReturnCode::ErrCannotClaimUsb => PoKeysError::CannotClaimUsb,
366            ReturnCode::ErrCannotConnect => PoKeysError::CannotConnect,
367        }
368    }
369}