// Copyright 2018 Developers of the Rand project. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Error types use core::fmt; #[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!(), } } } /// 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 /// `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>, } impl Error { /// Create a new instance, with specified kind and a message. pub fn new(kind: ErrorKind, msg: &'static str) -> Self { #[cfg(feature="std")] { Error { kind, msg, cause: None } } #[cfg(not(feature="std"))] { Error { kind, msg } } } /// 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. #[cfg(feature="std")] pub fn with_cause(kind: ErrorKind, msg: &'static str, cause: E) -> Self where E: Into> { Error { kind, 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. #[cfg(not(feature="std"))] pub fn with_cause(kind: ErrorKind, msg: &'static str, _cause: E) -> Self { Error { kind, msg } } /// Take the cause, if any. This allows the embedded cause to be extracted. /// This uses `Option::take`, leaving `self` with no cause. #[cfg(feature="std")] pub fn take_cause(&mut self) -> Option> { self.cause.take() } } 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); } } write!(f, "{} ({})", self.msg, self.kind.description()) } } #[cfg(feature="std")] impl stdError for Error { fn description(&self) -> &str { self.msg } fn cause(&self) -> Option<&stdError> { self.cause.as_ref().map(|e| e.as_ref() as &stdError) } } #[cfg(feature="std")] impl From for 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!(), } } }