pub trait Alphabet {
const SYMBOL_LIST: &'static [u8];
}
pub(crate) trait AlphabetExt {
const VALID_SYMBOL_LIST: &'static [u8];
const VALID_SYMBOL_MAP: [bool; 128];
}
impl<A: Alphabet> AlphabetExt for A {
const VALID_SYMBOL_LIST: &'static [u8] = {
assert_all_ascii(A::SYMBOL_LIST);
A::SYMBOL_LIST
};
const VALID_SYMBOL_MAP: [bool; 128] = {
let mut symbols_map = [false; 128];
let mut i = 0;
while i < A::VALID_SYMBOL_LIST.len() {
symbols_map[A::VALID_SYMBOL_LIST[i] as usize] = true;
i += 1;
}
symbols_map
};
}
const fn assert_all_ascii(s: &[u8]) {
let mut i = 0;
while i < s.len() {
assert!(s[i].is_ascii(), "found non-ascii symbol in alphabet");
i += 1;
}
}
macro_rules! define_and_impl_alphabet {
($name:ident, $symbols:expr, $description:expr $(,)?) => {
#[doc = concat!(" ", $description, "
# Example
```rust
use nid::{alphabet::", stringify!($name), ", Nanoid};
let id: Nanoid<21, ", stringify!($name), "> = Nanoid::new();
```")]
#[derive(Debug)]
pub struct $name;
impl Alphabet for $name {
const SYMBOL_LIST: &'static [u8] = $symbols;
}
};
}
define_and_impl_alphabet!(
Base64UrlAlphabet,
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-",
"Alphabet with `A-Za-z0-9_-` symbols. This is the default alphabet used in Nano ID.",
);
define_and_impl_alphabet!(
Base62Alphabet,
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
"Alphabet with `A-Za-z0-9` symbols.",
);
define_and_impl_alphabet!(
Base58Alphabet,
b"ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz123456789",
"Alphabet with `A-Za-z0-9` symbols excluding `0OlI`.",
);
define_and_impl_alphabet!(
Base36Alphabet,
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
"Alphabet with `A-Z0-9` symbols.",
);
define_and_impl_alphabet!(
Base36LowercaseAlphabet,
b"abcdefghijklmnopqrstuvwxyz0123456789",
"Alphabet with `a-z0-9` symbols.",
);
define_and_impl_alphabet!(
Base32Alphabet,
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
"Alphabet with `A-Z2-7` symbols.",
);
define_and_impl_alphabet!(
Base32LowercaseAlphabet,
b"abcdefghijklmnopqrstuvwxyz234567",
"Alphabet with `a-z2-7` symbols.",
);
define_and_impl_alphabet!(
Base16Alphabet,
b"ABCDEF0123456789",
"Alphabet with `A-F0-9` symbols.",
);
define_and_impl_alphabet!(
Base16LowercaseAlphabet,
b"abcdef0123456789",
"Alphabet with `a-f0-9` symbols.",
);
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn test_alphabet_len() {
assert_eq!(Base64UrlAlphabet::SYMBOL_LIST.len(), 64);
assert_eq!(Base62Alphabet::SYMBOL_LIST.len(), 62);
assert_eq!(Base58Alphabet::SYMBOL_LIST.len(), 58);
assert_eq!(Base36Alphabet::SYMBOL_LIST.len(), 36);
assert_eq!(Base36LowercaseAlphabet::SYMBOL_LIST.len(), 36);
assert_eq!(Base32Alphabet::SYMBOL_LIST.len(), 32);
assert_eq!(Base32LowercaseAlphabet::SYMBOL_LIST.len(), 32);
assert_eq!(Base16Alphabet::SYMBOL_LIST.len(), 16);
assert_eq!(Base16LowercaseAlphabet::SYMBOL_LIST.len(), 16);
}
#[test]
fn test_alphabet_symbol_map() {
assert!(Base64UrlAlphabet::VALID_SYMBOL_MAP[b'A' as usize]);
assert!(!Base64UrlAlphabet::VALID_SYMBOL_MAP[b':' as usize]);
assert!(Base62Alphabet::VALID_SYMBOL_MAP[b'A' as usize]);
assert!(!Base62Alphabet::VALID_SYMBOL_MAP[b'-' as usize]);
assert!(Base58Alphabet::VALID_SYMBOL_MAP[b'A' as usize]);
assert!(!Base58Alphabet::VALID_SYMBOL_MAP[b'0' as usize]);
assert!(Base36Alphabet::VALID_SYMBOL_MAP[b'A' as usize]);
assert!(!Base36Alphabet::VALID_SYMBOL_MAP[b'a' as usize]);
assert!(Base36LowercaseAlphabet::VALID_SYMBOL_MAP[b'a' as usize]);
assert!(!Base36LowercaseAlphabet::VALID_SYMBOL_MAP[b'A' as usize]);
assert!(Base32Alphabet::VALID_SYMBOL_MAP[b'A' as usize]);
assert!(!Base32Alphabet::VALID_SYMBOL_MAP[b'8' as usize]);
assert!(Base32LowercaseAlphabet::VALID_SYMBOL_MAP[b'a' as usize]);
assert!(!Base32LowercaseAlphabet::VALID_SYMBOL_MAP[b'8' as usize]);
assert!(Base16Alphabet::VALID_SYMBOL_MAP[b'A' as usize]);
assert!(!Base16Alphabet::VALID_SYMBOL_MAP[b'Z' as usize]);
assert!(Base16LowercaseAlphabet::VALID_SYMBOL_MAP[b'a' as usize]);
assert!(!Base16LowercaseAlphabet::VALID_SYMBOL_MAP[b'z' as usize]);
}
}