1#[cfg(feature = "std")]
2extern crate std;
34use core::{fmt, num::NonZeroU32};
56// This private alias mirrors `std::io::RawOsError`:
7// https://doc.rust-lang.org/std/io/type.RawOsError.html)
8cfg_if::cfg_if!(
9if #[cfg(target_os = "uefi")] {
10type RawOsError = usize;
11 } else {
12type RawOsError = i32;
13 }
14);
1516/// A small and `no_std` compatible error type
17///
18/// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and
19/// if so, which error code the OS gave the application. If such an error is
20/// encountered, please consult with your system documentation.
21///
22/// Internally this type is a NonZeroU32, with certain values reserved for
23/// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`].
24///
25/// *If this crate's `"std"` Cargo feature is enabled*, then:
26/// - [`getrandom::Error`][Error] implements
27/// [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html)
28/// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements
29/// [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html).
30#[derive(Copy, Clone, Eq, PartialEq)]
31pub struct Error(NonZeroU32);
3233impl Error {
34/// This target/platform is not supported by `getrandom`.
35pub const UNSUPPORTED: Error = Self::new_internal(0);
36/// The platform-specific `errno` returned a non-positive value.
37pub const ERRNO_NOT_POSITIVE: Error = Self::new_internal(1);
38/// Encountered an unexpected situation which should not happen in practice.
39pub const UNEXPECTED: Error = Self::new_internal(2);
4041/// Codes below this point represent OS Errors (i.e. positive i32 values).
42 /// Codes at or above this point, but below [`Error::CUSTOM_START`] are
43 /// reserved for use by the `rand` and `getrandom` crates.
44pub const INTERNAL_START: u32 = 1 << 31;
4546/// Codes at or above this point can be used by users to define their own
47 /// custom errors.
48pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30);
4950/// Creates a new instance of an `Error` from a particular OS error code.
51 ///
52 /// This method is analogous to [`std::io::Error::from_raw_os_error()`][1],
53 /// except that it works in `no_std` contexts and `code` will be
54 /// replaced with `Error::UNEXPECTED` if it isn't in the range
55 /// `1..Error::INTERNAL_START`. Thus, for the result `r`,
56 /// `r == Self::UNEXPECTED || r.raw_os_error().unsigned_abs() == code`.
57 ///
58 /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.from_raw_os_error
59#[allow(dead_code)]
60pub(super) fn from_os_error(code: u32) -> Self {
61match NonZeroU32::new(code) {
62Some(code) if code.get() < Self::INTERNAL_START => Self(code),
63_ => Self::UNEXPECTED,
64 }
65 }
6667/// Extract the raw OS error code (if this error came from the OS)
68 ///
69 /// This method is identical to [`std::io::Error::raw_os_error()`][1], except
70 /// that it works in `no_std` contexts. On most targets this method returns
71 /// `Option<i32>`, but some platforms (e.g. UEFI) may use a different primitive
72 /// type like `usize`. Consult with the [`RawOsError`] docs for more information.
73 ///
74 /// If this method returns `None`, the error value can still be formatted via
75 /// the `Display` implementation.
76 ///
77 /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error
78 /// [`RawOsError`]: https://doc.rust-lang.org/std/io/type.RawOsError.html
79#[inline]
80pub fn raw_os_error(self) -> Option<RawOsError> {
81let code = self.0.get();
82if code >= Self::INTERNAL_START {
83return None;
84 }
85let errno = RawOsError::try_from(code).ok()?;
86#[cfg(target_os = "solid_asp3")]
87let errno = -errno;
88Some(errno)
89 }
9091/// Creates a new instance of an `Error` from a particular custom error code.
92pub const fn new_custom(n: u16) -> Error {
93// SAFETY: code > 0 as CUSTOM_START > 0 and adding n won't overflow a u32.
94let code = Error::CUSTOM_START + (n as u32);
95 Error(unsafe { NonZeroU32::new_unchecked(code) })
96 }
9798/// Creates a new instance of an `Error` from a particular internal error code.
99pub(crate) const fn new_internal(n: u16) -> Error {
100// SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32.
101let code = Error::INTERNAL_START + (n as u32);
102 Error(unsafe { NonZeroU32::new_unchecked(code) })
103 }
104105fn internal_desc(&self) -> Option<&'static str> {
106let desc = match *self {
107 Error::UNSUPPORTED => "getrandom: this target is not supported",
108 Error::ERRNO_NOT_POSITIVE => "errno: did not return a positive value",
109 Error::UNEXPECTED => "unexpected situation",
110#[cfg(any(
111 target_os = "ios",
112 target_os = "visionos",
113 target_os = "watchos",
114 target_os = "tvos",
115 ))]
116Error::IOS_RANDOM_GEN => "SecRandomCopyBytes: iOS Security framework failure",
117#[cfg(all(windows, target_vendor = "win7"))]
118Error::WINDOWS_RTL_GEN_RANDOM => "RtlGenRandom: Windows system function failure",
119#[cfg(all(feature = "wasm_js", getrandom_backend = "wasm_js"))]
120Error::WEB_CRYPTO => "Web Crypto API is unavailable",
121#[cfg(target_os = "vxworks")]
122Error::VXWORKS_RAND_SECURE => "randSecure: VxWorks RNG module is not initialized",
123124#[cfg(any(
125 getrandom_backend = "rdrand",
126 all(target_arch = "x86_64", target_env = "sgx")
127 ))]
128Error::FAILED_RDRAND => "RDRAND: failed multiple times: CPU issue likely",
129#[cfg(any(
130 getrandom_backend = "rdrand",
131 all(target_arch = "x86_64", target_env = "sgx")
132 ))]
133Error::NO_RDRAND => "RDRAND: instruction not supported",
134135#[cfg(getrandom_backend = "rndr")]
136Error::RNDR_FAILURE => "RNDR: Could not generate a random number",
137#[cfg(getrandom_backend = "rndr")]
138Error::RNDR_NOT_AVAILABLE => "RNDR: Register not supported",
139_ => return None,
140 };
141Some(desc)
142 }
143}
144145impl fmt::Debug for Error {
146fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147let mut dbg = f.debug_struct("Error");
148if let Some(errno) = self.raw_os_error() {
149 dbg.field("os_error", &errno);
150#[cfg(feature = "std")]
151dbg.field("description", &std::io::Error::from_raw_os_error(errno));
152 } else if let Some(desc) = self.internal_desc() {
153 dbg.field("internal_code", &self.0.get());
154 dbg.field("description", &desc);
155 } else {
156 dbg.field("unknown_code", &self.0.get());
157 }
158 dbg.finish()
159 }
160}
161162impl fmt::Display for Error {
163fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164if let Some(errno) = self.raw_os_error() {
165cfg_if! {
166if #[cfg(feature = "std")] {
167 std::io::Error::from_raw_os_error(errno).fmt(f)
168 } else {
169write!(f, "OS Error: {}", errno)
170 }
171 }
172 } else if let Some(desc) = self.internal_desc() {
173 f.write_str(desc)
174 } else {
175write!(f, "Unknown Error: {}", self.0.get())
176 }
177 }
178}
179180#[cfg(test)]
181mod tests {
182use super::Error;
183use core::mem::size_of;
184185#[test]
186fn test_size() {
187assert_eq!(size_of::<Error>(), 4);
188assert_eq!(size_of::<Result<(), Error>>(), 4);
189 }
190}