const CROCKFORD: &[u8; 32] = b"0123456789abcdefghjkmnpqrstvwxyz";
pub fn mint(millis: u64, seq: u64) -> String {
let mut id = String::with_capacity(23);
for shift in (0..10).rev() {
id.push(CROCKFORD[((millis >> (shift * 5)) & 31) as usize] as char);
}
for shift in (0..13).rev() {
id.push(CROCKFORD[((seq >> (shift * 5)) & 31) as usize] as char);
}
id
}
pub fn seq_of(id: &str) -> Option<u64> {
if id.len() != 23 || !id.bytes().all(|b| CROCKFORD.contains(&b)) {
return None;
}
let mut seq: u64 = 0;
for (i, b) in id.bytes().skip(10).enumerate() {
let digit = CROCKFORD.iter().position(|c| *c == b)? as u64;
if i == 0 && digit > 15 {
return None;
}
seq = (seq << 5) | digit;
}
Some(seq)
}
pub fn is_id(id: &str) -> bool {
seq_of(id).is_some()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extremes_round_trip() {
for seq in [0, 1, u64::MAX, u64::MAX / 3] {
assert_eq!(seq_of(&mint(0, seq)), Some(seq));
}
assert_eq!(seq_of("0000000000g000000000000"), None);
assert_eq!(seq_of("0000000000f000000000000"), Some(15 << 60));
assert_eq!(seq_of("short"), None);
assert_eq!(seq_of("0000000000000000000000U"), None);
}
}