Skip to main content

tuwunel_core/utils/
rand.rs

1use std::{
2	ops::Range,
3	time::{Duration, SystemTime},
4};
5
6use arrayvec::ArrayString;
7use rand::{RngExt, rng, seq::SliceRandom};
8use ruma::OwnedEventId;
9
10pub fn shuffle<T>(vec: &mut [T]) {
11	let mut rng = rng();
12	vec.shuffle(&mut rng);
13}
14
15/// A uniform random index into `0..len`, or `0` when `len` is `0`.
16#[must_use]
17pub fn index(len: usize) -> usize {
18	match len {
19		| 0 => 0,
20		| len => rng().random_range(0..len),
21	}
22}
23
24pub fn string(length: usize) -> String {
25	rng()
26		.sample_iter(&rand::distr::Alphanumeric)
27		.take(length)
28		.map(char::from)
29		.collect()
30}
31
32/// A random string of `length` characters drawn uniformly from `charset`.
33#[must_use]
34pub fn string_from(charset: &[u8], length: usize) -> String {
35	let mut rng = rng();
36	(0..length)
37		.map(|_| char::from(charset[rng.random_range(0..charset.len())]))
38		.collect()
39}
40
41#[inline]
42pub fn string_array<const LENGTH: usize>() -> ArrayString<LENGTH> {
43	let mut ret = ArrayString::<LENGTH>::new();
44	rng()
45		.sample_iter(&rand::distr::Alphanumeric)
46		.take(LENGTH)
47		.map(char::from)
48		.for_each(|c| ret.push(c));
49
50	ret
51}
52
53#[must_use]
54pub fn event_id() -> OwnedEventId {
55	use base64::{
56		Engine,
57		alphabet::URL_SAFE,
58		engine::{GeneralPurpose, general_purpose::NO_PAD},
59	};
60
61	let mut binary: [u8; 32] = [0; _];
62	rand::fill(&mut binary);
63
64	let mut encoded: [u8; 43] = [0; _];
65	GeneralPurpose::new(&URL_SAFE, NO_PAD)
66		.encode_slice(binary, &mut encoded)
67		.expect("Failed to encode binary to base64");
68
69	let event_id: &str = str::from_utf8(&encoded)
70		.expect("Failed to convert array of base64 bytes to valid utf8 str");
71
72	OwnedEventId::from_parts('$', event_id, None)
73		.expect("Failed to generate valid random event_id")
74}
75
76#[must_use]
77pub fn truncate_string(mut str: String, range: Range<u64>) -> String {
78	let len = rng()
79		.random_range(range)
80		.try_into()
81		.unwrap_or(usize::MAX);
82
83	if let Some((i, _)) = str.char_indices().nth(len) {
84		str.truncate(i);
85	}
86
87	str
88}
89
90#[inline]
91#[must_use]
92pub fn truncate_str(str: &str, range: Range<u64>) -> &str {
93	let len = rng()
94		.random_range(range)
95		.try_into()
96		.unwrap_or(usize::MAX);
97
98	str.char_indices()
99		.nth(len)
100		.map(|(i, _)| str.split_at(i).0)
101		.unwrap_or(str)
102}
103
104#[inline]
105#[must_use]
106pub fn time_from_now_secs(range: Range<u64>) -> SystemTime {
107	SystemTime::now()
108		.checked_add(secs(range))
109		.expect("range does not overflow SystemTime")
110}
111
112#[must_use]
113pub fn secs(range: Range<u64>) -> Duration {
114	let mut rng = rng();
115	Duration::from_secs(rng.random_range(range))
116}