#![cfg_attr(not(feature = "std"), no_std)]
#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
use core::cell::RefCell;
use core::fmt::{self, Write};
#[cfg(all(feature = "alloc", not(feature = "std")))]
extern crate alloc;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::string::String;
pub mod alphabet;
mod std_rand;
pub use alphabet::{Alphabet, HexAlphabet};
use rand::Rng;
#[cfg(feature = "std-rand")]
pub use std_rand::*;
const BUFFER_SIZE: usize = 64;
pub const DEFAULT_SIZE: usize = 21;
#[derive(Clone)]
pub struct Generator<'a, R, const N: usize = 64> {
alphabet: &'a Alphabet<N>,
random: R,
size: usize,
}
impl<'a, R: Rng, const N: usize> Generator<'a, R, N> {
pub fn new(size: usize, alphabet: &'a Alphabet<N>, random: R) -> Self {
Self {
size,
alphabet,
random,
}
}
pub fn size(self, size: usize) -> Self {
Self { size, ..self }
}
pub fn alphabet<const M: usize>(self, alphabet: &Alphabet<M>) -> Generator<'_, R, M> {
Generator {
alphabet,
size: self.size,
random: self.random,
}
}
pub fn write_to<W: Write>(&mut self, out: &mut W) -> fmt::Result {
if self.size == 0 {
return Ok(());
}
debug_assert!(N.is_power_of_two());
let mask: usize = N - 1;
debug_assert!(mask.count_ones() == mask.trailing_ones());
let mut buffer = [0u8; BUFFER_SIZE];
let mut rem = self.size;
while rem > 0 {
let bytes = &mut buffer[..self.size.min(BUFFER_SIZE)];
self.random.fill(bytes);
for &b in &*bytes {
let idx = b as usize & mask;
debug_assert!(idx < N);
out.write_char(self.alphabet.0[idx])?;
}
rem -= bytes.len();
}
Ok(())
}
pub fn fmt(&mut self) -> Fmt<'_, 'a, R, N> {
Fmt(RefCell::new(self))
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn gen(&mut self) -> String {
let mut res = String::with_capacity(self.size);
self.write_to(&mut res).unwrap();
res
}
#[cfg(feature = "smartstring")]
pub fn gen_smartstring(&mut self) -> smartstring::alias::String {
let mut res = smartstring::alias::String::new();
self.write_to(&mut res).unwrap();
res
}
}
impl<'a, R: Rng> Generator<'a, R> {
pub fn with_random(random: R) -> Self {
Self {
alphabet: &alphabet::DEFAULT,
random,
size: DEFAULT_SIZE,
}
}
}
pub struct Fmt<'g, 'a: 'g, R: Rng, const N: usize>(RefCell<&'g mut Generator<'a, R, N>>);
impl<'g, 'a: 'g, R: Rng, const N: usize> fmt::Display for Fmt<'g, 'a, R, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.borrow_mut().write_to(f)
}
}
#[cfg(feature = "std-rand")]
#[macro_export]
macro_rules! randoid {
() => {
$crate::randoid()
};
($size:expr) => {
$crate::Generator::with_size($size).gen()
};
($size:expr, &$alphabet:expr) => {
$crate::Generator::new($size, &$alphabet, rand::thread_rng()).gen()
};
($size:expr, [$($alphabet:literal),+]) => {
randoid!($size, &$crate::alphabet::Alphabet::new([$($alphabet),+]))
};
($size:expr, &$alphabet:expr, $rand:expr) => {
$crate::Generator::new($size, &$alphabet, $rand).gen()
};
($size:expr, [$($alphabet:literal),+], $rand:expr) => {
randoid!($size, &$crate::alphabet::Alphabet::new([$($alphabet),+]), $rand)
};
}