use rand::Rng;
pub(crate) const RANDOM_STRING_ALPHABET: &[u8] = b"0123456789qwertyuioplkjhgfdsazxcvbnm";
pub(crate) const BASE36_ALPHABET: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";
pub fn random_int(min: i64, max: i64) -> i64 {
rand::thread_rng().gen_range(min..max)
}
pub fn random_string(len: usize) -> String {
pick_chars(RANDOM_STRING_ALPHABET, len)
}
pub(crate) fn generate_random_string(len: usize) -> String {
pick_chars(BASE36_ALPHABET, len)
}
fn pick_chars(alphabet: &[u8], len: usize) -> String {
let mut rng = rand::thread_rng();
(0..len)
.map(|_| {
let idx = rng.gen_range(0..alphabet.len());
alphabet[idx] as char
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn random_int_is_in_range() {
for _ in 0..1000 {
let n = random_int(5, 10);
assert!((5..10).contains(&n), "{n} out of range");
}
}
#[test]
fn random_int_negative_range() {
for _ in 0..200 {
let n = random_int(-10, -1);
assert!((-10..-1).contains(&n));
}
}
#[test]
fn random_string_has_expected_length() {
for len in [0_usize, 1, 8, 32, 256] {
assert_eq!(random_string(len).len(), len);
}
}
#[test]
fn random_string_uses_node_alphabet() {
let s = random_string(500);
for c in s.chars() {
assert!(
RANDOM_STRING_ALPHABET.contains(&(c as u8)),
"char {c:?} not in alphabet"
);
}
}
#[test]
fn generate_random_string_is_base36() {
let s = generate_random_string(500);
for c in s.chars() {
assert!(
c.is_ascii_digit() || c.is_ascii_lowercase(),
"char {c:?} is not base36"
);
}
}
}