quantacore/
error.rs

1//! Error types for QUAC 100 SDK.
2//!
3//! This module provides error handling through the `QuacError` type and
4//! the `ErrorCode` enum which maps to the native library's error codes.
5
6use std::fmt;
7use thiserror::Error;
8
9/// Result type alias for QUAC 100 operations.
10pub type Result<T> = std::result::Result<T, QuacError>;
11
12/// Error codes returned by QUAC 100 operations.
13///
14/// These map directly to the native library's error codes.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16#[repr(i32)]
17pub enum ErrorCode {
18    /// Operation completed successfully
19    Success = 0,
20    /// Generic error
21    Error = -1,
22    /// Invalid parameter
23    InvalidParam = -2,
24    /// Output buffer too small
25    BufferSmall = -3,
26    /// No QUAC 100 device found
27    DeviceNotFound = -4,
28    /// Device is busy
29    DeviceBusy = -5,
30    /// Device error
31    DeviceError = -6,
32    /// Memory allocation failed
33    OutOfMemory = -7,
34    /// Operation not supported
35    NotSupported = -8,
36    /// Authentication required
37    AuthRequired = -9,
38    /// Authentication failed
39    AuthFailed = -10,
40    /// Key not found
41    KeyNotFound = -11,
42    /// Invalid key
43    InvalidKey = -12,
44    /// Signature verification failed
45    VerificationFailed = -13,
46    /// Decapsulation failed
47    DecapsFailed = -14,
48    /// Hardware acceleration unavailable
49    HardwareUnavail = -15,
50    /// Operation timed out
51    Timeout = -16,
52    /// Library not initialized
53    NotInitialized = -17,
54    /// Library already initialized
55    AlreadyInit = -18,
56    /// Invalid handle
57    InvalidHandle = -19,
58    /// Operation cancelled
59    Cancelled = -20,
60    /// Entropy pool depleted
61    EntropyDepleted = -21,
62    /// Self-test failed
63    SelfTestFailed = -22,
64    /// Tamper detected
65    TamperDetected = -23,
66    /// Temperature error
67    Temperature = -24,
68    /// Power supply error
69    Power = -25,
70    /// Invalid algorithm
71    InvalidAlgorithm = -26,
72    /// Cryptographic operation error
73    CryptoError = -27,
74    /// Internal error
75    InternalError = -99,
76}
77
78impl ErrorCode {
79    /// Get human-readable message for this error code.
80    pub fn message(&self) -> &'static str {
81        match self {
82            Self::Success => "Operation completed successfully",
83            Self::Error => "Generic error",
84            Self::InvalidParam => "Invalid parameter",
85            Self::BufferSmall => "Output buffer too small",
86            Self::DeviceNotFound => "No QUAC 100 device found",
87            Self::DeviceBusy => "Device is busy",
88            Self::DeviceError => "Device error",
89            Self::OutOfMemory => "Memory allocation failed",
90            Self::NotSupported => "Operation not supported",
91            Self::AuthRequired => "Authentication required",
92            Self::AuthFailed => "Authentication failed",
93            Self::KeyNotFound => "Key not found",
94            Self::InvalidKey => "Invalid key",
95            Self::VerificationFailed => "Signature verification failed",
96            Self::DecapsFailed => "Decapsulation failed",
97            Self::HardwareUnavail => "Hardware acceleration unavailable",
98            Self::Timeout => "Operation timed out",
99            Self::NotInitialized => "Library not initialized",
100            Self::AlreadyInit => "Library already initialized",
101            Self::InvalidHandle => "Invalid handle",
102            Self::Cancelled => "Operation cancelled",
103            Self::EntropyDepleted => "Entropy pool depleted",
104            Self::SelfTestFailed => "Self-test failed",
105            Self::TamperDetected => "Tamper detected",
106            Self::Temperature => "Temperature error",
107            Self::Power => "Power supply error",
108            Self::InvalidAlgorithm => "Invalid algorithm",
109            Self::CryptoError => "Cryptographic operation error",
110            Self::InternalError => "Internal error",
111        }
112    }
113
114    /// Create from raw i32 value.
115    pub fn from_raw(code: i32) -> Self {
116        match code {
117            0 => Self::Success,
118            -1 => Self::Error,
119            -2 => Self::InvalidParam,
120            -3 => Self::BufferSmall,
121            -4 => Self::DeviceNotFound,
122            -5 => Self::DeviceBusy,
123            -6 => Self::DeviceError,
124            -7 => Self::OutOfMemory,
125            -8 => Self::NotSupported,
126            -9 => Self::AuthRequired,
127            -10 => Self::AuthFailed,
128            -11 => Self::KeyNotFound,
129            -12 => Self::InvalidKey,
130            -13 => Self::VerificationFailed,
131            -14 => Self::DecapsFailed,
132            -15 => Self::HardwareUnavail,
133            -16 => Self::Timeout,
134            -17 => Self::NotInitialized,
135            -18 => Self::AlreadyInit,
136            -19 => Self::InvalidHandle,
137            -20 => Self::Cancelled,
138            -21 => Self::EntropyDepleted,
139            -22 => Self::SelfTestFailed,
140            -23 => Self::TamperDetected,
141            -24 => Self::Temperature,
142            -25 => Self::Power,
143            -26 => Self::InvalidAlgorithm,
144            -27 => Self::CryptoError,
145            _ => Self::InternalError,
146        }
147    }
148
149    /// Convert to raw i32 value.
150    pub fn to_raw(self) -> i32 {
151        self as i32
152    }
153
154    /// Check if this is a success code.
155    pub fn is_success(self) -> bool {
156        self == Self::Success
157    }
158}
159
160impl fmt::Display for ErrorCode {
161    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162        write!(f, "{}", self.message())
163    }
164}
165
166/// Main error type for QUAC 100 operations.
167#[derive(Error, Debug)]
168pub enum QuacError {
169    /// Error from the native library
170    #[error("QUAC 100 error ({code:?}): {message}")]
171    Native {
172        /// The error code
173        code: ErrorCode,
174        /// Human-readable message
175        message: String,
176    },
177
178    /// Device not found
179    #[error("Device not found: {0}")]
180    DeviceNotFound(String),
181
182    /// Invalid parameter
183    #[error("Invalid parameter: {0}")]
184    InvalidParameter(String),
185
186    /// Verification failed
187    #[error("Verification failed")]
188    VerificationFailed,
189
190    /// Decapsulation failed
191    #[error("Decapsulation failed")]
192    DecapsulationFailed,
193
194    /// Library not initialized
195    #[error("Library not initialized")]
196    NotInitialized,
197
198    /// Buffer too small
199    #[error("Buffer too small: need {needed} bytes, got {got}")]
200    BufferTooSmall {
201        /// Bytes needed
202        needed: usize,
203        /// Bytes provided
204        got: usize,
205    },
206
207    /// I/O error
208    #[error("I/O error: {0}")]
209    Io(#[from] std::io::Error),
210
211    /// UTF-8 conversion error
212    #[error("UTF-8 error: {0}")]
213    Utf8(#[from] std::str::Utf8Error),
214
215    /// Null pointer error
216    #[error("Null pointer encountered")]
217    NullPointer,
218
219    /// Internal error
220    #[error("Internal error: {0}")]
221    Internal(String),
222}
223
224impl QuacError {
225    /// Create a new native error from an error code.
226    pub fn from_code(code: ErrorCode) -> Self {
227        Self::Native {
228            message: code.message().to_string(),
229            code,
230        }
231    }
232
233    /// Create a new native error from a raw error code.
234    pub fn from_raw(code: i32) -> Self {
235        Self::from_code(ErrorCode::from_raw(code))
236    }
237
238    /// Get the error code if this is a native error.
239    pub fn code(&self) -> Option<ErrorCode> {
240        match self {
241            Self::Native { code, .. } => Some(*code),
242            Self::DeviceNotFound(_) => Some(ErrorCode::DeviceNotFound),
243            Self::InvalidParameter(_) => Some(ErrorCode::InvalidParam),
244            Self::VerificationFailed => Some(ErrorCode::VerificationFailed),
245            Self::DecapsulationFailed => Some(ErrorCode::DecapsFailed),
246            Self::NotInitialized => Some(ErrorCode::NotInitialized),
247            Self::BufferTooSmall { .. } => Some(ErrorCode::BufferSmall),
248            _ => None,
249        }
250    }
251
252    /// Check if this is a verification error.
253    pub fn is_verification_error(&self) -> bool {
254        matches!(
255            self,
256            Self::VerificationFailed | Self::Native { code: ErrorCode::VerificationFailed, .. }
257        )
258    }
259}
260
261/// Check a raw return code and convert to Result.
262pub(crate) fn check_error(code: i32) -> Result<()> {
263    if code == 0 {
264        Ok(())
265    } else {
266        Err(QuacError::from_raw(code))
267    }
268}
269
270#[cfg(test)]
271mod tests {
272    use super::*;
273
274    #[test]
275    fn test_error_code_from_raw() {
276        assert_eq!(ErrorCode::from_raw(0), ErrorCode::Success);
277        assert_eq!(ErrorCode::from_raw(-2), ErrorCode::InvalidParam);
278        assert_eq!(ErrorCode::from_raw(-17), ErrorCode::NotInitialized);
279        assert_eq!(ErrorCode::from_raw(-999), ErrorCode::InternalError);
280    }
281
282    #[test]
283    fn test_error_code_message() {
284        assert_eq!(ErrorCode::Success.message(), "Operation completed successfully");
285        assert!(ErrorCode::InvalidParam.message().contains("Invalid"));
286    }
287
288    #[test]
289    fn test_quac_error_from_code() {
290        let err = QuacError::from_code(ErrorCode::DeviceNotFound);
291        assert_eq!(err.code(), Some(ErrorCode::DeviceNotFound));
292    }
293
294    #[test]
295    fn test_check_error() {
296        assert!(check_error(0).is_ok());
297        assert!(check_error(-1).is_err());
298    }
299}