crazy_train/randomizer.rs
1//! This module provides a Randomizer struct that manages random number generation
2//! with support for seeded and non-seeded generation. It allows for generating
3//! random numbers, booleans, strings, paths, and shuffling items.
4//!
5
6use crate::generator::{StringDef, StringDefBuilder};
7use rand::{rngs::StdRng, seq::SliceRandom, Rng, RngCore, SeedableRng};
8use std::{cell::RefCell, path::PathBuf};
9
10/// Struct for managing random number generation, allowing seed control for reproducibility.
11pub struct Randomizer {
12 pub rng: RefCell<Box<dyn RngCore + Send>>,
13 pub seed: u64,
14}
15
16/// Default implementation for [`Randomizer`], initializing RNG with a random seed.
17impl Default for Randomizer {
18 fn default() -> Self {
19 let mut seed_rng = StdRng::from_entropy();
20 let seed = seed_rng.next_u64();
21
22 let rng = RefCell::new(Box::new(StdRng::seed_from_u64(seed)));
23
24 Self { rng, seed }
25 }
26}
27
28impl Randomizer {
29 /// Create a new [`Randomizer`] with a specified seed.
30 ///
31 /// # Example:
32 ///
33 /// ```rust
34 /// use crazy_train::Randomizer;
35 /// let randomizer = Randomizer::with_seed(42);
36 /// ```
37 #[must_use]
38 pub fn with_seed(seed: u64) -> Self {
39 let rng = RefCell::new(Box::new(StdRng::seed_from_u64(seed)));
40 Self { rng, seed }
41 }
42
43 /// Generate a random number between the specified minimum and maximum values (inclusive).
44 ///
45 /// # Example:
46 ///
47 /// ```rust
48 /// use crazy_train::Randomizer;
49 /// let randomizer = Randomizer::with_seed(42);
50 /// assert_eq!(randomizer.number_between(1,10), 7);
51 // assert_eq!(!randomizer.number_between(1,10), 2);
52 /// ```
53 pub fn number_between(&self, min: u32, max: u32) -> u32 {
54 let mut rng = self.rng.borrow_mut();
55 let random_number = rng.next_u32();
56 min + (random_number % (max - min + 1))
57 }
58
59 /// Generate a random boolean value (true or false).
60 ///
61 /// # Example:
62 ///
63 /// ```rust
64 /// use crazy_train::Randomizer;
65 /// let randomizer = Randomizer::with_seed(42);
66 /// assert!(randomizer.bool());
67 // assert!(!randomizer.bool());
68 /// ```
69 pub fn bool(&self) -> bool {
70 let mut rng = self.rng.borrow_mut();
71 let random_number = rng.next_u32();
72 random_number % 2 == 0
73 }
74
75 /// Create a [`StringDefBuilder`] based on a given [`StringDef`].
76 ///
77 /// # Example:
78 ///
79 /// ```rust
80 /// use crazy_train::{Randomizer, StringDef};
81 /// let string_def = StringDef::default();
82 /// let randomizer = Randomizer::with_seed(42);
83 /// assert_eq!(randomizer.string(string_def.clone()).to_string(), "noqkak");
84 /// assert_eq!(randomizer.string(string_def.clone()).include_capital_letters(true).to_string(), "TWdAyN");
85 /// assert_eq!(randomizer.string(string_def.clone()).include_unicode(true).to_string(), "kdnfa😩");
86 /// assert_eq!(randomizer.string(string_def.clone()).include_numbers(true).to_string(), "0684n0");
87 /// assert_eq!(randomizer.string(string_def.clone()).include_symbol(true).to_string(), "=wqf`g");
88 /// assert_eq!(randomizer.string(string_def.clone()).length(10).to_string(), "wgavmyyuzw");
89 /// ```
90 pub fn string(&self, def: StringDef) -> StringDefBuilder<'_> {
91 StringDefBuilder {
92 string_def: def,
93 rng: &self.rng,
94 }
95 }
96
97 /// Generate a random path of a specified length.
98 ///
99 /// # Example:
100 ///
101 /// ```rust
102 /// use crazy_train::Randomizer;
103 /// use std::path::PathBuf;
104 /// let randomizer = Randomizer::with_seed(42);
105 /// assert_eq!(randomizer.path(), PathBuf::from("gowqzkza"));
106 /// ```
107 pub fn path(&self) -> PathBuf {
108 let mut rng = self.rng.borrow_mut();
109
110 let path_length = rng.gen_range(5..=10);
111 let path_name: String = (0..path_length)
112 .map(|_| char::from(rng.gen_range(b'a'..=b'z')))
113 .collect();
114
115 PathBuf::from(path_name)
116 }
117
118 /// Shuffle a slice of items and return a new vector with the shuffled items.
119 ///
120 /// # Example:
121 ///
122 /// ```rust
123 /// use crazy_train::Randomizer;
124 /// let randomizer = Randomizer::with_seed(42);
125 /// let list = vec![1, 2, 3, 4, 5, 6];
126 /// assert_eq!(randomizer.shuffle(&list), vec![1, 5, 6, 3, 2, 4]);
127 /// ```
128 pub fn shuffle<T>(&self, items: &[T]) -> Vec<T>
129 where
130 T: Clone,
131 {
132 let mut rng = self.rng.borrow_mut();
133 let mut shuffled_items = items.to_vec();
134 shuffled_items.shuffle(&mut *rng);
135 shuffled_items
136 }
137
138 /// Pick a random selection of items from a given slice.
139 ///
140 /// # Example:
141 ///
142 /// ```rust
143 /// use crazy_train::Randomizer;
144 /// let randomizer = Randomizer::with_seed(42);
145 /// let list = vec![1, 2, 3, 4, 5, 6];
146 /// assert_eq!(randomizer.pick_random(&list), vec![2, 6]);
147 /// ```
148 pub fn pick_random<T>(&self, items: &[T]) -> Vec<T>
149 where
150 T: Clone,
151 {
152 let mut rng = self.rng.borrow_mut();
153
154 let count = rng.gen_range(1..=10);
155
156 (0..count)
157 .map(|_| {
158 let index = rng.gen_range(0..items.len());
159 items[index].clone()
160 })
161 .collect()
162 }
163}
164
165#[cfg(test)]
166mod tests {
167
168 use super::*;
169
170 #[test]
171 fn rand_number() {
172 let randomizer = Randomizer::with_seed(42);
173 assert_eq!(randomizer.number_between(1, 100), 27);
174 assert_eq!(randomizer.number_between(1, 100), 52);
175 assert_eq!(randomizer.number_between(1, 100), 98);
176 }
177
178 #[test]
179 fn rand_bool() {
180 let randomizer = Randomizer::with_seed(42);
181 assert!(randomizer.bool());
182 assert!(!randomizer.bool());
183 assert!(!randomizer.bool());
184 assert!(!randomizer.bool());
185 assert!(!randomizer.bool());
186 assert!(!randomizer.bool());
187 assert!(randomizer.bool());
188 }
189
190 #[test]
191 fn rand_string() {
192 let randomizer = Randomizer::with_seed(42);
193 assert_eq!(
194 randomizer.string(StringDef::default()).to_string(),
195 "noqkak".to_string()
196 );
197 assert_eq!(
198 randomizer.string(StringDef::default()).to_string(),
199 "twdayn".to_string()
200 );
201 assert_eq!(
202 randomizer
203 .string(StringDef {
204 include_symbol: true,
205 ..Default::default()
206 })
207 .to_string(),
208 "kdnfa)".to_string()
209 );
210
211 assert_eq!(
212 randomizer
213 .string(StringDef {
214 length: 10,
215 ..Default::default()
216 })
217 .to_string(),
218 "hdpnewfykq".to_string()
219 );
220 assert_eq!(
221 randomizer
222 .string(StringDef {
223 include_unicode: true,
224 ..Default::default()
225 })
226 .to_string(),
227 "vjjp😓".to_string()
228 );
229 }
230
231 #[test]
232 fn rand_path() {
233 let randomizer = Randomizer::with_seed(42);
234 assert_eq!(randomizer.path(), PathBuf::from("gowqzkza"));
235 }
236
237 #[test]
238 fn shuffle() {
239 let randomizer = Randomizer::with_seed(42);
240 let list = vec![1, 2, 3, 4, 5, 6];
241 assert_eq!(randomizer.shuffle(&list), vec![1, 5, 6, 3, 2, 4]);
242 }
243
244 #[test]
245 fn pick_random() {
246 let randomizer = Randomizer::with_seed(42);
247 let list = vec![1, 2, 3, 4, 5, 6];
248 assert_eq!(randomizer.pick_random(&list), vec![2, 6]);
249 assert_eq!(randomizer.pick_random(&list), vec![3, 1, 3, 5, 6, 1, 6]);
250 }
251}