1use core::{
2 convert::TryFrom,
3 fmt::{self, Display, Formatter},
4};
5
6#[repr(u8)]
8#[derive(Debug, Copy, Clone)]
9pub enum ErrorCode {
10 UnsupportedInstruction,
12 HardwareFailure,
14}
15
16impl ErrorCode {
17 #[cfg(not(feature = "std"))]
18 const fn as_randcore_code(self) -> core::num::NonZeroU32 {
19 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}