]> git.lizzy.rs Git - generate-random.git/blob - lib/src/lib.rs
f68b9f7d725d8db38379103be3cc24a7080db92b
[generate-random.git] / lib / src / lib.rs
1 //! Generate random data.
2 //!
3 //! # Examples
4 //!
5 //! ```
6 //! use generate_random::GenerateRandom;
7 //!
8 //! #[derive(GenerateRandom)]
9 //! enum MyEnum {
10 //!     A,
11 //!     C(bool),
12 //!     B {
13 //!         x: u8,
14 //!     },
15 //!     // Providing a weight allows changing the probabilities.
16 //!     // This variant is now twice as likely to be generated as the others.
17 //!     #[weight(2)]
18 //!     D,
19 //! }
20 //!
21 //! let mut rng = rand::thread_rng();
22 //! let my_value = MyEnum::generate_random(&mut rng);
23 //! ```
24
25 /// This derive macro provides an implementation
26 /// of the [`trait@GenerateRandom`] trait.
27 ///
28 /// Enum variants can be given a `weight` attribute
29 /// to change how often it is generated.
30 /// By default, the weight is `1`.
31 /// The probability of a variants is its weight
32 /// divided by the sum over all variants.
33 pub use generate_random_macro::GenerateRandom;
34
35 /// Enable randomly generating values of a type.
36 ///
37 /// This trait can be implemented using the derive
38 /// macro of the same name: [`macro@GenerateRandom`].
39 pub trait GenerateRandom {
40     /// Create a new random value of this type.
41     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self;
42 }
43
44 macro_rules! impl_generate_random {
45         ( $( $t:ty, )+ ) => {
46                 $(
47                         impl GenerateRandom for $t {
48                                 fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
49                                         rng.gen()
50                                 }
51                         }
52                 )+
53         }
54 }
55
56 impl_generate_random! {
57     bool,
58     char,
59     u8,
60     i8,
61     u16,
62     i16,
63     u32,
64     i32,
65     u64,
66     i64,
67     u128,
68     i128,
69     usize,
70     isize,
71     f32,
72     f64,
73 }
74
75 impl<T: GenerateRandom> GenerateRandom for Option<T> {
76     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
77         if bool::generate_random(rng) {
78             Some(T::generate_random(rng))
79         } else {
80             None
81         }
82     }
83 }
84
85 impl<T: GenerateRandom, const N: usize> GenerateRandom for [T; N] {
86     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
87         use core::mem::MaybeUninit;
88
89         let mut arr: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
90
91         for elem in arr.iter_mut() {
92             *elem = MaybeUninit::new(T::generate_random(rng));
93         }
94
95         let ret = unsafe { std::mem::transmute_copy(&arr) };
96         std::mem::forget(arr);
97
98         ret
99     }
100 }
101
102 impl GenerateRandom for String {
103     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
104         use rand::distributions::{Alphanumeric, DistString};
105
106         let len = rng.gen_range(0..32);
107         Alphanumeric.sample_string(rng, len)
108     }
109 }
110
111 impl<T: GenerateRandom> GenerateRandom for Vec<T> {
112     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
113         let len = rng.gen_range(0..8);
114         (0..len).map(|_| T::generate_random(rng)).collect()
115     }
116 }
117
118 impl<K, V> GenerateRandom for std::collections::HashMap<K, V>
119 where
120     K: GenerateRandom + std::cmp::Eq + std::hash::Hash,
121     V: GenerateRandom,
122 {
123     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
124         let len = rng.gen_range(0..8);
125         (0..len)
126             .map(|_| (K::generate_random(rng), V::generate_random(rng)))
127             .collect()
128     }
129 }
130
131 #[cfg(feature = "enumset")]
132 impl<T: enumset::EnumSetType + GenerateRandom> GenerateRandom for enumset::EnumSet<T> {
133     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
134         let max = enumset::EnumSet::<T>::variant_count() * 2;
135         let len = rng.gen_range(0..max);
136
137         (0..len).map(|_| T::generate_random(rng)).collect()
138     }
139 }
140
141 macro_rules! impl_generate_random_tuple {
142         ( $t0:ident $( $t:ident )* ) => {
143                 impl< $t0, $( $t, )* > GenerateRandom for ( $t0, $( $t, )* )
144                 where
145                         $t0: GenerateRandom,
146                         $(
147                                 $t: GenerateRandom,
148                         )*
149                 {
150                         fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
151                                 (
152                                         $t0::generate_random(rng),
153                                         $(
154                                                 $t::generate_random(rng),
155                                         )*
156                                 )
157                         }
158                 }
159                 impl_generate_random_tuple!( $( $t )* );
160         };
161         () => {
162                 impl GenerateRandom for () {
163                         fn generate_random<R: rand::Rng + ?Sized>(_rng: &mut R) -> Self {
164                                 ()
165                         }
166                 }
167         }
168 }
169
170 impl_generate_random_tuple!(A B C D E F G H I J K L);
171
172 #[cfg(test)]
173 mod tests {
174     use super::*;
175
176     fn rng() -> impl rand::Rng {
177         use rand::SeedableRng;
178         rand_chacha::ChaCha8Rng::from(rand_chacha::ChaCha8Core::from_seed([37; 32]))
179     }
180
181     #[test]
182     fn test_u8() {
183         let mut rng = rng();
184         assert_eq!(u8::generate_random(&mut rng), 55);
185     }
186 }