1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
use std::sync::Arc;

use parking_lot::RwLock;
use rand::distributions::{Alphanumeric, Standard};
use rand::prelude::Distribution;
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha12Rng;

use crate::Seed;

#[derive(Debug)]
#[must_use]
/// The main RNG data structure.
pub struct Random {
  rng: Arc<RwLock<ChaCha12Rng>>,
}

#[must_use]
#[cfg(feature = "std")]
pub(crate) fn new_seed() -> Seed {
  let mut rng = rand::thread_rng();
  Seed(rng.gen())
}

impl Random {
  /// Create a new RNG from a new, random seed.
  #[cfg(feature = "std")]
  pub fn new() -> Self {
    Self::from_seed(new_seed())
  }

  // Need to allow this lint so we can write an API that consumes the Seed even
  // though it's not technically necessary.
  #[allow(clippy::needless_pass_by_value)]
  /// Create a new [Random] RNG from a seed.
  pub fn from_seed(seed: Seed) -> Self {
    let rng = ChaCha12Rng::seed_from_u64(seed.0);
    Self {
      rng: Arc::new(RwLock::new(rng)),
    }
  }

  /// Generated a new seed from this RNG.
  pub fn seed(&self) -> Seed {
    Seed(self.gen())
  }

  #[must_use]
  /// Function that delegates to [rand::Rng::gen()]
  pub fn gen<T>(&self) -> T
  where
    Standard: Distribution<T>,
  {
    let mut rng = self.rng.write();
    rng.gen()
  }

  /// Utility function to generate a new [u32]
  pub fn u32(&self) -> u32 {
    self.gen()
  }

  /// Utility function to generate a new [i32]
  pub fn i32(&self) -> i32 {
    self.gen()
  }

  /// Utility function to generate a new [Vec] of bytes.
  pub fn bytes(&self, length: usize) -> Vec<u8> {
    let mut bytes: Vec<u8> = Vec::with_capacity(length);
    let mut rng = self.rng.write();
    for _ in 0..length {
      bytes.push(rng.gen());
    }
    bytes
  }

  /// Utility function to generate a new [String]
  pub fn string(&self, length: usize) -> String {
    let mut string: String = String::with_capacity(length);
    let mut rng = self.rng.write();

    for _ in 0..length {
      string.push(rng.gen());
    }
    string
  }

  /// Utility function to generate a new [String] consisting only of numbers and letters.
  pub fn alphanumeric(&self, length: usize) -> String {
    let mut rng = self.rng.write();
    let chars: String = std::iter::repeat(())
      .map(|()| rng.sample(Alphanumeric))
      .map(char::from)
      .take(length)
      .collect();
    chars
  }

  /// Utility function to generate a new [uuid::Uuid]
  #[cfg(feature = "uuid")]
  pub fn uuid(&self) -> uuid::Uuid {
    let mut raw_bytes: [u8; 16] = [0; 16];
    let mut rng = self.rng.write();
    rng.fill(&mut raw_bytes);
    let bytes: uuid::Bytes = raw_bytes;
    let builder = uuid::Builder::from_bytes(bytes);
    builder.into_uuid()
  }

  /// Utility function that delegates to [rand::Rng::gen_range()]
  pub fn range(&self, min: u32, max: u32) -> u32 {
    let mut rng = self.rng.write();
    rng.gen_range(min..max)
  }
}

#[cfg(feature = "std")]
impl Default for Random {
  fn default() -> Self {
    Self::new()
  }
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn bytes() {
    let rng = Random::from_seed(Seed(100000));
    let bytes1 = rng.bytes(10);
    let bytes2 = rng.bytes(10);
    assert_ne!(bytes1, bytes2);
    let rng = Random::from_seed(Seed(100000));
    let bytes2 = rng.bytes(10);
    assert_eq!(bytes1, bytes2);
  }
  #[test]
  fn string() {
    let rng = Random::from_seed(Seed(100000));
    let v1 = rng.string(10);
    let v2 = rng.string(10);
    assert_ne!(v1, v2);
    let rng = Random::from_seed(Seed(100000));
    let v2 = rng.string(10);
    assert_eq!(v1, v2);
  }

  #[test]
  fn alphanum() {
    let rng = Random::from_seed(Seed(100000));
    let v1 = rng.alphanumeric(10);
    let v2 = rng.alphanumeric(10);
    assert_ne!(v1, v2);
    let rng = Random::from_seed(Seed(100000));
    let v2 = rng.alphanumeric(10);
    assert_eq!(v1, v2);
  }

  #[test]
  #[cfg(feature = "uuid")]
  fn uuid() {
    let rng = Random::from_seed(Seed(100000));
    let v1 = rng.uuid();
    let v2 = rng.uuid();
    assert_ne!(v1, v2);
    let rng = Random::from_seed(Seed(100000));
    let v2 = rng.uuid();
    assert_eq!(v1, v2);
  }
}