#![doc(
html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
html_root_url = "https://docs.rs/rustq-nanoid"
)]
#[cfg(feature = "smartstring")]
use smartstring::alias::String;
pub mod alphabet;
pub mod rngs;
use std::sync::Mutex;
const POOL_SIZE_MULTIPLIER: usize = 128;
const DEFAULT_SIZE: usize = 21;
const POOL_SIZE: usize = DEFAULT_SIZE * POOL_SIZE_MULTIPLIER;
lazy_static! {
static ref POOL: Mutex<[u8; POOL_SIZE]> = Mutex::new([0; POOL_SIZE]);
static ref POOL_OFFSET: Mutex<usize> = Mutex::new(POOL_SIZE);
}
pub fn format(random: fn(usize) -> Vec<u8>, alphabet: &[char], size: usize) -> String {
assert!(
alphabet.len() <= u8::max_value() as usize,
"The alphabet cannot be longer than a `u8` (to comply with the `random` function)"
);
assert!(size <= POOL_SIZE, "The size should smaller than pool size");
let bytes = &mut POOL.lock().unwrap();
let mask = alphabet.len().next_power_of_two() - 1;
debug_assert!(alphabet.len() <= mask + 1);
let mut pointer = *POOL_OFFSET.lock().unwrap();
let mut id = String::with_capacity(size);
while id.len() < size {
if pointer == POOL_SIZE {
let buf = random(POOL_SIZE);
for i in 0..POOL_SIZE {
bytes[i] = buf[i];
}
pointer = 0;
}
let byte = bytes[pointer] as usize & mask;
if alphabet.len() > byte {
id.push(alphabet[byte]);
}
pointer += 1;
}
*POOL_OFFSET.lock().unwrap() = pointer;
id
}
#[cfg(test)]
mod test_format {
use super::*;
#[test]
#[should_panic]
fn bad_alphabet() {
let alphabet: Vec<char> = (0..32_u8).cycle().map(|i| i as char).take(1000).collect();
nanoid!(21, &alphabet);
}
#[test]
fn non_power_2() {
let id: String = nanoid!(42, &alphabet::SAFE[0..62]);
assert_eq!(id.len(), 42);
}
}
#[macro_export]
macro_rules! nanoid {
() => {
$crate::format($crate::rngs::default, &$crate::alphabet::SAFE, 21)
};
($size:expr) => {
$crate::format($crate::rngs::default, &$crate::alphabet::SAFE, $size)
};
($size:expr, $alphabet:expr) => {
$crate::format($crate::rngs::default, $alphabet, $size)
};
($size:expr, $alphabet:expr, $random:expr) => {
$crate::format($random, $alphabet, $size)
};
}
#[cfg(test)]
mod test_macros {
use super::*;
#[test]
fn simple() {
let id: String = nanoid!();
assert_eq!(id.len(), 21);
}
#[test]
fn generate() {
let id: String = nanoid!(42);
assert_eq!(id.len(), 42);
}
#[test]
fn custom() {
let id: String = nanoid!(42, &alphabet::SAFE);
assert_eq!(id.len(), 42);
}
#[test]
fn complex() {
let id: String = nanoid!(4, &alphabet::SAFE, rngs::default);
assert_eq!(id.len(), 4);
}
#[test]
fn simple_expression() {
let id: String = nanoid!(42 / 2);
assert_eq!(id.len(), 21);
}
}
#[cfg(doctest)]
doc_comment::doctest!("../README.md");
#[macro_use]
extern crate lazy_static;