use rand::Rng;
use std::collections::HashSet;
pub const ALPHABET: &[u8] = b"ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
pub const LENGTH: usize = 8;
const MAX_ATTEMPTS: usize = 500;
pub fn random() -> String {
let mut rng = rand::thread_rng();
(0..LENGTH)
.map(|_| ALPHABET[rng.gen_range(0..ALPHABET.len())] as char)
.collect()
}
pub fn generate_unique(existing: &HashSet<String>) -> Result<String, &'static str> {
for _ in 0..MAX_ATTEMPTS {
let candidate = random();
if !existing.contains(&candidate) {
return Ok(candidate);
}
}
Err("failed to generate unique pairing code after 500 attempts")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alphabet_excludes_ambiguous_chars() {
for c in [b'0', b'O', b'1', b'I'] {
assert!(
!ALPHABET.contains(&c),
"ambiguous char {} in alphabet",
c as char
);
}
}
#[test]
fn generated_code_uses_only_alphabet() {
for _ in 0..50 {
let c = random();
assert_eq!(c.len(), LENGTH);
assert!(c.chars().all(|ch| ALPHABET.contains(&(ch as u8))));
}
}
#[test]
fn generate_unique_avoids_collision() {
let mut existing = HashSet::new();
for _ in 0..200 {
let c = generate_unique(&existing).unwrap();
assert!(!existing.contains(&c));
existing.insert(c);
}
}
}