#![forbid(unsafe_code)]
#![forbid(missing_docs)]
#![doc = include_str!("../README.md")]
mod consts;
pub fn encode(mut num: usize) -> String {
if num < 64 {
return consts::CHARS[num].into();
}
let estimated_length = num.next_power_of_two().trailing_zeros().max(1);
let mut b64 = String::with_capacity(estimated_length as usize);
while num > 0 {
b64.insert(0, consts::CHARS[num & 63]);
num >>= 6;
}
b64
}
#[cfg(feature = "rand")]
pub fn generate(range: std::ops::Range<usize>) -> String {
use rand::Rng;
let mut rng = rand::thread_rng();
let num = rng.gen_range(range);
encode(num)
}
pub fn max_value_for_letters(n: u32) -> Option<usize> {
64usize.checked_pow(n).map(|x| x - 1)
}
#[cfg(test)]
mod tests {
use crate::{consts::CHARS, encode};
#[test]
fn test_to_b64() {
for i in 0..=9 {
assert_eq!(encode(i), i.to_string());
}
for (i, c) in CHARS.iter().enumerate().skip(9) {
assert_eq!(encode(i), c.to_string());
assert_eq!(encode(i).len(), 1);
}
assert_eq!(encode(64), "10");
assert_eq!(encode(65), "11");
assert_eq!(encode(64 * 2), "20");
assert_eq!(encode(64 * 2 + 1), "21");
}
#[test]
#[cfg(feature = "rand")]
fn generate() {
for _ in 0..100 {
let id = crate::generate(0..100);
assert!(id.len() <= 2);
}
}
}