Skip to main content

lib_q_random/
error.rs

1// Allow clippy warnings in error handling code
2// These are legitimate patterns for error reporting and formatting
3#![allow(
4    clippy::uninlined_format_args,
5    clippy::must_use_candidate,
6    clippy::too_many_lines
7)]
8
9//! Error types for lib-q-random
10//!
11//! This module defines comprehensive error types for random number generation
12//! operations, providing detailed information about failure modes and recovery
13//! strategies.
14
15#[cfg(all(not(feature = "std"), feature = "alloc"))]
16use alloc::string::{
17    String,
18    ToString,
19};
20use core::fmt;
21
22/// Result type alias for lib-q-random operations
23pub type Result<T> = core::result::Result<T, Error>;
24
25/// Comprehensive error types for RNG operations
26///
27/// This enum provides detailed error information for various failure modes
28/// in random number generation, enabling proper error handling and recovery.
29#[derive(Debug, Clone, PartialEq)]
30pub enum Error {
31    /// Entropy source is not available or failed
32    EntropySourceUnavailable {
33        /// Description of the entropy source failure
34        source: &'static str,
35        /// Additional context about the failure
36        context: Option<&'static str>,
37    },
38
39    /// Insufficient entropy for cryptographic operations
40    InsufficientEntropy {
41        /// Required entropy bits
42        required: usize,
43        /// Available entropy bits
44        available: usize,
45        /// Quality assessment of available entropy
46        quality: f64,
47    },
48
49    /// Entropy validation failed
50    EntropyValidationFailed {
51        /// Reason for validation failure
52        reason: &'static str,
53        /// Entropy quality score (0.0 to 1.0)
54        quality: f64,
55        /// Additional validation details
56        details: Option<&'static str>,
57    },
58
59    /// Hardware RNG failure
60    HardwareRngFailed {
61        /// Hardware device identifier
62        device: &'static str,
63        /// Error code or status
64        status: Option<u32>,
65        /// Additional error details
66        details: Option<&'static str>,
67    },
68
69    /// Platform-specific RNG failure
70    PlatformRngFailed {
71        /// Platform identifier
72        platform: &'static str,
73        /// Error code or status
74        code: Option<i32>,
75        /// Additional error details
76        details: Option<&'static str>,
77    },
78
79    /// Invalid configuration or parameters
80    InvalidConfiguration {
81        /// Parameter that caused the error
82        parameter: &'static str,
83        /// Expected value or range
84        #[cfg(feature = "alloc")]
85        expected: String,
86        /// Expected value or range (`no_std` version)
87        #[cfg(not(feature = "alloc"))]
88        expected: &'static str,
89        /// Actual value provided
90        #[cfg(feature = "alloc")]
91        actual: String,
92        /// Actual value provided (`no_std` version)
93        #[cfg(not(feature = "alloc"))]
94        actual: &'static str,
95    },
96
97    /// Memory allocation failure
98    MemoryAllocationFailed {
99        /// Size of allocation that failed
100        size: usize,
101        /// Additional context
102        context: Option<&'static str>,
103    },
104
105    /// Thread safety violation
106    ThreadSafetyViolation {
107        /// Description of the violation
108        violation: &'static str,
109        /// Additional context
110        context: Option<&'static str>,
111    },
112
113    /// Cryptographic operation failed
114    CryptographicFailure {
115        /// Operation that failed
116        operation: &'static str,
117        /// Error code or status
118        code: Option<u32>,
119        /// Additional details
120        details: Option<&'static str>,
121    },
122
123    /// Test vector validation failed
124    TestVectorValidationFailed {
125        /// Test vector identifier
126        vector_id: &'static str,
127        /// Expected value
128        #[cfg(feature = "alloc")]
129        expected: String,
130        /// Expected value (`no_std` version)
131        #[cfg(not(feature = "alloc"))]
132        expected: &'static str,
133        /// Actual value
134        #[cfg(feature = "alloc")]
135        actual: String,
136        /// Actual value (`no_std` version)
137        #[cfg(not(feature = "alloc"))]
138        actual: &'static str,
139    },
140
141    /// Feature not available
142    FeatureNotAvailable {
143        /// Feature that is not available
144        feature: &'static str,
145        /// Required features for this functionality
146        required_features: &'static [&'static str],
147    },
148
149    /// Internal implementation error
150    InternalError {
151        /// Component that failed
152        component: &'static str,
153        /// Error message
154        message: &'static str,
155    },
156}
157
158impl Error {
159    /// Create a new entropy source unavailable error
160    pub fn entropy_source_unavailable(source: &'static str) -> Self {
161        Self::EntropySourceUnavailable {
162            source,
163            context: None,
164        }
165    }
166
167    /// Create a new entropy source unavailable error with context
168    pub fn entropy_source_unavailable_with_context(
169        source: &'static str,
170        context: &'static str,
171    ) -> Self {
172        Self::EntropySourceUnavailable {
173            source,
174            context: Some(context),
175        }
176    }
177
178    /// Create a new insufficient entropy error
179    pub fn insufficient_entropy(required: usize, available: usize, quality: f64) -> Self {
180        Self::InsufficientEntropy {
181            required,
182            available,
183            quality,
184        }
185    }
186
187    /// Create a new entropy validation failed error
188    pub fn entropy_validation_failed(reason: &'static str, quality: f64) -> Self {
189        Self::EntropyValidationFailed {
190            reason,
191            quality,
192            details: None,
193        }
194    }
195
196    /// Create a new entropy validation failed error with details
197    pub fn entropy_validation_failed_with_details(
198        reason: &'static str,
199        quality: f64,
200        details: &'static str,
201    ) -> Self {
202        Self::EntropyValidationFailed {
203            reason,
204            quality,
205            details: Some(details),
206        }
207    }
208
209    /// Create a new hardware RNG failed error
210    pub fn hardware_rng_failed(device: &'static str) -> Self {
211        Self::HardwareRngFailed {
212            device,
213            status: None,
214            details: None,
215        }
216    }
217
218    /// Create a new hardware RNG failed error with status
219    pub fn hardware_rng_failed_with_status(
220        device: &'static str,
221        status: u32,
222        details: &'static str,
223    ) -> Self {
224        Self::HardwareRngFailed {
225            device,
226            status: Some(status),
227            details: Some(details),
228        }
229    }
230
231    /// Create a new platform RNG failed error
232    pub fn platform_rng_failed(platform: &'static str) -> Self {
233        Self::PlatformRngFailed {
234            platform,
235            code: None,
236            details: None,
237        }
238    }
239
240    /// Create a new platform RNG failed error with code
241    pub fn platform_rng_failed_with_code(
242        platform: &'static str,
243        code: i32,
244        details: &'static str,
245    ) -> Self {
246        Self::PlatformRngFailed {
247            platform,
248            code: Some(code),
249            details: Some(details),
250        }
251    }
252
253    /// Create a new invalid configuration error
254    pub fn invalid_configuration(
255        parameter: &'static str,
256        expected: &'static str,
257        actual: &'static str,
258    ) -> Self {
259        Self::InvalidConfiguration {
260            parameter,
261            #[cfg(feature = "alloc")]
262            expected: expected.to_string(),
263            #[cfg(not(feature = "alloc"))]
264            expected,
265            #[cfg(feature = "alloc")]
266            actual: actual.to_string(),
267            #[cfg(not(feature = "alloc"))]
268            actual,
269        }
270    }
271
272    /// Create a new memory allocation failed error
273    pub fn memory_allocation_failed(size: usize) -> Self {
274        Self::MemoryAllocationFailed {
275            size,
276            context: None,
277        }
278    }
279
280    /// Create a new memory allocation failed error with context
281    pub fn memory_allocation_failed_with_context(size: usize, context: &'static str) -> Self {
282        Self::MemoryAllocationFailed {
283            size,
284            context: Some(context),
285        }
286    }
287
288    /// Create a new thread safety violation error
289    pub fn thread_safety_violation(violation: &'static str) -> Self {
290        Self::ThreadSafetyViolation {
291            violation,
292            context: None,
293        }
294    }
295
296    /// Create a new thread safety violation error with context
297    pub fn thread_safety_violation_with_context(
298        violation: &'static str,
299        context: &'static str,
300    ) -> Self {
301        Self::ThreadSafetyViolation {
302            violation,
303            context: Some(context),
304        }
305    }
306
307    /// Create a new cryptographic failure error
308    pub fn cryptographic_failure(operation: &'static str) -> Self {
309        Self::CryptographicFailure {
310            operation,
311            code: None,
312            details: None,
313        }
314    }
315
316    /// Create a new cryptographic failure error with code
317    pub fn cryptographic_failure_with_code(
318        operation: &'static str,
319        code: u32,
320        details: &'static str,
321    ) -> Self {
322        Self::CryptographicFailure {
323            operation,
324            code: Some(code),
325            details: Some(details),
326        }
327    }
328
329    /// Create a new test vector validation failed error
330    pub fn test_vector_validation_failed(
331        vector_id: &'static str,
332        expected: &'static str,
333        actual: &'static str,
334    ) -> Self {
335        Self::TestVectorValidationFailed {
336            vector_id,
337            #[cfg(feature = "alloc")]
338            expected: expected.to_string(),
339            #[cfg(not(feature = "alloc"))]
340            expected,
341            #[cfg(feature = "alloc")]
342            actual: actual.to_string(),
343            #[cfg(not(feature = "alloc"))]
344            actual,
345        }
346    }
347
348    /// Create a new feature not available error
349    pub fn feature_not_available(
350        feature: &'static str,
351        required_features: &'static [&'static str],
352    ) -> Self {
353        Self::FeatureNotAvailable {
354            feature,
355            required_features,
356        }
357    }
358
359    /// Create a new internal error
360    pub fn internal_error(component: &'static str, message: &'static str) -> Self {
361        Self::InternalError { component, message }
362    }
363
364    /// Check if this error is recoverable
365    pub fn is_recoverable(&self) -> bool {
366        matches!(
367            self,
368            Self::EntropySourceUnavailable { .. } |
369                Self::PlatformRngFailed { .. } |
370                Self::HardwareRngFailed { .. }
371        )
372    }
373
374    /// Check if this error indicates insufficient entropy
375    pub fn is_entropy_related(&self) -> bool {
376        matches!(
377            self,
378            Self::EntropySourceUnavailable { .. } |
379                Self::InsufficientEntropy { .. } |
380                Self::EntropyValidationFailed { .. }
381        )
382    }
383
384    /// Get a human-readable error description
385    pub fn description(&self) -> &'static str {
386        match self {
387            Self::EntropySourceUnavailable { .. } => "Entropy source is not available",
388            Self::InsufficientEntropy { .. } => "Insufficient entropy for cryptographic operations",
389            Self::EntropyValidationFailed { .. } => "Entropy validation failed",
390            Self::HardwareRngFailed { .. } => "Hardware random number generator failed",
391            Self::PlatformRngFailed { .. } => "Platform random number generator failed",
392            Self::InvalidConfiguration { .. } => "Invalid configuration or parameters",
393            Self::MemoryAllocationFailed { .. } => "Memory allocation failed",
394            Self::ThreadSafetyViolation { .. } => "Thread safety violation detected",
395            Self::CryptographicFailure { .. } => "Cryptographic operation failed",
396            Self::TestVectorValidationFailed { .. } => "Test vector validation failed",
397            Self::FeatureNotAvailable { .. } => "Required feature is not available",
398            Self::InternalError { .. } => "Internal implementation error",
399        }
400    }
401}
402
403impl fmt::Display for Error {
404    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405        match self {
406            Self::EntropySourceUnavailable { source, context } => {
407                write!(f, "Entropy source '{}' is not available", source)?;
408                if let Some(ctx) = context {
409                    write!(f, ": {}", ctx)?;
410                }
411                Ok(())
412            }
413            Self::InsufficientEntropy {
414                required,
415                available,
416                quality,
417            } => {
418                write!(
419                    f,
420                    "Insufficient entropy: required {} bits, available {} bits (quality: {:.2})",
421                    required, available, quality
422                )
423            }
424            Self::EntropyValidationFailed {
425                reason,
426                quality,
427                details,
428            } => {
429                write!(
430                    f,
431                    "Entropy validation failed: {} (quality: {:.2})",
432                    reason, quality
433                )?;
434                if let Some(details) = details {
435                    write!(f, ": {}", details)?;
436                }
437                Ok(())
438            }
439            Self::HardwareRngFailed {
440                device,
441                status,
442                details,
443            } => {
444                write!(f, "Hardware RNG '{}' failed", device)?;
445                if let Some(status) = status {
446                    write!(f, " (status: {})", status)?;
447                }
448                if let Some(details) = details {
449                    write!(f, ": {}", details)?;
450                }
451                Ok(())
452            }
453            Self::PlatformRngFailed {
454                platform,
455                code,
456                details,
457            } => {
458                write!(f, "Platform RNG '{}' failed", platform)?;
459                if let Some(code) = code {
460                    write!(f, " (code: {})", code)?;
461                }
462                if let Some(details) = details {
463                    write!(f, ": {}", details)?;
464                }
465                Ok(())
466            }
467            Self::InvalidConfiguration {
468                parameter,
469                expected,
470                actual,
471            } => {
472                write!(
473                    f,
474                    "Invalid configuration for '{}': expected {}, got {}",
475                    parameter, expected, actual
476                )
477            }
478            Self::MemoryAllocationFailed { size, context } => {
479                write!(f, "Memory allocation failed for {} bytes", size)?;
480                if let Some(ctx) = context {
481                    write!(f, ": {}", ctx)?;
482                }
483                Ok(())
484            }
485            Self::ThreadSafetyViolation { violation, context } => {
486                write!(f, "Thread safety violation: {}", violation)?;
487                if let Some(ctx) = context {
488                    write!(f, ": {}", ctx)?;
489                }
490                Ok(())
491            }
492            Self::CryptographicFailure {
493                operation,
494                code,
495                details,
496            } => {
497                write!(f, "Cryptographic operation '{}' failed", operation)?;
498                if let Some(code) = code {
499                    write!(f, " (code: {})", code)?;
500                }
501                if let Some(details) = details {
502                    write!(f, ": {}", details)?;
503                }
504                Ok(())
505            }
506            Self::TestVectorValidationFailed {
507                vector_id,
508                expected,
509                actual,
510            } => {
511                write!(
512                    f,
513                    "Test vector '{}' validation failed: expected {}, got {}",
514                    vector_id, expected, actual
515                )
516            }
517            Self::FeatureNotAvailable {
518                feature,
519                required_features,
520            } => {
521                write!(f, "Feature '{}' is not available", feature)?;
522                if !required_features.is_empty() {
523                    #[cfg(feature = "alloc")]
524                    {
525                        write!(f, " (required features: {})", required_features.join(", "))?;
526                    }
527                    #[cfg(not(feature = "alloc"))]
528                    {
529                        write!(f, " (required features: {:?})", required_features)?;
530                    }
531                }
532                Ok(())
533            }
534            Self::InternalError { component, message } => {
535                write!(f, "Internal error in '{}': {}", component, message)
536            }
537        }
538    }
539}
540
541#[cfg(feature = "std")]
542impl std::error::Error for Error {}
543
544#[cfg(test)]
545mod tests {
546    #[cfg(all(not(feature = "std"), feature = "alloc"))]
547    use alloc::format;
548
549    use super::*;
550
551    #[test]
552    fn test_error_creation() {
553        let err = Error::entropy_source_unavailable("test_source");
554        assert_eq!(err.description(), "Entropy source is not available");
555        assert!(err.is_entropy_related());
556        assert!(err.is_recoverable());
557    }
558
559    #[test]
560    #[cfg(any(feature = "std", feature = "alloc"))]
561    fn test_error_display() {
562        let err = Error::insufficient_entropy(256, 128, 0.5);
563        let display = format!("{}", err);
564        assert!(display.contains("Insufficient entropy"));
565        assert!(display.contains("256"));
566        assert!(display.contains("128"));
567        assert!(display.contains("0.50"));
568    }
569
570    #[test]
571    fn test_error_types() {
572        let entropy_err = Error::entropy_source_unavailable("test");
573        assert!(entropy_err.is_entropy_related());
574
575        let config_err = Error::invalid_configuration("param", "expected", "actual");
576        assert!(!config_err.is_entropy_related());
577        assert!(!config_err.is_recoverable());
578    }
579}