Skip to main content

moonpool_core/
random.rs

1//! Random number generation provider abstraction.
2//!
3//! This module provides a provider pattern for random number generation,
4//! consistent with other provider abstractions in the simulation framework
5//! like `TimeProvider`, `NetworkProvider`, and `TaskProvider`.
6
7use rand::distr::{Distribution, StandardUniform, uniform::SampleUniform};
8#[cfg(feature = "tokio-providers")]
9use rand::prelude::*;
10#[cfg(feature = "tokio-providers")]
11use std::cell::RefCell;
12use std::ops::Range;
13
14/// Provider trait for random number generation.
15///
16/// This trait abstracts random number generation to enable both
17/// deterministic simulation randomness and real random numbers
18/// in a unified way. Implementations handle the source of randomness
19/// appropriate for their environment.
20pub trait RandomProvider: Clone + Send + Sync + 'static {
21    /// Generate a random value of type T.
22    ///
23    /// The type T must implement the Standard distribution.
24    fn random<T>(&self) -> T
25    where
26        StandardUniform: Distribution<T>;
27
28    /// Generate a random value within a specified range.
29    ///
30    /// The range is exclusive of the upper bound (start..end).
31    fn random_range<T>(&self, range: Range<T>) -> T
32    where
33        T: SampleUniform + PartialOrd;
34
35    /// Generate a random f64 between 0.0 and 1.0.
36    ///
37    /// This is a convenience method for generating ratios and percentages.
38    fn random_ratio(&self) -> f64;
39
40    /// Generate a random bool with the given probability of being true.
41    ///
42    /// The probability should be between 0.0 and 1.0.
43    fn random_bool(&self, probability: f64) -> bool;
44}
45
46/// Production random provider using thread-local RNG.
47///
48/// Uses `rand::rng()` (thread-local, non-cryptographic) for efficient
49/// random number generation in production environments.
50///
51/// # Example
52///
53/// ```rust
54/// use moonpool_core::{RandomProvider, TokioRandomProvider};
55///
56/// let random = TokioRandomProvider::new();
57/// let value: u64 = random.random();
58/// let in_range = random.random_range(1..100);
59/// ```
60#[cfg(feature = "tokio-providers")]
61#[derive(Clone, Default)]
62pub struct TokioRandomProvider;
63
64#[cfg(feature = "tokio-providers")]
65impl TokioRandomProvider {
66    /// Create a new production random provider.
67    #[must_use]
68    pub fn new() -> Self {
69        Self
70    }
71}
72
73// Thread-local RNG for TokioRandomProvider
74#[cfg(feature = "tokio-providers")]
75thread_local! {
76    static RNG: RefCell<rand::rngs::ThreadRng> = RefCell::new(rand::rng());
77}
78
79#[cfg(feature = "tokio-providers")]
80impl RandomProvider for TokioRandomProvider {
81    fn random<T>(&self) -> T
82    where
83        StandardUniform: Distribution<T>,
84    {
85        RNG.with(|rng| rng.borrow_mut().random())
86    }
87
88    fn random_range<T>(&self, range: Range<T>) -> T
89    where
90        T: SampleUniform + PartialOrd,
91    {
92        RNG.with(|rng| rng.borrow_mut().random_range(range))
93    }
94
95    fn random_ratio(&self) -> f64 {
96        RNG.with(|rng| rng.borrow_mut().random())
97    }
98
99    fn random_bool(&self, probability: f64) -> bool {
100        self.random_ratio() < probability
101    }
102}