use rand::RngCore;
const ALPHABET: &[u8; 62] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
pub const DEFAULT_OBJECT_ID_SIZE: usize = 10;
pub fn random_string(size: usize) -> String {
let mut rng = rand::thread_rng();
let mut out = String::with_capacity(size);
let mut buf = [0u8; 64];
let mut have = 0usize;
let mut pos = 0usize;
while out.len() < size {
if pos == have {
rng.fill_bytes(&mut buf);
have = buf.len();
pos = 0;
}
let b = buf[pos];
pos += 1;
if b < 248 {
out.push(ALPHABET[(b % 62) as usize] as char);
}
}
out
}
pub fn new_object_id() -> String {
random_string(DEFAULT_OBJECT_ID_SIZE)
}
pub fn is_valid_auto_object_id(s: &str) -> bool {
!s.is_empty() && s.bytes().all(|b| b.is_ascii_alphanumeric())
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn default_shape() {
let id = new_object_id();
assert_eq!(id.len(), 10);
assert!(
is_valid_auto_object_id(&id),
"{id} failed the upstream regex shape"
);
}
#[test]
fn alphabet_is_exactly_the_upstream_62() {
let set: HashSet<u8> = ALPHABET.iter().copied().collect();
assert_eq!(set.len(), 62, "alphabet has a duplicate");
for b in b'A'..=b'Z' {
assert!(set.contains(&b));
}
for b in b'a'..=b'z' {
assert!(set.contains(&b));
}
for b in b'0'..=b'9' {
assert!(set.contains(&b));
}
}
#[test]
fn validator_matches_the_regex_semantics() {
assert!(is_valid_auto_object_id("aA0"));
assert!(is_valid_auto_object_id("a"));
assert!(is_valid_auto_object_id(&"a".repeat(500)));
assert!(!is_valid_auto_object_id(""));
assert!(!is_valid_auto_object_id("has-dash"));
assert!(!is_valid_auto_object_id("has space"));
assert!(!is_valid_auto_object_id("ünïcode"));
}
#[test]
fn covers_the_whole_alphabet() {
let mut seen: HashSet<char> = HashSet::new();
for _ in 0..2000 {
seen.extend(random_string(32).chars());
}
assert_eq!(
seen.len(),
62,
"some alphabet characters were never produced"
);
}
#[test]
fn ids_are_not_repeating() {
let ids: HashSet<String> = (0..1000).map(|_| new_object_id()).collect();
assert_eq!(ids.len(), 1000, "collision in 1000 draws of a 62^10 space");
}
}