-
Notifications
You must be signed in to change notification settings - Fork 432
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Revise rand_core::Error (alt #771) #800
Changes from 2 commits
6daae3e
a4e5f01
25650c4
b61e61b
f42abf8
959043d
ca1b7c4
4da7750
c5a5ab1
2d55947
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,169 +9,119 @@ | |
//! Error types | ||
|
||
use core::fmt; | ||
#[cfg(not(feature="std"))] | ||
use core::num::NonZeroU32; | ||
|
||
#[cfg(feature="std")] | ||
use std::error::Error as stdError; | ||
#[cfg(feature="std")] | ||
use std::io; | ||
|
||
/// Error kind which can be matched over. | ||
#[derive(PartialEq, Eq, Debug, Copy, Clone)] | ||
pub enum ErrorKind { | ||
/// Feature is not available; not recoverable. | ||
/// | ||
/// This is the most permanent failure type and implies the error cannot be | ||
/// resolved simply by retrying (e.g. the feature may not exist in this | ||
/// build of the application or on the current platform). | ||
Unavailable, | ||
/// General failure; there may be a chance of recovery on retry. | ||
/// | ||
/// This is the catch-all kind for errors from known and unknown sources | ||
/// which do not have a more specific kind / handling method. | ||
/// | ||
/// It is suggested to retry a couple of times or retry later when | ||
/// handling; some error sources may be able to resolve themselves, | ||
/// although this is not likely. | ||
Unexpected, | ||
/// A transient failure which likely can be resolved or worked around. | ||
/// | ||
/// This error kind exists for a few specific cases where it is known that | ||
/// the error likely can be resolved internally, but is reported anyway. | ||
Transient, | ||
/// Not ready yet: recommended to try again a little later. | ||
/// | ||
/// This error kind implies the generator needs more time or needs some | ||
/// other part of the application to do something else first before it is | ||
/// ready for use; for example this may be used by external generators | ||
/// which require time for initialization. | ||
NotReady, | ||
#[doc(hidden)] | ||
__Nonexhaustive, | ||
} | ||
|
||
impl ErrorKind { | ||
/// True if this kind of error may resolve itself on retry. | ||
/// | ||
/// See also `should_wait()`. | ||
pub fn should_retry(self) -> bool { | ||
self != ErrorKind::Unavailable | ||
} | ||
|
||
/// True if we should retry but wait before retrying | ||
/// | ||
/// This implies `should_retry()` is true. | ||
pub fn should_wait(self) -> bool { | ||
self == ErrorKind::NotReady | ||
} | ||
|
||
/// A description of this error kind | ||
pub fn description(self) -> &'static str { | ||
match self { | ||
ErrorKind::Unavailable => "permanently unavailable", | ||
ErrorKind::Unexpected => "unexpected failure", | ||
ErrorKind::Transient => "transient failure", | ||
ErrorKind::NotReady => "not ready yet", | ||
ErrorKind::__Nonexhaustive => unreachable!(), | ||
} | ||
} | ||
} | ||
|
||
// A randomly-chosen 24-bit prefix for our codes. | ||
#[cfg(not(feature="std"))] | ||
pub(crate) const CODE_PREFIX: u32 = 0x517e8100; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current |
||
|
||
/// Error type of random number generators | ||
/// | ||
/// This is a relatively simple error type, designed for compatibility with and | ||
/// without the Rust `std` library. It embeds a "kind" code, a message (static | ||
/// string only), and an optional chained cause (`std` only). The `kind` and | ||
/// `msg` fields can be accessed directly; cause can be accessed via | ||
/// without the Rust `std` library. It embeds a message (static | ||
/// string only), and an optional chained cause (`std` only). The | ||
/// `msg` field can be accessed directly; cause can be accessed via | ||
/// `std::error::Error::cause` or `Error::take_cause`. Construction can only be | ||
/// done via `Error::new` or `Error::with_cause`. | ||
#[derive(Debug)] | ||
pub struct Error { | ||
/// The error kind | ||
pub kind: ErrorKind, | ||
/// The error message | ||
pub msg: &'static str, | ||
#[cfg(feature="std")] | ||
cause: Option<Box<stdError + Send + Sync>>, | ||
cause: Option<Box<std::error::Error + Send + Sync>>, | ||
#[cfg(not(feature="std"))] | ||
code: NonZeroU32, | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't this still have the issue that the error types are incompatible for different features, making the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, with enabled There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Functionally this is the case (and the |
||
|
||
impl Error { | ||
/// Create a new instance, with specified kind and a message. | ||
pub fn new(kind: ErrorKind, msg: &'static str) -> Self { | ||
/// Create a new instance, with a message. | ||
pub fn new(msg: &'static str) -> Self { | ||
#[cfg(feature="std")] { | ||
Error { kind, msg, cause: None } | ||
Error { msg, cause: None } | ||
} | ||
#[cfg(not(feature="std"))] { | ||
Error { kind, msg } | ||
Error { msg, code: NonZeroU32::new(CODE_PREFIX).unwrap() } | ||
} | ||
} | ||
|
||
/// Create a new instance, with specified kind, message, and a | ||
/// chained cause. | ||
/// | ||
/// Note: `stdError` is an alias for `std::error::Error`. | ||
/// | ||
/// If not targetting `std` (i.e. `no_std`), this function is replaced by | ||
/// another with the same prototype, except that there are no bounds on the | ||
/// type `E` (because both `Box` and `stdError` are unavailable), and the | ||
/// `cause` is ignored. | ||
/// Create a new instance, with a message and a chained cause. | ||
/// | ||
/// This function is only available with the `std` feature. | ||
// NOTE: with specialisation we could support both. | ||
#[cfg(feature="std")] | ||
pub fn with_cause<E>(kind: ErrorKind, msg: &'static str, cause: E) -> Self | ||
where E: Into<Box<stdError + Send + Sync>> | ||
pub fn with_cause<E>(msg: &'static str, cause: E) -> Self | ||
where E: Into<Box<std::error::Error + Send + Sync>> | ||
{ | ||
Error { kind, msg, cause: Some(cause.into()) } | ||
Error { msg, cause: Some(cause.into()) } | ||
} | ||
|
||
/// Create a new instance, with specified kind, message, and a | ||
/// chained cause. | ||
/// | ||
/// In `no_std` mode the *cause* is ignored. | ||
/// Create a new instance, with a message and an error code. | ||
/// | ||
/// This function is only available without the `std` feature. | ||
#[cfg(not(feature="std"))] | ||
pub fn with_cause<E>(kind: ErrorKind, msg: &'static str, _cause: E) -> Self { | ||
Error { kind, msg } | ||
pub fn with_code(msg: &'static str, code: NonZeroU32) -> Self { | ||
Error { msg, code } | ||
} | ||
|
||
/// Take the cause, if any. This allows the embedded cause to be extracted. | ||
/// This uses `Option::take`, leaving `self` with no cause. | ||
/// | ||
/// This function is only available with the `std` feature. | ||
#[cfg(feature="std")] | ||
pub fn take_cause(&mut self) -> Option<Box<stdError + Send + Sync>> { | ||
pub fn take_cause(&mut self) -> Option<Box<std::error::Error + Send + Sync>> { | ||
self.cause.take() | ||
} | ||
|
||
/// Retrieve the error code. | ||
/// | ||
/// This function is only available without the `std` feature. | ||
#[cfg(not(feature="std"))] | ||
pub fn code(&self) -> NonZeroU32 { | ||
self.code | ||
} | ||
} | ||
|
||
impl fmt::Display for Error { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
#[cfg(feature="std")] { | ||
if let Some(ref cause) = self.cause { | ||
return write!(f, "{} ({}); cause: {}", | ||
self.msg, self.kind.description(), cause); | ||
return write!(f, "{}; cause: {}", | ||
self.msg, cause); | ||
} | ||
} | ||
write!(f, "{} ({})", self.msg, self.kind.description()) | ||
write!(f, "{}", self.msg) | ||
} | ||
} | ||
|
||
#[cfg(feature="getrandom")] | ||
impl From<getrandom::Error> for Error { | ||
fn from(error: getrandom::Error) -> Self { | ||
let msg = "getrandom error"; | ||
#[cfg(feature="std")] { | ||
Error { msg, cause: Some(Box::new(error)) } | ||
} | ||
#[cfg(not(feature="std"))] { | ||
Error { msg, code: error.code() } | ||
} | ||
} | ||
} | ||
|
||
#[cfg(feature="std")] | ||
impl stdError for Error { | ||
impl std::error::Error for Error { | ||
fn description(&self) -> &str { | ||
self.msg | ||
} | ||
|
||
fn cause(&self) -> Option<&stdError> { | ||
self.cause.as_ref().map(|e| e.as_ref() as &stdError) | ||
fn cause(&self) -> Option<&std::error::Error> { | ||
self.cause.as_ref().map(|e| e.as_ref() as &std::error::Error) | ||
} | ||
} | ||
|
||
#[cfg(feature="std")] | ||
impl From<Error> for io::Error { | ||
impl From<Error> for std::io::Error { | ||
fn from(error: Error) -> Self { | ||
use std::io::ErrorKind::*; | ||
match error.kind { | ||
ErrorKind::Unavailable => io::Error::new(NotFound, error), | ||
ErrorKind::Unexpected | | ||
ErrorKind::Transient => io::Error::new(Other, error), | ||
ErrorKind::NotReady => io::Error::new(WouldBlock, error), | ||
ErrorKind::__Nonexhaustive => unreachable!(), | ||
} | ||
std::io::Error::new(std::io::ErrorKind::Other, error) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this coupled to
getrandom
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean we should support
std
withoutgetrandom
? We could try, but (1)getrandom
supports (pretty-much) anystd
platform anyway and (2) I'm not sure Cargo allows us to implygetrandom/std
only when bothstd
andgetrandom
are enabled.