Skip to content

Commit

Permalink
Merge pull request #225 from dhardy/error
Browse files Browse the repository at this point in the history
New error handling + type
  • Loading branch information
dhardy authored Jan 10, 2018
2 parents 3d1dbb6 + c9bfa3a commit 8e8bb6a
Show file tree
Hide file tree
Showing 5 changed files with 380 additions and 257 deletions.
139 changes: 139 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright 2017-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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;

/// Error kind which can be matched over.
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum ErrorKind {
/// Permanent failure: likely not recoverable without user action.
Unavailable,
/// Temporary failure: recommended to retry a few times, but may also be
/// irrecoverable.
Transient,
/// Not ready yet: recommended to try again a little later.
NotReady,
/// Uncategorised error
Other,
#[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 {
match self {
ErrorKind::Transient | ErrorKind::NotReady => true,
_ => false,
}
}

/// 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 => "permanent failure or unavailable",
ErrorKind::Transient => "transient failure",
ErrorKind::NotReady => "not ready yet",
ErrorKind::Other => "uncategorised",
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).
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
msg: &'static str,
#[cfg(feature="std")]
cause: Option<Box<stdError + Send + Sync>>,
}

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: kind, msg: msg, cause: None }
}
#[cfg(not(feature="std"))] {
Error { kind: kind, msg: 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<E>(kind: ErrorKind, msg: &'static str, cause: E) -> Self
where E: Into<Box<stdError + Send + Sync>>
{
Error { kind: kind, msg: 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<E>(kind: ErrorKind, msg: &'static str, _cause: E) -> Self {
Error { kind: kind, msg: msg }
}

/// Get the error kind
pub fn kind(&self) -> ErrorKind {
self.kind
}

/// Get the error message
pub fn msg(&self) -> &'static str {
self.msg
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "RNG error [{}]: {}", self.kind.description(), self.msg())
}
}

#[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)
}
}
55 changes: 53 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,6 @@
use core::marker;
use core::mem;
#[cfg(feature="std")] use std::cell::RefCell;
#[cfg(feature="std")] use std::io;
#[cfg(feature="std")] use std::rc::Rc;

// external rngs
Expand All @@ -266,6 +265,9 @@ pub use chacha::ChaChaRng;
pub use prng::XorShiftRng;
pub use prng::Hc128Rng;

// error types
pub use error::{ErrorKind, Error};

// local use declarations
#[cfg(target_pointer_width = "32")]
use prng::IsaacRng as IsaacWordRng;
Expand Down Expand Up @@ -295,6 +297,7 @@ pub mod isaac {
}

// private modules
mod error;
mod rand_impls;
mod prng;

Expand Down Expand Up @@ -450,6 +453,22 @@ pub trait Rng {
impls::fill_bytes_via_u64(self, dest)
}

/// Fill `dest` entirely with random data.
///
/// This is the only method which allows an RNG to report errors while
/// generating random data; other methods either handle the error
/// internally or panic. This method is
/// the intended way to use external (true) RNGs, like `OsRng`. Its main
/// use-cases are to generate keys and to seed (infallible) PRNGs.
///
/// Other than error handling, this method is identical to [`fill_bytes`], and
/// has a default implementation simply wrapping [`fill_bytes`].
///
/// [`fill_bytes`]: trait.Rng.html#method.fill_bytes
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
Ok(self.fill_bytes(dest))
}

/// Return a random value of a `Rand` type.
///
/// # Example
Expand Down Expand Up @@ -604,48 +623,68 @@ pub trait Rng {
}

impl<'a, R: ?Sized> Rng for &'a mut R where R: Rng {
#[inline]
fn next_u32(&mut self) -> u32 {
(**self).next_u32()
}

#[inline]
fn next_u64(&mut self) -> u64 {
(**self).next_u64()
}

#[inline]
fn next_f32(&mut self) -> f32 {
(**self).next_f32()
}

#[inline]
fn next_f64(&mut self) -> f64 {
(**self).next_f64()
}

#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
(**self).fill_bytes(dest)
}

#[inline]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
(**self).try_fill_bytes(dest)
}
}

#[cfg(feature="std")]
impl<R: ?Sized> Rng for Box<R> where R: Rng {
#[inline]
fn next_u32(&mut self) -> u32 {
(**self).next_u32()
}

#[inline]
fn next_u64(&mut self) -> u64 {
(**self).next_u64()
}

#[inline]
fn next_f32(&mut self) -> f32 {
(**self).next_f32()
}

#[inline]
fn next_f64(&mut self) -> f64 {
(**self).next_f64()
}

#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
(**self).fill_bytes(dest)
}

#[inline]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
(**self).try_fill_bytes(dest)
}
}

/// Iterator which will generate a stream of random items.
Expand Down Expand Up @@ -778,7 +817,7 @@ impl StdRng {
/// Reading the randomness from the OS may fail, and any error is
/// propagated via the `io::Result` return value.
#[cfg(feature="std")]
pub fn new() -> io::Result<StdRng> {
pub fn new() -> Result<StdRng, Error> {
match OsRng::new() {
Ok(mut r) => Ok(StdRng { rng: r.gen() }),
Err(e1) => {
Expand Down Expand Up @@ -808,6 +847,11 @@ impl Rng for StdRng {
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.rng.fill_bytes(dest)
}

#[inline]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
self.rng.try_fill_bytes(dest)
}
}

impl<'a> SeedableRng<&'a [usize]> for StdRng {
Expand Down Expand Up @@ -892,10 +936,12 @@ pub fn thread_rng() -> ThreadRng {

#[cfg(feature="std")]
impl Rng for ThreadRng {
#[inline]
fn next_u32(&mut self) -> u32 {
self.rng.borrow_mut().next_u32()
}

#[inline]
fn next_u64(&mut self) -> u64 {
self.rng.borrow_mut().next_u64()
}
Expand All @@ -904,6 +950,11 @@ impl Rng for ThreadRng {
fn fill_bytes(&mut self, bytes: &mut [u8]) {
self.rng.borrow_mut().fill_bytes(bytes)
}

#[inline]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
self.rng.borrow_mut().try_fill_bytes(dest)
}
}

/// Generates a random value using the thread-local random number generator.
Expand Down
Loading

0 comments on commit 8e8bb6a

Please sign in to comment.