|  | use core::{fmt, num::NonZeroU32}; | 
|  |  | 
|  | /// A small and `no_std` compatible error type | 
|  | /// | 
|  | /// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and | 
|  | /// if so, which error code the OS gave the application. If such an error is | 
|  | /// encountered, please consult with your system documentation. | 
|  | /// | 
|  | /// Internally this type is a NonZeroU32, with certain values reserved for | 
|  | /// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`]. | 
|  | /// | 
|  | /// *If this crate's `"std"` Cargo feature is enabled*, then: | 
|  | /// - [`getrandom::Error`][Error] implements | 
|  | ///   [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html) | 
|  | /// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements | 
|  | ///   [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html). | 
|  | #[derive(Copy, Clone, Eq, PartialEq)] | 
|  | pub struct Error(NonZeroU32); | 
|  |  | 
|  | const fn internal_error(n: u16) -> Error { | 
|  | // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32. | 
|  | let code = Error::INTERNAL_START + (n as u32); | 
|  | Error(unsafe { NonZeroU32::new_unchecked(code) }) | 
|  | } | 
|  |  | 
|  | impl Error { | 
|  | /// This target/platform is not supported by `getrandom`. | 
|  | pub const UNSUPPORTED: Error = internal_error(0); | 
|  | /// The platform-specific `errno` returned a non-positive value. | 
|  | pub const ERRNO_NOT_POSITIVE: Error = internal_error(1); | 
|  | /// Encountered an unexpected situation which should not happen in practice. | 
|  | pub const UNEXPECTED: Error = internal_error(2); | 
|  | /// Call to [`CCRandomGenerateBytes`](https://opensource.apple.com/source/CommonCrypto/CommonCrypto-60074/include/CommonRandom.h.auto.html) failed | 
|  | /// on iOS, tvOS, or waatchOS. | 
|  | // TODO: Update this constant name in the next breaking release. | 
|  | pub const IOS_SEC_RANDOM: Error = internal_error(3); | 
|  | /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed. | 
|  | pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4); | 
|  | /// RDRAND instruction failed due to a hardware issue. | 
|  | pub const FAILED_RDRAND: Error = internal_error(5); | 
|  | /// RDRAND instruction unsupported on this target. | 
|  | pub const NO_RDRAND: Error = internal_error(6); | 
|  | /// The environment does not support the Web Crypto API. | 
|  | pub const WEB_CRYPTO: Error = internal_error(7); | 
|  | /// Calling Web Crypto API `crypto.getRandomValues` failed. | 
|  | pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8); | 
|  | /// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized). | 
|  | pub const VXWORKS_RAND_SECURE: Error = internal_error(11); | 
|  | /// Node.js does not have the `crypto` CommonJS module. | 
|  | pub const NODE_CRYPTO: Error = internal_error(12); | 
|  | /// Calling Node.js function `crypto.randomFillSync` failed. | 
|  | pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13); | 
|  | /// Called from an ES module on Node.js. This is unsupported, see: | 
|  | /// <https://docs.rs/getrandom#nodejs-es-module-support>. | 
|  | pub const NODE_ES_MODULE: Error = internal_error(14); | 
|  |  | 
|  | /// Codes below this point represent OS Errors (i.e. positive i32 values). | 
|  | /// Codes at or above this point, but below [`Error::CUSTOM_START`] are | 
|  | /// reserved for use by the `rand` and `getrandom` crates. | 
|  | pub const INTERNAL_START: u32 = 1 << 31; | 
|  |  | 
|  | /// Codes at or above this point can be used by users to define their own | 
|  | /// custom errors. | 
|  | pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30); | 
|  |  | 
|  | /// Extract the raw OS error code (if this error came from the OS) | 
|  | /// | 
|  | /// This method is identical to [`std::io::Error::raw_os_error()`][1], except | 
|  | /// that it works in `no_std` contexts. If this method returns `None`, the | 
|  | /// error value can still be formatted via the `Display` implementation. | 
|  | /// | 
|  | /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error | 
|  | #[inline] | 
|  | pub fn raw_os_error(self) -> Option<i32> { | 
|  | if self.0.get() < Self::INTERNAL_START { | 
|  | match () { | 
|  | #[cfg(target_os = "solid_asp3")] | 
|  | // On SOLID, negate the error code again to obtain the original | 
|  | // error code. | 
|  | () => Some(-(self.0.get() as i32)), | 
|  | #[cfg(not(target_os = "solid_asp3"))] | 
|  | () => Some(self.0.get() as i32), | 
|  | } | 
|  | } else { | 
|  | None | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Extract the bare error code. | 
|  | /// | 
|  | /// This code can either come from the underlying OS, or be a custom error. | 
|  | /// Use [`Error::raw_os_error()`] to disambiguate. | 
|  | #[inline] | 
|  | pub const fn code(self) -> NonZeroU32 { | 
|  | self.0 | 
|  | } | 
|  | } | 
|  |  | 
|  | cfg_if! { | 
|  | if #[cfg(unix)] { | 
|  | fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> { | 
|  | let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char; | 
|  | if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 { | 
|  | return None; | 
|  | } | 
|  |  | 
|  | // Take up to trailing null byte | 
|  | let n = buf.len(); | 
|  | let idx = buf.iter().position(|&b| b == 0).unwrap_or(n); | 
|  | core::str::from_utf8(&buf[..idx]).ok() | 
|  | } | 
|  | } else { | 
|  | fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> { | 
|  | None | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl fmt::Debug for Error { | 
|  | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 
|  | let mut dbg = f.debug_struct("Error"); | 
|  | if let Some(errno) = self.raw_os_error() { | 
|  | dbg.field("os_error", &errno); | 
|  | let mut buf = [0u8; 128]; | 
|  | if let Some(err) = os_err(errno, &mut buf) { | 
|  | dbg.field("description", &err); | 
|  | } | 
|  | } else if let Some(desc) = internal_desc(*self) { | 
|  | dbg.field("internal_code", &self.0.get()); | 
|  | dbg.field("description", &desc); | 
|  | } else { | 
|  | dbg.field("unknown_code", &self.0.get()); | 
|  | } | 
|  | dbg.finish() | 
|  | } | 
|  | } | 
|  |  | 
|  | impl fmt::Display for Error { | 
|  | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 
|  | if let Some(errno) = self.raw_os_error() { | 
|  | let mut buf = [0u8; 128]; | 
|  | match os_err(errno, &mut buf) { | 
|  | Some(err) => err.fmt(f), | 
|  | None => write!(f, "OS Error: {}", errno), | 
|  | } | 
|  | } else if let Some(desc) = internal_desc(*self) { | 
|  | f.write_str(desc) | 
|  | } else { | 
|  | write!(f, "Unknown Error: {}", self.0.get()) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl From<NonZeroU32> for Error { | 
|  | fn from(code: NonZeroU32) -> Self { | 
|  | Self(code) | 
|  | } | 
|  | } | 
|  |  | 
|  | fn internal_desc(error: Error) -> Option<&'static str> { | 
|  | match error { | 
|  | Error::UNSUPPORTED => Some("getrandom: this target is not supported"), | 
|  | Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"), | 
|  | Error::UNEXPECTED => Some("unexpected situation"), | 
|  | Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"), | 
|  | Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"), | 
|  | Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"), | 
|  | Error::NO_RDRAND => Some("RDRAND: instruction not supported"), | 
|  | Error::WEB_CRYPTO => Some("Web Crypto API is unavailable"), | 
|  | Error::WEB_GET_RANDOM_VALUES => Some("Calling Web API crypto.getRandomValues failed"), | 
|  | Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"), | 
|  | Error::NODE_CRYPTO => Some("Node.js crypto CommonJS module is unavailable"), | 
|  | Error::NODE_RANDOM_FILL_SYNC => Some("Calling Node.js API crypto.randomFillSync failed"), | 
|  | Error::NODE_ES_MODULE => Some("Node.js ES modules are not directly supported, see https://docs.rs/getrandom#nodejs-es-module-support"), | 
|  | _ => None, | 
|  | } | 
|  | } | 
|  |  | 
|  | #[cfg(test)] | 
|  | mod tests { | 
|  | use super::Error; | 
|  | use core::mem::size_of; | 
|  |  | 
|  | #[test] | 
|  | fn test_size() { | 
|  | assert_eq!(size_of::<Error>(), 4); | 
|  | assert_eq!(size_of::<Result<(), Error>>(), 4); | 
|  | } | 
|  | } |