1use rand::{RngExt, distr::Alphanumeric, prelude::IndexedRandom};
4use wae_types::{WaeError, WaeErrorKind, WaeResult as TestingResult};
5
6pub trait Fixture: Sized {
8 fn generate() -> TestingResult<Self>;
10}
11
12pub trait FixtureBuilder<T>: Sized {
14 fn build(self) -> TestingResult<T>;
16}
17
18#[derive(Debug, Clone)]
20pub struct RandomString {
21 pub length: usize,
23 pub prefix: Option<String>,
25 pub suffix: Option<String>,
27}
28
29impl Default for RandomString {
30 fn default() -> Self {
31 Self { length: 10, prefix: None, suffix: None }
32 }
33}
34
35impl RandomString {
36 pub fn new() -> Self {
38 Self::default()
39 }
40
41 pub fn length(mut self, len: usize) -> Self {
43 self.length = len;
44 self
45 }
46
47 pub fn prefix(mut self, prefix: impl Into<String>) -> Self {
49 self.prefix = Some(prefix.into());
50 self
51 }
52
53 pub fn suffix(mut self, suffix: impl Into<String>) -> Self {
55 self.suffix = Some(suffix.into());
56 self
57 }
58
59 pub fn generate(&self) -> TestingResult<String> {
61 let mut rng = rand::rng();
62 let random_part: String = (0..self.length).map(|_| rng.sample(Alphanumeric) as char).collect();
63
64 let mut result = String::new();
65 if let Some(ref prefix) = self.prefix {
66 result.push_str(prefix);
67 }
68 result.push_str(&random_part);
69 if let Some(ref suffix) = self.suffix {
70 result.push_str(suffix);
71 }
72
73 Ok(result)
74 }
75}
76
77impl Fixture for String {
78 fn generate() -> TestingResult<Self> {
79 RandomString::new().generate()
80 }
81}
82
83#[derive(Debug, Clone)]
85pub struct RandomNumber<T> {
86 pub min: T,
88 pub max: T,
90 _marker: std::marker::PhantomData<T>,
91}
92
93impl<T> RandomNumber<T> {
94 pub fn new(min: T, max: T) -> Self {
96 Self { min, max, _marker: std::marker::PhantomData }
97 }
98}
99
100impl RandomNumber<i32> {
101 pub fn generate(&self) -> TestingResult<i32> {
103 let mut rng = rand::rng();
104 Ok(rng.random_range(self.min..=self.max))
105 }
106}
107
108impl RandomNumber<i64> {
109 pub fn generate(&self) -> TestingResult<i64> {
111 let mut rng = rand::rng();
112 Ok(rng.random_range(self.min..=self.max))
113 }
114}
115
116impl RandomNumber<u32> {
117 pub fn generate(&self) -> TestingResult<u32> {
119 let mut rng = rand::rng();
120 Ok(rng.random_range(self.min..=self.max))
121 }
122}
123
124impl RandomNumber<u64> {
125 pub fn generate(&self) -> TestingResult<u64> {
127 let mut rng = rand::rng();
128 Ok(rng.random_range(self.min..=self.max))
129 }
130}
131
132impl RandomNumber<f64> {
133 pub fn generate(&self) -> TestingResult<f64> {
135 let mut rng = rand::rng();
136 Ok(rng.random_range(self.min..=self.max))
137 }
138}
139
140impl Fixture for i32 {
141 fn generate() -> TestingResult<Self> {
142 let generator: RandomNumber<i32> = RandomNumber::new(0, 1000000);
143 generator.generate()
144 }
145}
146
147impl Fixture for u64 {
148 fn generate() -> TestingResult<Self> {
149 RandomNumber::new(0u64, 1000000u64).generate()
150 }
151}
152
153#[derive(Debug, Clone)]
155pub struct RandomEmail {
156 pub domain: String,
158}
159
160impl Default for RandomEmail {
161 fn default() -> Self {
162 Self { domain: "test.example.com".to_string() }
163 }
164}
165
166impl RandomEmail {
167 pub fn new() -> Self {
169 Self::default()
170 }
171
172 pub fn domain(mut self, domain: impl Into<String>) -> Self {
174 self.domain = domain.into();
175 self
176 }
177
178 pub fn generate(&self) -> TestingResult<String> {
180 let username = RandomString::new().length(8).generate()?;
181 Ok(format!("{}@{}", username, self.domain))
182 }
183}
184
185#[derive(Debug, Clone, Default)]
187pub struct RandomUuid {
188 pub version4: bool,
190}
191
192impl RandomUuid {
193 pub fn new() -> Self {
195 Self { version4: true }
196 }
197
198 pub fn version7(mut self) -> Self {
200 self.version4 = false;
201 self
202 }
203
204 pub fn generate(&self) -> TestingResult<uuid::Uuid> {
206 if self.version4 { Ok(uuid::Uuid::new_v4()) } else { Ok(uuid::Uuid::now_v7()) }
207 }
208
209 pub fn generate_string(&self) -> TestingResult<String> {
211 Ok(self.generate()?.to_string())
212 }
213}
214
215impl Fixture for uuid::Uuid {
216 fn generate() -> TestingResult<Self> {
217 RandomUuid::new().generate()
218 }
219}
220
221#[derive(Debug, Clone, Default)]
223pub struct RandomBool {
224 pub true_probability: f64,
226}
227
228impl RandomBool {
229 pub fn new() -> Self {
231 Self { true_probability: 0.5 }
232 }
233
234 pub fn probability(mut self, prob: f64) -> Self {
236 self.true_probability = prob.clamp(0.0, 1.0);
237 self
238 }
239
240 pub fn generate(&self) -> TestingResult<bool> {
242 let mut rng = rand::rng();
243 Ok(rng.random_bool(self.true_probability))
244 }
245}
246
247impl Fixture for bool {
248 fn generate() -> TestingResult<Self> {
249 RandomBool::new().generate()
250 }
251}
252
253#[derive(Debug, Clone)]
255pub struct RandomChoice<T> {
256 pub items: Vec<T>,
258}
259
260impl<T: Clone> RandomChoice<T> {
261 pub fn new(items: Vec<T>) -> Self {
263 Self { items }
264 }
265
266 pub fn generate(&self) -> TestingResult<T> {
268 let mut rng = rand::rng();
269 self.items
270 .choose(&mut rng)
271 .cloned()
272 .ok_or_else(|| WaeError::new(WaeErrorKind::FixtureError { reason: "No items to choose from".to_string() }))
273 }
274}
275
276#[derive(Debug, Clone)]
278pub struct RandomDateTime {
279 pub start_timestamp: i64,
281 pub end_timestamp: i64,
283}
284
285impl Default for RandomDateTime {
286 fn default() -> Self {
287 Self { start_timestamp: 0, end_timestamp: 4102444800 }
288 }
289}
290
291impl RandomDateTime {
292 pub fn new() -> Self {
294 Self::default()
295 }
296
297 pub fn range(mut self, start: i64, end: i64) -> Self {
299 self.start_timestamp = start;
300 self.end_timestamp = end;
301 self
302 }
303
304 pub fn generate_timestamp(&self) -> TestingResult<i64> {
306 let mut rng = rand::rng();
307 Ok(rng.random_range(self.start_timestamp..=self.end_timestamp))
308 }
309}
310
311pub struct FixtureGenerator;
313
314impl FixtureGenerator {
315 pub fn strings(count: usize, length: usize) -> TestingResult<Vec<String>> {
317 (0..count).map(|_| RandomString::new().length(length).generate()).collect()
318 }
319
320 pub fn emails(count: usize, domain: &str) -> TestingResult<Vec<String>> {
322 (0..count).map(|_| RandomEmail::new().domain(domain).generate()).collect()
323 }
324
325 pub fn uuids(count: usize) -> TestingResult<Vec<uuid::Uuid>> {
327 (0..count).map(|_| RandomUuid::new().generate()).collect()
328 }
329
330 pub fn i32_numbers(count: usize, min: i32, max: i32) -> TestingResult<Vec<i32>> {
332 let mut rng = rand::rng();
333 (0..count).map(|_| Ok(rng.random_range(min..=max))).collect()
334 }
335
336 pub fn u64_numbers(count: usize, min: u64, max: u64) -> TestingResult<Vec<u64>> {
338 let mut rng = rand::rng();
339 (0..count).map(|_| Ok(rng.random_range(min..=max))).collect()
340 }
341}