1#[macro_export]
4macro_rules! gen {
5 ($mod:tt, $len:tt, $alphabet:tt) => {
6 #[doc = concat!(" Nanoid with alphabet table `", stringify!($alphabet), "`")]
7 mod $mod {
8 pub const MASK: usize = ($len as usize).next_power_of_two() - 1;
9 pub const ALPHABET: &'static [u8; $len] = $alphabet;
10 }
11
12 #[doc = concat!(" Nanoid with ", stringify!($mod))]
13 #[must_use]
14 pub fn $mod<const N: usize>() -> String {
15 let mut bytes = vec![0u8; 8 * N / 5];
16 let mut id = String::with_capacity(N);
17
18 loop {
19 ::getrandom::getrandom(&mut bytes)
20 .unwrap_or_else(|err| panic!("could not retreive random bytes: {err}"));
21
22 for byte in &bytes {
23 let idx = *byte as usize & $mod::MASK;
24 if idx < $len {
25 id.push($mod::ALPHABET[idx] as char)
26 }
27 if id.len() == N {
28 return id;
29 }
30 }
31 }
32 }
33 };
34}
35
36#[cfg(feature = "base58")]
37gen!(
38 base58,
39 58,
40 b"ModueSymbhaswnPr123456789ABCDEFGHNRVfgctiUvzKqYTJkLxpZXjQW"
41);
42
43#[cfg(feature = "base62")]
44gen!(
45 base62,
46 62,
47 b"ModuleSymbhasOwnPr0123456789ABCDEFGHNRVfgctiUvzKqYTJkLxpZXIjQW"
48);
49
50#[cfg(feature = "base64")]
51gen!(
52 base64,
53 64,
54 b"ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW"
55);
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 #[cfg(feature = "base58")]
63 fn generates_base58() {
64 let id = base58::<21>();
65 println!("{}", &id);
66 assert_eq!(id.len(), 21);
67 }
68
69 #[test]
70 #[cfg(feature = "base62")]
71 fn generates_base62() {
72 let id = base62::<21>();
73 println!("{}", &id);
74 assert_eq!(id.len(), 21);
75 }
76
77 #[test]
78 #[cfg(feature = "base64")]
79 fn generates_base64() {
80 let id = base64::<21>();
81 println!("{}", &id);
82 assert_eq!(id.len(), 21);
83 }
84
85 #[test]
86 fn generates_uid() {
87 gen!(
88 uid,
89 64,
90 b"_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
91 );
92
93 let id = uid::<21>();
94 println!("{}", &id);
95 assert_eq!(id.len(), 21);
96 }
97
98 #[test]
99 #[cfg(feature = "base62")]
100 fn symbols() {
101 use std::collections::BTreeMap;
102
103 let mut counts = BTreeMap::new();
104
105 for _ in 0..1_000_000 {
106 let id = base62::<10>();
107 for c in id.chars() {
108 *counts.entry(c).or_insert(0) += 1;
109 }
110 }
111
112 println!("{} symbols generated", counts.len());
113 for (c, count) in &counts {
114 println!("{}: {}", c, count);
115 }
116
117 assert_eq!(counts.len(), 62);
118 }
119}