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