]> git.lizzy.rs Git - generate-random.git/blob - lib/src/lib.rs
bd49e6be21f52d9f76ef9a9791582ab3e634f453
[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 /// Enable randomly generating values of an enum
45 /// with a predefined variant
46 ///
47 /// This trait is automatically implemented for enums
48 /// by the [`macro@GenerateRandom`] macro
49 pub trait GenerateRandomVariant {
50     /// Return number of variants
51     fn num_variants() -> usize;
52
53     /// Return name of variant with index
54     fn variant_name(variant: usize) -> &'static str;
55
56     /// Create a randomly generated value with a predefied variant
57     fn generate_random_variant<R: rand::Rng + ?Sized>(rng: &mut R, variant: usize) -> Self;
58 }
59
60 macro_rules! impl_generate_random {
61         ( $( $t:ty, )+ ) => {
62                 $(
63                         impl GenerateRandom for $t {
64                                 fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
65                                         rng.gen()
66                                 }
67                         }
68                 )+
69         }
70 }
71
72 impl_generate_random! {
73     bool,
74     char,
75     u8,
76     i8,
77     u16,
78     i16,
79     u32,
80     i32,
81     u64,
82     i64,
83     u128,
84     i128,
85     usize,
86     isize,
87     f32,
88     f64,
89 }
90
91 impl<T: GenerateRandom> GenerateRandom for Option<T> {
92     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
93         if bool::generate_random(rng) {
94             Some(T::generate_random(rng))
95         } else {
96             None
97         }
98     }
99 }
100
101 impl<T: GenerateRandom, const N: usize> GenerateRandom for [T; N] {
102     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
103         core::array::from_fn(|_| T::generate_random(rng))
104     }
105 }
106
107 impl GenerateRandom for String {
108     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
109         use rand::distributions::{Alphanumeric, DistString};
110
111         let len = rng.gen_range(0..32);
112         Alphanumeric.sample_string(rng, len)
113     }
114 }
115
116 impl<T: GenerateRandom> GenerateRandom for Vec<T> {
117     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
118         let len = rng.gen_range(0..8);
119         (0..len).map(|_| T::generate_random(rng)).collect()
120     }
121 }
122
123 impl<T> GenerateRandom for std::collections::HashSet<T>
124 where
125     T: GenerateRandom + std::cmp::Eq + std::hash::Hash,
126 {
127     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
128         let len = rng.gen_range(0..8);
129         (0..len).map(|_| T::generate_random(rng)).collect()
130     }
131 }
132
133 impl<K, V> GenerateRandom for std::collections::HashMap<K, V>
134 where
135     K: GenerateRandom + std::cmp::Eq + std::hash::Hash,
136     V: GenerateRandom,
137 {
138     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
139         let len = rng.gen_range(0..8);
140         (0..len)
141             .map(|_| (K::generate_random(rng), V::generate_random(rng)))
142             .collect()
143     }
144 }
145
146 impl<T: GenerateRandom> GenerateRandom for Box<T> {
147     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
148         Box::new(T::generate_random(rng))
149     }
150 }
151
152 impl<T: GenerateRandom> GenerateRandom for std::ops::Range<T> {
153     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
154         T::generate_random(rng)..T::generate_random(rng)
155     }
156 }
157
158 impl<T: GenerateRandom> GenerateRandom for std::ops::RangeFrom<T> {
159     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
160         T::generate_random(rng)..
161     }
162 }
163
164 impl GenerateRandom for std::ops::RangeFull {
165     fn generate_random<R: rand::Rng + ?Sized>(_rng: &mut R) -> Self {
166         ..
167     }
168 }
169
170 impl<T: GenerateRandom> GenerateRandom for std::ops::RangeInclusive<T> {
171     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
172         T::generate_random(rng)..=T::generate_random(rng)
173     }
174 }
175
176 impl<T: GenerateRandom> GenerateRandom for std::ops::RangeTo<T> {
177     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
178         ..T::generate_random(rng)
179     }
180 }
181 impl<T: GenerateRandom> GenerateRandom for std::ops::RangeToInclusive<T> {
182     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
183         ..=T::generate_random(rng)
184     }
185 }
186
187 #[cfg(feature = "enumset")]
188 impl<T: enumset::EnumSetType + GenerateRandom> GenerateRandom for enumset::EnumSet<T> {
189     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
190         let max = enumset::EnumSet::<T>::variant_count() * 2;
191         let len = rng.gen_range(0..max);
192
193         (0..len).map(|_| T::generate_random(rng)).collect()
194     }
195 }
196
197 macro_rules! impl_generate_random_tuple {
198         ( $t0:ident $( $t:ident )* ) => {
199                 impl< $t0, $( $t, )* > GenerateRandom for ( $t0, $( $t, )* )
200                 where
201                         $t0: GenerateRandom,
202                         $(
203                                 $t: GenerateRandom,
204                         )*
205                 {
206                         fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
207                                 (
208                                         $t0::generate_random(rng),
209                                         $(
210                                                 $t::generate_random(rng),
211                                         )*
212                                 )
213                         }
214                 }
215                 impl_generate_random_tuple!( $( $t )* );
216         };
217         () => {
218                 #[allow(clippy::unused_unit)]
219                 impl GenerateRandom for () {
220                         fn generate_random<R: rand::Rng + ?Sized>(_rng: &mut R) -> Self {
221                                 ()
222                         }
223                 }
224         }
225 }
226
227 impl_generate_random_tuple!(A B C D E F G H I J K L);
228
229 #[cfg(test)]
230 mod tests {
231     use super::*;
232
233     fn rng() -> impl rand::Rng {
234         use rand::SeedableRng;
235         rand_chacha::ChaCha8Rng::from(rand_chacha::ChaCha8Core::from_seed([37; 32]))
236     }
237
238     #[test]
239     fn test_u8() {
240         let mut rng = rng();
241         assert_eq!(u8::generate_random(&mut rng), 55);
242     }
243 }