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}