// Copyright 2018 Developers of the Rand project. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use core::fmt; use core::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`]. #[derive(Copy, Clone, Eq, PartialEq)] pub struct Error(NonZeroU32); impl Error { #[deprecated(since = "0.1.7")] /// Unknown error. pub const UNKNOWN: Error = UNSUPPORTED; #[deprecated(since = "0.1.7")] /// System entropy source is unavailable. pub const UNAVAILABLE: Error = UNSUPPORTED; /// 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()`, except /// that it works in `no_std` contexts. If this method returns `None`, the /// error value can still be formatted via the `Display` implementation. #[inline] pub fn raw_os_error(self) -> Option { if self.0.get() < Self::INTERNAL_START { 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 fn code(self) -> NonZeroU32 { self.0 } } cfg_if! { if #[cfg(unix)] { fn os_err_desc(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 if #[cfg(target_os = "wasi")] { fn os_err_desc(errno: i32, _buf: &mut [u8]) -> Option<&str> { core::num::NonZeroU16::new(errno as u16) .and_then(wasi::wasi_unstable::error_str) } } else { fn os_err_desc(_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(desc) = os_err_desc(errno, &mut buf) { dbg.field("description", &desc); } } 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_desc(errno, &mut buf) { Some(desc) => f.write_str(desc), 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 for Error { fn from(code: NonZeroU32) -> Self { Self(code) } } // TODO: Convert to a function when min_version >= 1.33 macro_rules! internal_error { ($n:expr) => { Error(unsafe { NonZeroU32::new_unchecked(Error::INTERNAL_START + $n as u16 as u32) }) }; } /// Internal Error constants pub(crate) const UNSUPPORTED: Error = internal_error!(0); pub(crate) const ERRNO_NOT_POSITIVE: Error = internal_error!(1); pub(crate) const UNKNOWN_IO_ERROR: Error = internal_error!(2); pub(crate) const SEC_RANDOM_FAILED: Error = internal_error!(3); pub(crate) const RTL_GEN_RANDOM_FAILED: Error = internal_error!(4); pub(crate) const FAILED_RDRAND: Error = internal_error!(5); pub(crate) const NO_RDRAND: Error = internal_error!(6); pub(crate) const BINDGEN_CRYPTO_UNDEF: Error = internal_error!(7); pub(crate) const BINDGEN_GRV_UNDEF: Error = internal_error!(8); pub(crate) const STDWEB_NO_RNG: Error = internal_error!(9); pub(crate) const STDWEB_RNG_FAILED: Error = internal_error!(10); pub(crate) const RAND_SECURE_FATAL: Error = internal_error!(11); fn internal_desc(error: Error) -> Option<&'static str> { match error { UNSUPPORTED => Some("getrandom: this target is not supported"), ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"), UNKNOWN_IO_ERROR => Some("Unknown std::io::Error"), SEC_RANDOM_FAILED => Some("SecRandomCopyBytes: call failed"), RTL_GEN_RANDOM_FAILED => Some("RtlGenRandom: call failed"), FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"), NO_RDRAND => Some("RDRAND: instruction not supported"), BINDGEN_CRYPTO_UNDEF => Some("wasm-bindgen: self.crypto is undefined"), BINDGEN_GRV_UNDEF => Some("wasm-bindgen: crypto.getRandomValues is undefined"), STDWEB_NO_RNG => Some("stdweb: no randomness source available"), STDWEB_RNG_FAILED => Some("stdweb: failed to get randomness"), RAND_SECURE_FATAL => Some("randSecure: random number generator module is not initialized"), _ => None, } } #[cfg(test)] mod tests { use super::Error; use core::mem::size_of; #[test] fn test_size() { assert_eq!(size_of::(), 4); assert_eq!(size_of::>(), 4); } }