rdrand/
errors.rs

1use core::{
2    convert::TryFrom,
3    fmt::{self, Display, Formatter},
4};
5
6/// Errors in this library
7#[repr(u8)]
8#[derive(Debug, Copy, Clone)]
9pub enum ErrorCode {
10    /// The hardware instruction is not supported
11    UnsupportedInstruction,
12    /// There was a hardware failure
13    HardwareFailure,
14}
15
16impl ErrorCode {
17    #[cfg(not(feature = "std"))]
18    const fn as_randcore_code(self) -> core::num::NonZeroU32 {
19        /// Arbitrary, off top of head bitmask for error codes that come from rdrand
20        const RDRAND_TAG: u32 = rand_core::Error::CUSTOM_START + 0x3D34_7D00;
21        unsafe { core::num::NonZeroU32::new_unchecked(RDRAND_TAG + self as u32) }
22    }
23}
24
25#[cfg(not(feature = "std"))]
26impl From<ErrorCode> for rand_core::Error {
27    fn from(code: ErrorCode) -> rand_core::Error {
28        code.as_randcore_code().into()
29    }
30}
31
32#[cfg(feature = "std")]
33impl From<ErrorCode> for rand_core::Error {
34    fn from(code: ErrorCode) -> rand_core::Error {
35        rand_core::Error::new(code)
36    }
37}
38
39#[cfg(feature = "std")]
40impl std::error::Error for ErrorCode {}
41
42impl Display for ErrorCode {
43    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
44        f.write_str(match self {
45            ErrorCode::UnsupportedInstruction => "the hardware instruction is not supported",
46            ErrorCode::HardwareFailure => "hardware generator failure",
47        })
48    }
49}
50
51#[derive(Copy, Clone, Debug)]
52pub struct NotAnErrorCode;
53
54#[cfg(feature = "std")]
55impl std::error::Error for NotAnErrorCode {}
56
57impl Display for NotAnErrorCode {
58    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
59        f.write_str("the error is not an rdrand error")
60    }
61}
62
63impl TryFrom<&rand_core::Error> for ErrorCode {
64    type Error = NotAnErrorCode;
65    #[cfg(feature = "std")]
66    fn try_from(error: &rand_core::Error) -> Result<Self, Self::Error> {
67        error
68            .inner()
69            .downcast_ref::<ErrorCode>()
70            .copied()
71            .ok_or(NotAnErrorCode)
72    }
73    #[cfg(not(feature = "std"))]
74    fn try_from(error: &rand_core::Error) -> Result<Self, Self::Error> {
75        let code = error.code().ok_or(NotAnErrorCode)?;
76        if code == ErrorCode::UnsupportedInstruction.as_randcore_code() {
77            Ok(ErrorCode::UnsupportedInstruction)
78        } else if code == ErrorCode::HardwareFailure.as_randcore_code() {
79            Ok(ErrorCode::HardwareFailure)
80        } else {
81            Err(NotAnErrorCode)
82        }
83    }
84}
85
86impl TryFrom<rand_core::Error> for ErrorCode {
87    type Error = NotAnErrorCode;
88    fn try_from(error: rand_core::Error) -> Result<Self, Self::Error> {
89        <ErrorCode as TryFrom<&rand_core::Error>>::try_from(&error)
90    }
91}
92
93#[cfg(test)]
94mod test {
95    use super::ErrorCode;
96    use core::convert::TryInto;
97    use rand_core::Error;
98
99    #[test]
100    fn error_code_send() {
101        fn assert_send<T: Send>() {}
102        assert_send::<ErrorCode>();
103    }
104
105    #[test]
106    fn error_code_sync() {
107        fn assert_sync<T: Sync>() {}
108        assert_sync::<ErrorCode>();
109    }
110
111    #[test]
112    fn error_code_copy() {
113        fn assert_copy<T: Copy>() {}
114        assert_copy::<ErrorCode>();
115    }
116
117    #[test]
118    fn error_code_clone() {
119        fn assert_clone<T: Clone>() {}
120        assert_clone::<ErrorCode>();
121    }
122
123    #[test]
124    #[cfg(feature = "std")]
125    fn error_code_error() {
126        fn assert_error<T: std::error::Error>() {}
127        assert_error::<ErrorCode>();
128    }
129
130    #[test]
131    fn conversion_roundtrip_unsupported_hardware() {
132        let core_rand: Error = ErrorCode::UnsupportedInstruction.into();
133        let code: ErrorCode = core_rand.try_into().expect("should convert back");
134        assert!(matches!(code, ErrorCode::UnsupportedInstruction));
135    }
136
137    #[test]
138    fn conversion_roundtrip_hardware_failure() {
139        let core_rand: Error = ErrorCode::HardwareFailure.into();
140        let code: ErrorCode = core_rand.try_into().expect("should convert back");
141        assert!(matches!(code, ErrorCode::HardwareFailure));
142    }
143}