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