use core::{fmt, num::NonZeroU32};
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Error(NonZeroU32);
const fn internal_error(n: u16) -> Error {
let code = Error::INTERNAL_START + (n as u32);
Error(unsafe { NonZeroU32::new_unchecked(code) })
}
impl Error {
pub const UNSUPPORTED: Error = internal_error(0);
pub const ERRNO_NOT_POSITIVE: Error = internal_error(1);
pub const IOS_SEC_RANDOM: Error = internal_error(3);
pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4);
pub const FAILED_RDRAND: Error = internal_error(5);
pub const NO_RDRAND: Error = internal_error(6);
pub const WEB_CRYPTO: Error = internal_error(7);
pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8);
pub const VXWORKS_RAND_SECURE: Error = internal_error(11);
pub const NODE_CRYPTO: Error = internal_error(12);
pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13);
pub const INTERNAL_START: u32 = 1 << 31;
pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30);
#[inline]
pub fn raw_os_error(self) -> Option<i32> {
if self.0.get() < Self::INTERNAL_START {
Some(self.0.get() as i32)
} else {
None
}
}
#[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;
}
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(errno: i32, _buf: &mut [u8]) -> Option<wasi::Error> {
wasi::Error::from_raw_error(errno as _)
}
} 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::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("Web API crypto.getRandomValues is unavailable"),
Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"),
Error::NODE_CRYPTO => Some("Node.js crypto module is unavailable"),
Error::NODE_RANDOM_FILL_SYNC => Some("Node.js API crypto.randomFillSync is unavailable"),
_ => 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);
}
}