/rust/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.10/src/error.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2018 Developers of the Rand project. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
4 | | // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
5 | | // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your |
6 | | // option. This file may not be copied, modified, or distributed |
7 | | // except according to those terms. |
8 | | use core::{fmt, num::NonZeroU32}; |
9 | | |
10 | | /// A small and `no_std` compatible error type |
11 | | /// |
12 | | /// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and |
13 | | /// if so, which error code the OS gave the application. If such an error is |
14 | | /// encountered, please consult with your system documentation. |
15 | | /// |
16 | | /// Internally this type is a NonZeroU32, with certain values reserved for |
17 | | /// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`]. |
18 | | /// |
19 | | /// *If this crate's `"std"` Cargo feature is enabled*, then: |
20 | | /// - [`getrandom::Error`][Error] implements |
21 | | /// [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html) |
22 | | /// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements |
23 | | /// [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html). |
24 | 0 | #[derive(Copy, Clone, Eq, PartialEq)] |
25 | | pub struct Error(NonZeroU32); |
26 | | |
27 | 0 | const fn internal_error(n: u16) -> Error { |
28 | 0 | // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32. |
29 | 0 | let code = Error::INTERNAL_START + (n as u32); |
30 | 0 | Error(unsafe { NonZeroU32::new_unchecked(code) }) |
31 | 0 | } |
32 | | |
33 | | impl Error { |
34 | | /// This target/platform is not supported by `getrandom`. |
35 | | pub const UNSUPPORTED: Error = internal_error(0); |
36 | | /// The platform-specific `errno` returned a non-positive value. |
37 | | pub const ERRNO_NOT_POSITIVE: Error = internal_error(1); |
38 | | /// Call to iOS [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) failed. |
39 | | pub const IOS_SEC_RANDOM: Error = internal_error(3); |
40 | | /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed. |
41 | | pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4); |
42 | | /// RDRAND instruction failed due to a hardware issue. |
43 | | pub const FAILED_RDRAND: Error = internal_error(5); |
44 | | /// RDRAND instruction unsupported on this target. |
45 | | pub const NO_RDRAND: Error = internal_error(6); |
46 | | /// The environment does not support the Web Crypto API. |
47 | | pub const WEB_CRYPTO: Error = internal_error(7); |
48 | | /// Calling Web Crypto API `crypto.getRandomValues` failed. |
49 | | pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8); |
50 | | /// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized). |
51 | | pub const VXWORKS_RAND_SECURE: Error = internal_error(11); |
52 | | /// Node.js does not have the `crypto` CommonJS module. |
53 | | pub const NODE_CRYPTO: Error = internal_error(12); |
54 | | /// Calling Node.js function `crypto.randomFillSync` failed. |
55 | | pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13); |
56 | | /// Called from an ES module on Node.js. This is unsupported, see: |
57 | | /// <https://docs.rs/getrandom#nodejs-es-module-support>. |
58 | | pub const NODE_ES_MODULE: Error = internal_error(14); |
59 | | |
60 | | /// Codes below this point represent OS Errors (i.e. positive i32 values). |
61 | | /// Codes at or above this point, but below [`Error::CUSTOM_START`] are |
62 | | /// reserved for use by the `rand` and `getrandom` crates. |
63 | | pub const INTERNAL_START: u32 = 1 << 31; |
64 | | |
65 | | /// Codes at or above this point can be used by users to define their own |
66 | | /// custom errors. |
67 | | pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30); |
68 | | |
69 | | /// Extract the raw OS error code (if this error came from the OS) |
70 | | /// |
71 | | /// This method is identical to [`std::io::Error::raw_os_error()`][1], except |
72 | | /// that it works in `no_std` contexts. If this method returns `None`, the |
73 | | /// error value can still be formatted via the `Display` implementation. |
74 | | /// |
75 | | /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error |
76 | | #[inline] |
77 | 0 | pub fn raw_os_error(self) -> Option<i32> { |
78 | 0 | if self.0.get() < Self::INTERNAL_START { |
79 | 0 | match () { |
80 | 0 | #[cfg(target_os = "solid_asp3")] |
81 | 0 | // On SOLID, negate the error code again to obtain the original |
82 | 0 | // error code. |
83 | 0 | () => Some(-(self.0.get() as i32)), |
84 | 0 | #[cfg(not(target_os = "solid_asp3"))] |
85 | 0 | () => Some(self.0.get() as i32), |
86 | | } |
87 | | } else { |
88 | 0 | None |
89 | | } |
90 | 0 | } |
91 | | |
92 | | /// Extract the bare error code. |
93 | | /// |
94 | | /// This code can either come from the underlying OS, or be a custom error. |
95 | | /// Use [`Error::raw_os_error()`] to disambiguate. |
96 | | #[inline] |
97 | 0 | pub const fn code(self) -> NonZeroU32 { |
98 | 0 | self.0 |
99 | 0 | } |
100 | | } |
101 | | |
102 | | cfg_if! { |
103 | | if #[cfg(unix)] { |
104 | 0 | fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> { |
105 | 0 | let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char; |
106 | 0 | if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 { |
107 | 0 | return None; |
108 | 0 | } |
109 | 0 |
|
110 | 0 | // Take up to trailing null byte |
111 | 0 | let n = buf.len(); |
112 | 0 | let idx = buf.iter().position(|&b| b == 0).unwrap_or(n); |
113 | 0 | core::str::from_utf8(&buf[..idx]).ok() |
114 | 0 | } |
115 | | } else { |
116 | | fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> { |
117 | | None |
118 | | } |
119 | | } |
120 | | } |
121 | | |
122 | | impl fmt::Debug for Error { |
123 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
124 | 0 | let mut dbg = f.debug_struct("Error"); |
125 | 0 | if let Some(errno) = self.raw_os_error() { |
126 | 0 | dbg.field("os_error", &errno); |
127 | 0 | let mut buf = [0u8; 128]; |
128 | 0 | if let Some(err) = os_err(errno, &mut buf) { |
129 | 0 | dbg.field("description", &err); |
130 | 0 | } |
131 | 0 | } else if let Some(desc) = internal_desc(*self) { |
132 | 0 | dbg.field("internal_code", &self.0.get()); |
133 | 0 | dbg.field("description", &desc); |
134 | 0 | } else { |
135 | 0 | dbg.field("unknown_code", &self.0.get()); |
136 | 0 | } |
137 | 0 | dbg.finish() |
138 | 0 | } |
139 | | } |
140 | | |
141 | | impl fmt::Display for Error { |
142 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
143 | 0 | if let Some(errno) = self.raw_os_error() { |
144 | 0 | let mut buf = [0u8; 128]; |
145 | 0 | match os_err(errno, &mut buf) { |
146 | 0 | Some(err) => err.fmt(f), |
147 | 0 | None => write!(f, "OS Error: {}", errno), |
148 | | } |
149 | 0 | } else if let Some(desc) = internal_desc(*self) { |
150 | 0 | f.write_str(desc) |
151 | | } else { |
152 | 0 | write!(f, "Unknown Error: {}", self.0.get()) |
153 | | } |
154 | 0 | } |
155 | | } |
156 | | |
157 | | impl From<NonZeroU32> for Error { |
158 | 0 | fn from(code: NonZeroU32) -> Self { |
159 | 0 | Self(code) |
160 | 0 | } |
161 | | } |
162 | | |
163 | 0 | fn internal_desc(error: Error) -> Option<&'static str> { |
164 | 0 | match error { |
165 | 0 | Error::UNSUPPORTED => Some("getrandom: this target is not supported"), |
166 | 0 | Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"), |
167 | 0 | Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"), |
168 | 0 | Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"), |
169 | 0 | Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"), |
170 | 0 | Error::NO_RDRAND => Some("RDRAND: instruction not supported"), |
171 | 0 | Error::WEB_CRYPTO => Some("Web Crypto API is unavailable"), |
172 | 0 | Error::WEB_GET_RANDOM_VALUES => Some("Calling Web API crypto.getRandomValues failed"), |
173 | 0 | Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"), |
174 | 0 | Error::NODE_CRYPTO => Some("Node.js crypto CommonJS module is unavailable"), |
175 | 0 | Error::NODE_RANDOM_FILL_SYNC => Some("Calling Node.js API crypto.randomFillSync failed"), |
176 | 0 | Error::NODE_ES_MODULE => Some("Node.js ES modules are not directly supported, see https://docs.rs/getrandom#nodejs-es-module-support"), |
177 | 0 | _ => None, |
178 | | } |
179 | 0 | } |
180 | | |
181 | | #[cfg(test)] |
182 | | mod 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 | | } |