1use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, PoKeysError>;
7
8#[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 #[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 #[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 #[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 #[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 #[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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
170pub enum RecoveryStrategy {
171 Fail,
172 RetryWithDelay(u64), RetryWithBackoff,
174 ResetAndRetry,
175}
176
177impl PoKeysError {
178 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 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 (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 (
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 (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 (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 (Self::Io(_), Self::Io(_)) => false,
294 _ => false,
295 }
296 }
297}
298
299#[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 PoKeysError::UnsupportedPinCapability(_, _)
329 | PoKeysError::MissingRelatedCapability(_, _, _)
330 | PoKeysError::RelatedPinInactive(_, _)
331 | PoKeysError::RelatedCapabilityError(_) => ReturnCode::ErrParameter,
332 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 PoKeysError::InvalidPacketStructure(_)
341 | PoKeysError::InvalidCommand(_)
342 | PoKeysError::InvalidDeviceId(_)
343 | PoKeysError::InvalidChecksumDetailed { .. } => ReturnCode::ErrParameter,
344 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, 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}