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}