karbon_framework/util/
random.rs1use rand::RngExt;
2
3pub struct RandomHelper;
5
6impl RandomHelper {
7 pub fn token(byte_length: usize) -> String {
10 let mut rng = rand::rng();
11 let bytes: Vec<u8> = (0..byte_length).map(|_| rng.random()).collect();
12 bytes.iter().map(|b| format!("{:02x}", b)).collect()
13 }
14
15 pub fn alphanumeric(length: usize) -> String {
17 const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
18 let mut rng = rand::rng();
19 (0..length)
20 .map(|_| {
21 let idx = rng.random_range(0..CHARSET.len());
22 CHARSET[idx] as char
23 })
24 .collect()
25 }
26
27 pub fn otp(length: usize) -> String {
30 let mut rng = rand::rng();
31 (0..length)
32 .map(|_| {
33 let digit: u8 = rng.random_range(0..10);
34 char::from(b'0' + digit)
35 })
36 .collect()
37 }
38
39 pub fn int(min: i64, max: i64) -> i64 {
41 let mut rng = rand::rng();
42 rng.random_range(min..=max)
43 }
44
45 pub fn pick<'a, T>(items: &'a [T]) -> Option<&'a T> {
47 if items.is_empty() {
48 return None;
49 }
50 let mut rng = rand::rng();
51 let idx = rng.random_range(0..items.len());
52 Some(&items[idx])
53 }
54
55 pub fn shuffle<T>(mut items: Vec<T>) -> Vec<T> {
57 let mut rng = rand::rng();
58 let len = items.len();
59 for i in (1..len).rev() {
60 let j = rng.random_range(0..=i);
61 items.swap(i, j);
62 }
63 items
64 }
65
66 pub fn uuid() -> String {
68 uuid::Uuid::new_v4().to_string()
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn test_token_length() {
78 let t = RandomHelper::token(16);
79 assert_eq!(t.len(), 32); assert!(t.chars().all(|c| c.is_ascii_hexdigit()));
81 }
82
83 #[test]
84 fn test_token_uniqueness() {
85 let a = RandomHelper::token(16);
86 let b = RandomHelper::token(16);
87 assert_ne!(a, b);
88 }
89
90 #[test]
91 fn test_alphanumeric_length() {
92 let s = RandomHelper::alphanumeric(20);
93 assert_eq!(s.len(), 20);
94 assert!(s.chars().all(|c| c.is_alphanumeric()));
95 }
96
97 #[test]
98 fn test_otp_length_and_digits() {
99 let code = RandomHelper::otp(6);
100 assert_eq!(code.len(), 6);
101 assert!(code.chars().all(|c| c.is_ascii_digit()));
102 }
103
104 #[test]
105 fn test_otp_4_digits() {
106 let code = RandomHelper::otp(4);
107 assert_eq!(code.len(), 4);
108 }
109
110 #[test]
111 fn test_int_range() {
112 for _ in 0..100 {
113 let n = RandomHelper::int(1, 10);
114 assert!((1..=10).contains(&n));
115 }
116 }
117
118 #[test]
119 fn test_pick() {
120 let items = vec!["a", "b", "c"];
121 let picked = RandomHelper::pick(&items);
122 assert!(picked.is_some());
123 assert!(items.contains(picked.unwrap()));
124 }
125
126 #[test]
127 fn test_pick_empty() {
128 let items: Vec<i32> = vec![];
129 assert!(RandomHelper::pick(&items).is_none());
130 }
131
132 #[test]
133 fn test_shuffle_preserves_elements() {
134 let items = vec![1, 2, 3, 4, 5];
135 let shuffled = RandomHelper::shuffle(items.clone());
136 assert_eq!(shuffled.len(), items.len());
137 for item in &items {
138 assert!(shuffled.contains(item));
139 }
140 }
141
142 #[test]
143 fn test_uuid_format() {
144 let id = RandomHelper::uuid();
145 assert_eq!(id.len(), 36);
146 assert_eq!(id.chars().filter(|&c| c == '-').count(), 4);
147 }
148}