Skip to main content

generate_random/
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::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#[cfg(feature = "derive")]
34pub use generate_random_macro::GenerateRandom;
35
36pub use rand::{Rng, RngExt};
37
38/// Generate a random value for a type.
39pub fn generate<T, R>(rng: &mut R) -> T
40where
41    T: GenerateRandom,
42    R: Rng + ?Sized,
43{
44    GenerateRandom::generate_random(rng)
45}
46
47/// Enable randomly generating values of a type.
48///
49/// This trait can be implemented using the derive
50/// macro of the same name: [`macro@GenerateRandom`].
51pub trait GenerateRandom {
52    /// Create a new random value of this type.
53    fn generate_random<R: Rng + ?Sized>(rng: &mut R) -> Self;
54}
55
56macro_rules! impl_generate_random {
57    ( $( $t:ty, )+ ) => {
58        $(
59            impl GenerateRandom for $t {
60                fn generate_random<R: Rng + ?Sized>(rng: &mut R) -> Self {
61                    rand::RngExt::random(rng)
62                }
63            }
64        )+
65    }
66}
67
68impl_generate_random! {
69    bool,
70    char,
71    u8,
72    i8,
73    u16,
74    i16,
75    u32,
76    i32,
77    u64,
78    i64,
79    u128,
80    i128,
81    f32,
82    f64,
83}
84
85impl GenerateRandom for usize {
86    fn generate_random<R: Rng + ?Sized>(rng: &mut R) -> Self {
87        cfg_select! {
88            target_pointer_width = "16" => u16::generate_random(rng) as usize,
89            target_pointer_width = "32" => u32::generate_random(rng) as usize,
90            target_pointer_width = "64" => u64::generate_random(rng) as usize,
91        }
92    }
93}
94
95impl GenerateRandom for isize {
96    fn generate_random<R: Rng + ?Sized>(rng: &mut R) -> Self {
97        cfg_select! {
98            target_pointer_width = "16" => i16::generate_random(rng) as isize,
99            target_pointer_width = "32" => i32::generate_random(rng) as isize,
100            target_pointer_width = "64" => i64::generate_random(rng) as isize,
101        }
102    }
103}
104
105impl<T: GenerateRandom> GenerateRandom for Option<T> {
106    fn generate_random<R: Rng + ?Sized>(rng: &mut R) -> Self {
107        if bool::generate_random(rng) {
108            Some(T::generate_random(rng))
109        } else {
110            None
111        }
112    }
113}
114
115macro_rules! impl_generate_random_tuple {
116    ( $t0:ident $( $t:ident )* ) => {
117        impl< $t0, $( $t, )* > GenerateRandom for ( $t0, $( $t, )* )
118        where
119            $t0: GenerateRandom,
120            $(
121                $t: GenerateRandom,
122            )*
123        {
124            fn generate_random<R: Rng + ?Sized>(rng: &mut R) -> Self {
125                (
126                    $t0::generate_random(rng),
127                    $(
128                        $t::generate_random(rng),
129                    )*
130                )
131            }
132        }
133        impl_generate_random_tuple!( $( $t )* );
134    };
135    () => {
136        impl GenerateRandom for () {
137            fn generate_random<R: Rng + ?Sized>(_rng: &mut R) -> Self {
138                ()
139            }
140        }
141    }
142}
143
144impl_generate_random_tuple!(A B C D E F G H I J K L);
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149
150    fn rng() -> impl rand::Rng {
151        use rand::SeedableRng;
152        rand_chacha::ChaCha8Rng::from(rand_chacha::ChaCha8Core::from_seed([37; 32]))
153    }
154
155    #[test]
156    fn test_u8() {
157        let mut rng = rng();
158        assert_eq!(u8::generate_random(&mut rng), 55);
159    }
160}