Skip to content

Commit 8e8bb6a

Browse files
authored
Merge pull request #225 from dhardy/error
New error handling + type
2 parents 3d1dbb6 + c9bfa3a commit 8e8bb6a

File tree

5 files changed

+380
-257
lines changed

5 files changed

+380
-257
lines changed

src/error.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright 2017-2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! Error types
12+
13+
use core::fmt;
14+
15+
#[cfg(feature="std")]
16+
use std::error::Error as stdError;
17+
18+
/// Error kind which can be matched over.
19+
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
20+
pub enum ErrorKind {
21+
/// Permanent failure: likely not recoverable without user action.
22+
Unavailable,
23+
/// Temporary failure: recommended to retry a few times, but may also be
24+
/// irrecoverable.
25+
Transient,
26+
/// Not ready yet: recommended to try again a little later.
27+
NotReady,
28+
/// Uncategorised error
29+
Other,
30+
#[doc(hidden)]
31+
__Nonexhaustive,
32+
}
33+
34+
impl ErrorKind {
35+
/// True if this kind of error may resolve itself on retry.
36+
///
37+
/// See also `should_wait()`.
38+
pub fn should_retry(self) -> bool {
39+
match self {
40+
ErrorKind::Transient | ErrorKind::NotReady => true,
41+
_ => false,
42+
}
43+
}
44+
45+
/// True if we should retry but wait before retrying
46+
///
47+
/// This implies `should_retry()` is true.
48+
pub fn should_wait(self) -> bool {
49+
self == ErrorKind::NotReady
50+
}
51+
52+
/// A description of this error kind
53+
pub fn description(self) -> &'static str {
54+
match self {
55+
ErrorKind::Unavailable => "permanent failure or unavailable",
56+
ErrorKind::Transient => "transient failure",
57+
ErrorKind::NotReady => "not ready yet",
58+
ErrorKind::Other => "uncategorised",
59+
ErrorKind::__Nonexhaustive => unreachable!(),
60+
}
61+
}
62+
}
63+
64+
/// Error type of random number generators
65+
///
66+
/// This is a relatively simple error type, designed for compatibility with and
67+
/// without the Rust `std` library. It embeds a "kind" code, a message (static
68+
/// string only), and an optional chained cause (`std` only).
69+
#[derive(Debug)]
70+
pub struct Error {
71+
kind: ErrorKind,
72+
msg: &'static str,
73+
#[cfg(feature="std")]
74+
cause: Option<Box<stdError + Send + Sync>>,
75+
}
76+
77+
impl Error {
78+
/// Create a new instance, with specified kind and a message.
79+
pub fn new(kind: ErrorKind, msg: &'static str) -> Self {
80+
#[cfg(feature="std")] {
81+
Error { kind: kind, msg: msg, cause: None }
82+
}
83+
#[cfg(not(feature="std"))] {
84+
Error { kind: kind, msg: msg }
85+
}
86+
}
87+
88+
/// Create a new instance, with specified kind, message, and a
89+
/// chained cause.
90+
///
91+
/// Note: `stdError` is an alias for `std::error::Error`.
92+
///
93+
/// If not targetting `std` (i.e. `no_std`), this function is replaced by
94+
/// another with the same prototype, except that there are no bounds on the
95+
/// type `E` (because both `Box` and `stdError` are unavailable), and the
96+
/// `cause` is ignored.
97+
#[cfg(feature="std")]
98+
pub fn with_cause<E>(kind: ErrorKind, msg: &'static str, cause: E) -> Self
99+
where E: Into<Box<stdError + Send + Sync>>
100+
{
101+
Error { kind: kind, msg: msg, cause: Some(cause.into()) }
102+
}
103+
104+
/// Create a new instance, with specified kind, message, and a
105+
/// chained cause.
106+
///
107+
/// In `no_std` mode the *cause* is ignored.
108+
#[cfg(not(feature="std"))]
109+
pub fn with_cause<E>(kind: ErrorKind, msg: &'static str, _cause: E) -> Self {
110+
Error { kind: kind, msg: msg }
111+
}
112+
113+
/// Get the error kind
114+
pub fn kind(&self) -> ErrorKind {
115+
self.kind
116+
}
117+
118+
/// Get the error message
119+
pub fn msg(&self) -> &'static str {
120+
self.msg
121+
}
122+
}
123+
124+
impl fmt::Display for Error {
125+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
126+
write!(f, "RNG error [{}]: {}", self.kind.description(), self.msg())
127+
}
128+
}
129+
130+
#[cfg(feature="std")]
131+
impl stdError for Error {
132+
fn description(&self) -> &str {
133+
self.msg
134+
}
135+
136+
fn cause(&self) -> Option<&stdError> {
137+
self.cause.as_ref().map(|e| e.as_ref() as &stdError)
138+
}
139+
}

src/lib.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,6 @@
253253
use core::marker;
254254
use core::mem;
255255
#[cfg(feature="std")] use std::cell::RefCell;
256-
#[cfg(feature="std")] use std::io;
257256
#[cfg(feature="std")] use std::rc::Rc;
258257

259258
// external rngs
@@ -266,6 +265,9 @@ pub use chacha::ChaChaRng;
266265
pub use prng::XorShiftRng;
267266
pub use prng::Hc128Rng;
268267

268+
// error types
269+
pub use error::{ErrorKind, Error};
270+
269271
// local use declarations
270272
#[cfg(target_pointer_width = "32")]
271273
use prng::IsaacRng as IsaacWordRng;
@@ -295,6 +297,7 @@ pub mod isaac {
295297
}
296298

297299
// private modules
300+
mod error;
298301
mod rand_impls;
299302
mod prng;
300303

@@ -450,6 +453,22 @@ pub trait Rng {
450453
impls::fill_bytes_via_u64(self, dest)
451454
}
452455

456+
/// Fill `dest` entirely with random data.
457+
///
458+
/// This is the only method which allows an RNG to report errors while
459+
/// generating random data; other methods either handle the error
460+
/// internally or panic. This method is
461+
/// the intended way to use external (true) RNGs, like `OsRng`. Its main
462+
/// use-cases are to generate keys and to seed (infallible) PRNGs.
463+
///
464+
/// Other than error handling, this method is identical to [`fill_bytes`], and
465+
/// has a default implementation simply wrapping [`fill_bytes`].
466+
///
467+
/// [`fill_bytes`]: trait.Rng.html#method.fill_bytes
468+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
469+
Ok(self.fill_bytes(dest))
470+
}
471+
453472
/// Return a random value of a `Rand` type.
454473
///
455474
/// # Example
@@ -604,48 +623,68 @@ pub trait Rng {
604623
}
605624

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

631+
#[inline]
611632
fn next_u64(&mut self) -> u64 {
612633
(**self).next_u64()
613634
}
614635

636+
#[inline]
615637
fn next_f32(&mut self) -> f32 {
616638
(**self).next_f32()
617639
}
618640

641+
#[inline]
619642
fn next_f64(&mut self) -> f64 {
620643
(**self).next_f64()
621644
}
622645

646+
#[inline]
623647
fn fill_bytes(&mut self, dest: &mut [u8]) {
624648
(**self).fill_bytes(dest)
625649
}
650+
651+
#[inline]
652+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
653+
(**self).try_fill_bytes(dest)
654+
}
626655
}
627656

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

664+
#[inline]
634665
fn next_u64(&mut self) -> u64 {
635666
(**self).next_u64()
636667
}
637668

669+
#[inline]
638670
fn next_f32(&mut self) -> f32 {
639671
(**self).next_f32()
640672
}
641673

674+
#[inline]
642675
fn next_f64(&mut self) -> f64 {
643676
(**self).next_f64()
644677
}
645678

679+
#[inline]
646680
fn fill_bytes(&mut self, dest: &mut [u8]) {
647681
(**self).fill_bytes(dest)
648682
}
683+
684+
#[inline]
685+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
686+
(**self).try_fill_bytes(dest)
687+
}
649688
}
650689

651690
/// Iterator which will generate a stream of random items.
@@ -778,7 +817,7 @@ impl StdRng {
778817
/// Reading the randomness from the OS may fail, and any error is
779818
/// propagated via the `io::Result` return value.
780819
#[cfg(feature="std")]
781-
pub fn new() -> io::Result<StdRng> {
820+
pub fn new() -> Result<StdRng, Error> {
782821
match OsRng::new() {
783822
Ok(mut r) => Ok(StdRng { rng: r.gen() }),
784823
Err(e1) => {
@@ -808,6 +847,11 @@ impl Rng for StdRng {
808847
fn fill_bytes(&mut self, dest: &mut [u8]) {
809848
self.rng.fill_bytes(dest)
810849
}
850+
851+
#[inline]
852+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
853+
self.rng.try_fill_bytes(dest)
854+
}
811855
}
812856

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

893937
#[cfg(feature="std")]
894938
impl Rng for ThreadRng {
939+
#[inline]
895940
fn next_u32(&mut self) -> u32 {
896941
self.rng.borrow_mut().next_u32()
897942
}
898943

944+
#[inline]
899945
fn next_u64(&mut self) -> u64 {
900946
self.rng.borrow_mut().next_u64()
901947
}
@@ -904,6 +950,11 @@ impl Rng for ThreadRng {
904950
fn fill_bytes(&mut self, bytes: &mut [u8]) {
905951
self.rng.borrow_mut().fill_bytes(bytes)
906952
}
953+
954+
#[inline]
955+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
956+
self.rng.borrow_mut().try_fill_bytes(dest)
957+
}
907958
}
908959

909960
/// Generates a random value using the thread-local random number generator.

0 commit comments

Comments
 (0)