• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
9 //! Error types
10 
11 use core::fmt;
12 use core::num::NonZeroU32;
13 
14 #[cfg(feature = "std")] use std::boxed::Box;
15 
16 /// Error type of random number generators
17 ///
18 /// In order to be compatible with `std` and `no_std`, this type has two
19 /// possible implementations: with `std` a boxed `Error` trait object is stored,
20 /// while with `no_std` we merely store an error code.
21 pub struct Error {
22     #[cfg(feature = "std")]
23     inner: Box<dyn std::error::Error + Send + Sync + 'static>,
24     #[cfg(not(feature = "std"))]
25     code: NonZeroU32,
26 }
27 
28 impl Error {
29     /// Codes at or above this point can be used by users to define their own
30     /// custom errors.
31     ///
32     /// This has a fixed value of `(1 << 31) + (1 << 30) = 0xC000_0000`,
33     /// therefore the number of values available for custom codes is `1 << 30`.
34     ///
35     /// This is identical to [`getrandom::Error::CUSTOM_START`](https://docs.rs/getrandom/latest/getrandom/struct.Error.html#associatedconstant.CUSTOM_START).
36     pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30);
37     /// Codes below this point represent OS Errors (i.e. positive i32 values).
38     /// Codes at or above this point, but below [`Error::CUSTOM_START`] are
39     /// reserved for use by the `rand` and `getrandom` crates.
40     ///
41     /// This is identical to [`getrandom::Error::INTERNAL_START`](https://docs.rs/getrandom/latest/getrandom/struct.Error.html#associatedconstant.INTERNAL_START).
42     pub const INTERNAL_START: u32 = 1 << 31;
43 
44     /// Construct from any type supporting `std::error::Error`
45     ///
46     /// Available only when configured with `std`.
47     ///
48     /// See also `From<NonZeroU32>`, which is available with and without `std`.
49     #[cfg(feature = "std")]
50     #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
51     #[inline]
new<E>(err: E) -> Self where E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,52     pub fn new<E>(err: E) -> Self
53     where
54         E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
55     {
56         Error { inner: err.into() }
57     }
58 
59     /// Reference the inner error (`std` only)
60     ///
61     /// When configured with `std`, this is a trivial operation and never
62     /// panics. Without `std`, this method is simply unavailable.
63     #[cfg(feature = "std")]
64     #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
65     #[inline]
inner(&self) -> &(dyn std::error::Error + Send + Sync + 'static)66     pub fn inner(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
67         &*self.inner
68     }
69 
70     /// Unwrap the inner error (`std` only)
71     ///
72     /// When configured with `std`, this is a trivial operation and never
73     /// panics. Without `std`, this method is simply unavailable.
74     #[cfg(feature = "std")]
75     #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
76     #[inline]
take_inner(self) -> Box<dyn std::error::Error + Send + Sync + 'static>77     pub fn take_inner(self) -> Box<dyn std::error::Error + Send + Sync + 'static> {
78         self.inner
79     }
80 
81     /// Extract the raw OS error code (if this error came from the OS)
82     ///
83     /// This method is identical to `std::io::Error::raw_os_error()`, except
84     /// that it works in `no_std` contexts. If this method returns `None`, the
85     /// error value can still be formatted via the `Diplay` implementation.
86     #[inline]
raw_os_error(&self) -> Option<i32>87     pub fn raw_os_error(&self) -> Option<i32> {
88         #[cfg(feature = "std")]
89         {
90             if let Some(e) = self.inner.downcast_ref::<std::io::Error>() {
91                 return e.raw_os_error();
92             }
93         }
94         match self.code() {
95             Some(code) if u32::from(code) < Self::INTERNAL_START => Some(u32::from(code) as i32),
96             _ => None,
97         }
98     }
99 
100     /// Retrieve the error code, if any.
101     ///
102     /// If this `Error` was constructed via `From<NonZeroU32>`, then this method
103     /// will return this `NonZeroU32` code (for `no_std` this is always the
104     /// case). Otherwise, this method will return `None`.
105     #[inline]
code(&self) -> Option<NonZeroU32>106     pub fn code(&self) -> Option<NonZeroU32> {
107         #[cfg(feature = "std")]
108         {
109             self.inner.downcast_ref::<ErrorCode>().map(|c| c.0)
110         }
111         #[cfg(not(feature = "std"))]
112         {
113             Some(self.code)
114         }
115     }
116 }
117 
118 impl fmt::Debug for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result119     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120         #[cfg(feature = "std")]
121         {
122             write!(f, "Error {{ inner: {:?} }}", self.inner)
123         }
124         #[cfg(all(feature = "getrandom", not(feature = "std")))]
125         {
126             getrandom::Error::from(self.code).fmt(f)
127         }
128         #[cfg(not(feature = "getrandom"))]
129         {
130             write!(f, "Error {{ code: {} }}", self.code)
131         }
132     }
133 }
134 
135 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result136     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137         #[cfg(feature = "std")]
138         {
139             write!(f, "{}", self.inner)
140         }
141         #[cfg(all(feature = "getrandom", not(feature = "std")))]
142         {
143             getrandom::Error::from(self.code).fmt(f)
144         }
145         #[cfg(not(feature = "getrandom"))]
146         {
147             write!(f, "error code {}", self.code)
148         }
149     }
150 }
151 
152 impl From<NonZeroU32> for Error {
153     #[inline]
from(code: NonZeroU32) -> Self154     fn from(code: NonZeroU32) -> Self {
155         #[cfg(feature = "std")]
156         {
157             Error {
158                 inner: Box::new(ErrorCode(code)),
159             }
160         }
161         #[cfg(not(feature = "std"))]
162         {
163             Error { code }
164         }
165     }
166 }
167 
168 #[cfg(feature = "getrandom")]
169 impl From<getrandom::Error> for Error {
170     #[inline]
from(error: getrandom::Error) -> Self171     fn from(error: getrandom::Error) -> Self {
172         #[cfg(feature = "std")]
173         {
174             Error {
175                 inner: Box::new(error),
176             }
177         }
178         #[cfg(not(feature = "std"))]
179         {
180             Error { code: error.code() }
181         }
182     }
183 }
184 
185 #[cfg(feature = "std")]
186 impl std::error::Error for Error {
187     #[inline]
source(&self) -> Option<&(dyn std::error::Error + 'static)>188     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
189         self.inner.source()
190     }
191 }
192 
193 #[cfg(feature = "std")]
194 impl From<Error> for std::io::Error {
195     #[inline]
from(error: Error) -> Self196     fn from(error: Error) -> Self {
197         if let Some(code) = error.raw_os_error() {
198             std::io::Error::from_raw_os_error(code)
199         } else {
200             std::io::Error::new(std::io::ErrorKind::Other, error)
201         }
202     }
203 }
204 
205 #[cfg(feature = "std")]
206 #[derive(Debug, Copy, Clone)]
207 struct ErrorCode(NonZeroU32);
208 
209 #[cfg(feature = "std")]
210 impl fmt::Display for ErrorCode {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result211     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212         write!(f, "error code {}", self.0)
213     }
214 }
215 
216 #[cfg(feature = "std")]
217 impl std::error::Error for ErrorCode {}
218 
219 #[cfg(test)]
220 mod test {
221     #[cfg(feature = "getrandom")]
222     #[test]
test_error_codes()223     fn test_error_codes() {
224         // Make sure the values are the same as in `getrandom`.
225         assert_eq!(super::Error::CUSTOM_START, getrandom::Error::CUSTOM_START);
226         assert_eq!(super::Error::INTERNAL_START, getrandom::Error::INTERNAL_START);
227     }
228 }
229