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 
15 /// Error type of random number generators
16 ///
17 /// In order to be compatible with `std` and `no_std`, this type has two
18 /// possible implementations: with `std` a boxed `Error` trait object is stored,
19 /// while with `no_std` we merely store an error code.
20 pub struct Error {
21     #[cfg(feature="std")]
22     inner: Box<dyn std::error::Error + Send + Sync + 'static>,
23     #[cfg(not(feature="std"))]
24     code: NonZeroU32,
25 }
26 
27 impl Error {
28     /// Construct from any type supporting `std::error::Error`
29     ///
30     /// Available only when configured with `std`.
31     ///
32     /// See also `From<NonZeroU32>`, which is available with and without `std`.
33     #[cfg(feature="std")]
34     #[inline]
new<E>(err: E) -> Self where E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>35     pub fn new<E>(err: E) -> Self
36     where E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>
37     {
38         Error { inner: err.into() }
39     }
40 
41     /// Reference the inner error (`std` only)
42     ///
43     /// When configured with `std`, this is a trivial operation and never
44     /// panics. Without `std`, this method is simply unavailable.
45     #[cfg(feature="std")]
46     #[inline]
inner(&self) -> &(dyn std::error::Error + Send + Sync + 'static)47     pub fn inner(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
48         &*self.inner
49     }
50 
51     /// Unwrap the inner error (`std` only)
52     ///
53     /// When configured with `std`, this is a trivial operation and never
54     /// panics. Without `std`, this method is simply unavailable.
55     #[cfg(feature="std")]
56     #[inline]
take_inner(self) -> Box<dyn std::error::Error + Send + Sync + 'static>57     pub fn take_inner(self) -> Box<dyn std::error::Error + Send + Sync + 'static> {
58         self.inner
59     }
60 
61     /// Codes below this point represent OS Errors (i.e. positive i32 values).
62     /// Codes at or above this point, but below [`Error::CUSTOM_START`] are
63     /// reserved for use by the `rand` and `getrandom` crates.
64     pub const INTERNAL_START: u32 = 1 << 31;
65 
66     /// Codes at or above this point can be used by users to define their own
67     /// custom errors.
68     pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30);
69 
70     /// Extract the raw OS error code (if this error came from the OS)
71     ///
72     /// This method is identical to `std::io::Error::raw_os_error()`, except
73     /// that it works in `no_std` contexts. If this method returns `None`, the
74     /// error value can still be formatted via the `Diplay` implementation.
75     #[inline]
raw_os_error(&self) -> Option<i32>76     pub fn raw_os_error(&self) -> Option<i32> {
77         #[cfg(feature="std")] {
78             if let Some(e) = self.inner.downcast_ref::<std::io::Error>() {
79                 return e.raw_os_error();
80             }
81         }
82         match self.code() {
83             Some(code) if u32::from(code) < Self::INTERNAL_START =>
84                 Some(u32::from(code) as i32),
85             _ => None,
86         }
87     }
88 
89     /// Retrieve the error code, if any.
90     ///
91     /// If this `Error` was constructed via `From<NonZeroU32>`, then this method
92     /// will return this `NonZeroU32` code (for `no_std` this is always the
93     /// case). Otherwise, this method will return `None`.
94     #[inline]
code(&self) -> Option<NonZeroU32>95     pub fn code(&self) -> Option<NonZeroU32> {
96         #[cfg(feature="std")] {
97             self.inner.downcast_ref::<ErrorCode>().map(|c| c.0)
98         }
99         #[cfg(not(feature="std"))] {
100             Some(self.code)
101         }
102     }
103 }
104 
105 impl fmt::Debug for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result106     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107         #[cfg(feature="std")] {
108             write!(f, "Error {{ inner: {:?} }}", self.inner)
109         }
110         #[cfg(all(feature="getrandom", not(feature="std")))] {
111             getrandom::Error::from(self.code).fmt(f)
112         }
113         #[cfg(not(feature="getrandom"))] {
114             write!(f, "Error {{ code: {} }}", self.code)
115         }
116     }
117 }
118 
119 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result120     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121         #[cfg(feature="std")] {
122             write!(f, "{}", self.inner)
123         }
124         #[cfg(all(feature="getrandom", not(feature="std")))] {
125             getrandom::Error::from(self.code).fmt(f)
126         }
127         #[cfg(not(feature="getrandom"))] {
128             write!(f, "error code {}", self.code)
129         }
130     }
131 }
132 
133 impl From<NonZeroU32> for Error {
134     #[inline]
from(code: NonZeroU32) -> Self135     fn from(code: NonZeroU32) -> Self {
136         #[cfg(feature="std")] {
137             Error { inner: Box::new(ErrorCode(code)) }
138         }
139         #[cfg(not(feature="std"))] {
140             Error { code }
141         }
142     }
143 }
144 
145 #[cfg(feature="getrandom")]
146 impl From<getrandom::Error> for Error {
147     #[inline]
from(error: getrandom::Error) -> Self148     fn from(error: getrandom::Error) -> Self {
149         #[cfg(feature="std")] {
150             Error { inner: Box::new(error) }
151         }
152         #[cfg(not(feature="std"))] {
153             Error { code: error.code() }
154         }
155     }
156 }
157 
158 #[cfg(feature="std")]
159 impl std::error::Error for Error {
160     #[inline]
source(&self) -> Option<&(dyn std::error::Error + 'static)>161     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
162         self.inner.source()
163     }
164 }
165 
166 #[cfg(feature="std")]
167 impl From<Error> for std::io::Error {
168     #[inline]
from(error: Error) -> Self169     fn from(error: Error) -> Self {
170         if let Some(code) = error.raw_os_error() {
171             std::io::Error::from_raw_os_error(code)
172         } else {
173             std::io::Error::new(std::io::ErrorKind::Other, error)
174         }
175     }
176 }
177 
178 #[cfg(feature="std")]
179 #[derive(Debug, Copy, Clone)]
180 struct ErrorCode(NonZeroU32);
181 
182 #[cfg(feature="std")]
183 impl fmt::Display for ErrorCode {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result184     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185         write!(f, "error code {}", self.0)
186     }
187 }
188 
189 #[cfg(feature="std")]
190 impl std::error::Error for ErrorCode {}
191