1use core::{fmt, num::NonZeroU32};
9
10#[derive(Copy, Clone, Eq, PartialEq)]
25pub struct Error(NonZeroU32);
26
27const fn internal_error(n: u16) -> Error {
28 let code = Error::INTERNAL_START + (n as u32);
30 Error(unsafe { NonZeroU32::new_unchecked(code) })
31}
32
33impl Error {
34 pub const UNSUPPORTED: Error = internal_error(0);
36 pub const ERRNO_NOT_POSITIVE: Error = internal_error(1);
38 pub const IOS_SEC_RANDOM: Error = internal_error(3);
40 pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4);
42 pub const FAILED_RDRAND: Error = internal_error(5);
44 pub const NO_RDRAND: Error = internal_error(6);
46 pub const WEB_CRYPTO: Error = internal_error(7);
48 pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8);
50 pub const VXWORKS_RAND_SECURE: Error = internal_error(11);
52 pub const NODE_CRYPTO: Error = internal_error(12);
54 pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13);
56 pub const NODE_ES_MODULE: Error = internal_error(14);
59
60 pub const INTERNAL_START: u32 = 1 << 31;
64
65 pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30);
68
69 #[inline]
77 pub fn raw_os_error(self) -> Option<i32> {
78 if self.0.get() < Self::INTERNAL_START {
79 match () {
80 #[cfg(target_os = "solid_asp3")]
81 () => Some(-(self.0.get() as i32)),
84 #[cfg(not(target_os = "solid_asp3"))]
85 () => Some(self.0.get() as i32),
86 }
87 } else {
88 None
89 }
90 }
91
92 #[inline]
97 pub const fn code(self) -> NonZeroU32 {
98 self.0
99 }
100}
101
102cfg_if! {
103 if #[cfg(unix)] {
104 fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> {
105 let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char;
106 if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 {
107 return None;
108 }
109
110 let n = buf.len();
112 let idx = buf.iter().position(|&b| b == 0).unwrap_or(n);
113 core::str::from_utf8(&buf[..idx]).ok()
114 }
115 } else {
116 fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
117 None
118 }
119 }
120}
121
122impl fmt::Debug for Error {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 let mut dbg = f.debug_struct("Error");
125 if let Some(errno) = self.raw_os_error() {
126 dbg.field("os_error", &errno);
127 let mut buf = [0u8; 128];
128 if let Some(err) = os_err(errno, &mut buf) {
129 dbg.field("description", &err);
130 }
131 } else if let Some(desc) = internal_desc(*self) {
132 dbg.field("internal_code", &self.0.get());
133 dbg.field("description", &desc);
134 } else {
135 dbg.field("unknown_code", &self.0.get());
136 }
137 dbg.finish()
138 }
139}
140
141impl fmt::Display for Error {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 if let Some(errno) = self.raw_os_error() {
144 let mut buf = [0u8; 128];
145 match os_err(errno, &mut buf) {
146 Some(err) => err.fmt(f),
147 None => write!(f, "OS Error: {}", errno),
148 }
149 } else if let Some(desc) = internal_desc(*self) {
150 f.write_str(desc)
151 } else {
152 write!(f, "Unknown Error: {}", self.0.get())
153 }
154 }
155}
156
157impl From<NonZeroU32> for Error {
158 fn from(code: NonZeroU32) -> Self {
159 Self(code)
160 }
161}
162
163fn internal_desc(error: Error) -> Option<&'static str> {
164 match error {
165 Error::UNSUPPORTED => Some("getrandom: this target is not supported"),
166 Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"),
167 Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"),
168 Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"),
169 Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
170 Error::NO_RDRAND => Some("RDRAND: instruction not supported"),
171 Error::WEB_CRYPTO => Some("Web Crypto API is unavailable"),
172 Error::WEB_GET_RANDOM_VALUES => Some("Calling Web API crypto.getRandomValues failed"),
173 Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"),
174 Error::NODE_CRYPTO => Some("Node.js crypto CommonJS module is unavailable"),
175 Error::NODE_RANDOM_FILL_SYNC => Some("Calling Node.js API crypto.randomFillSync failed"),
176 Error::NODE_ES_MODULE => Some("Node.js ES modules are not directly supported, see https://docs.rs/getrandom#nodejs-es-module-support"),
177 _ => None,
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::Error;
184 use core::mem::size_of;
185
186 #[test]
187 fn test_size() {
188 assert_eq!(size_of::<Error>(), 4);
189 assert_eq!(size_of::<Result<(), Error>>(), 4);
190 }
191}