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}