nanoid/lib.rs
1//! A tiny, secure, URL-friendly, unique string ID generator
2//!
3//! **Safe.** It uses cryptographically strong random APIs
4//! and guarantees a proper distribution of symbols.
5//!
6//! **Compact.** It uses a larger alphabet than UUID (`A-Za-z0-9_-`)
7//! and has a similar number of unique IDs in just 21 symbols instead of 36.
8//!
9//! ```toml
10//! [dependencies]
11//! nanoid = "0.5.0"
12//! ```
13//!
14//! ```rust
15//! use nanoid::nanoid;
16//!
17//! let id = nanoid!(); //=> "Yo1Tr9F3iF-LFHX9i9GvA"
18//! # assert_eq!(id.len(), 21);
19//! ```
20//!
21//! ## Usage
22//!
23//! ### Simple
24//!
25//! The main module uses URL-friendly symbols (`A-Za-z0-9_-`) and returns an ID
26//! with 21 characters.
27//!
28//! ```rust
29//! use nanoid::nanoid;
30//!
31//! let id = nanoid!(); //=> "Yo1Tr9F3iF-LFHX9i9GvA"
32//! # assert_eq!(id.len(), 21);
33//! ```
34//!
35//! Symbols `-,.()` are not encoded in the URL. If used at the end of a link
36//! they could be identified as a punctuation symbol.
37//!
38//! ### Custom length
39//!
40//! If you want to reduce ID length (and increase collisions probability),
41//! you can pass the length as an argument generate function:
42//!
43//! ```rust
44//! use nanoid::nanoid;
45//!
46//! let id = nanoid!(10); //=> "IRFa-VaY2b"
47//! # assert_eq!(id.len(), 10);
48//! ```
49//!
50//! ### Custom Alphabet or Length
51//!
52//! If you want to change the ID's alphabet or length, you can pass
53//! a custom alphabet to the `nanoid!()` macro as the second parameter.
54//!
55//! ```rust
56//! use nanoid::nanoid;
57//!
58//! let alphabet: [char; 16] = [
59//! '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f'
60//! ];
61//!
62//! let id = nanoid!(10, &alphabet); //=> "4f90d13a42"
63//! # assert_eq!(id.len(), 10);
64//! ```
65//!
66//! Alphabet must contain 256 symbols or less.
67//! Otherwise, the generator will not be secure.
68//!
69//! ### Custom Random Bytes Generator
70//!
71//! You can replace the default safe random generator by passing your own
72//! function as the third argument to `nanoid!()`. For instance, to use a
73//! seed-based generator.
74//!
75//! ```rust
76//! use nanoid::nanoid;
77//!
78//! fn random_byte () -> u8 {
79//! 0
80//! }
81//!
82//! fn main() {
83//! fn random (size: usize) -> Vec<u8> {
84//! let mut bytes: Vec<u8> = vec![0; size];
85//!
86//! for i in 0..size {
87//! bytes[i] = random_byte();
88//! }
89//!
90//! bytes
91//! }
92//!
93//! nanoid!(10, &['a', 'b', 'c', 'd', 'e', 'f'], random); //=> "fbaefaadeb"
94//! }
95//! ```
96//!
97//! `random` function must accept the array size and return an vector
98//! with random numbers.
99//!
100//! If you want to use the same URL-friendly symbols with a custom random
101//! source, the default alphabet is exposed as `nanoid::alphabet::SAFE`:
102//!
103//! ```rust
104//! use nanoid::nanoid;
105//!
106//! fn random (size: usize) -> Vec<u8> {
107//! let result: Vec<u8> = vec![0; size];
108//!
109//! result
110//! }
111//!
112//! fn main() {
113//! nanoid!(10, &nanoid::alphabet::SAFE, random); //=> "93ce_Ltuub"
114//! }
115//! ```
116//!
117//! ### Seeded Random Generator
118//!
119//! You can use a seeded random generator for reproducible IDs.
120//! This is useful for testing or when you need deterministic output.
121//!
122//! ```rust
123//! use nanoid::nanoid;
124//! use rand::{rngs::StdRng, Rng, SeedableRng};
125//!
126//! let mut rng = StdRng::seed_from_u64(42);
127//!
128//! let id = nanoid!(10, &nanoid::alphabet::SAFE, |size| {
129//! let mut bytes = vec![0u8; size];
130//! rng.fill(&mut bytes[..]);
131//! bytes
132//! });
133//!
134//! # assert_eq!(id.len(), 10);
135//! println!("{}", id); //=> "wyBwxRa4Xf"
136//! ```
137//!
138//! The random generator accepts `Fn` and `FnMut` closures, allowing you to use
139//! stateful random generators. This enables use cases like:
140//!
141//! - Seeded RNGs for reproducible IDs
142//! - Custom stateful generators
143//! - Integration with external random sources
144//!
145
146#![doc(
147 html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
148 html_favicon_url = "https://www.rust-lang.org/favicon.ico",
149 html_root_url = "https://docs.rs/nanoid"
150)]
151
152#[cfg(feature = "smartstring")]
153use smartstring::alias::String;
154
155pub mod alphabet;
156pub mod rngs;
157
158pub fn format<F: FnMut(usize) -> Vec<u8>>(random: F, alphabet: &[char], size: usize) -> String {
159 assert!(
160 alphabet.len() <= u8::MAX as usize,
161 "The alphabet cannot be longer than a `u8` (to comply with the `random` function)"
162 );
163
164 #[cfg(not(feature = "smartstring"))]
165 let mut id = String::with_capacity(size);
166 #[cfg(feature = "smartstring")]
167 let mut id = String::new();
168
169 if alphabet.len().is_power_of_two() {
170 fast_impl(&mut id, random, alphabet, size);
171 } else {
172 generic_impl(&mut id, random, alphabet, size);
173 }
174 id
175}
176
177/// Generic implementation that works for any alphabet with up to 256 characters.
178fn generic_impl<F: FnMut(usize) -> Vec<u8>>(
179 id: &mut String,
180 mut random: F,
181 alphabet: &[char],
182 size: usize,
183) {
184 let mask = alphabet.len().next_power_of_two() - 1;
185 let step: usize = 8 * size / 5;
186
187 // Assert that the masking does not truncate the alphabet. (See #9)
188 debug_assert!(alphabet.len() <= mask + 1);
189
190 loop {
191 let bytes = random(step);
192
193 for &byte in &bytes {
194 let byte = byte as usize & mask;
195
196 if alphabet.len() > byte {
197 id.push(alphabet[byte]);
198
199 if id.len() == size {
200 return;
201 }
202 }
203 }
204 }
205}
206
207/// Faster implementation that assumes the size of the alphabet is a power of 2.
208///
209/// This allows us to skip some checks and branches that are necessary in the general case.
210fn fast_impl<F: FnMut(usize) -> Vec<u8>>(
211 id: &mut String,
212 mut random: F,
213 alphabet: &[char],
214 size: usize,
215) {
216 debug_assert!(alphabet.len().is_power_of_two());
217
218 let mask = alphabet.len() - 1;
219
220 // Since we never discard values, we can request the exact number of bytes up front.
221 let bytes = random(size);
222
223 for &byte in &bytes {
224 let byte = byte as usize & mask;
225 id.push(alphabet[byte]);
226 }
227}
228
229#[cfg(test)]
230mod test_format {
231 use super::*;
232
233 #[test]
234 fn generates_random_string() {
235 fn random(size: usize) -> Vec<u8> {
236 [2, 255, 0, 1].iter().cloned().cycle().take(size).collect()
237 }
238
239 assert_eq!(format(random, &['a', 'b', 'c'], 4), "cabc");
240 }
241
242 #[test]
243 #[should_panic]
244 fn bad_alphabet() {
245 let alphabet: Vec<char> = (0..32_u8).cycle().map(|i| i as char).take(1000).collect();
246 nanoid!(21, &alphabet);
247 }
248
249 #[test]
250 fn non_power_2() {
251 let id: String = nanoid!(42, &alphabet::SAFE[0..62]);
252
253 assert_eq!(id.len(), 42);
254 }
255
256 #[test]
257 fn power_of_two_uses_fast_path() {
258 fn random(size: usize) -> Vec<u8> {
259 (0..size as u8).collect()
260 }
261
262 let alphabet: [char; 4] = ['a', 'b', 'c', 'd'];
263 // With a power-of-two alphabet, every byte maps directly via `byte & mask`,
264 // so the output is fully determined by the input bytes.
265 assert_eq!(format(random, &alphabet, 8), "abcdabcd");
266 }
267}
268
269#[macro_export]
270macro_rules! nanoid {
271 // simple
272 () => {
273 $crate::format($crate::rngs::default, &$crate::alphabet::SAFE, 21)
274 };
275
276 // generate
277 ($size:expr) => {
278 $crate::format($crate::rngs::default, &$crate::alphabet::SAFE, $size)
279 };
280
281 // custom
282 ($size:expr, $alphabet:expr) => {
283 $crate::format($crate::rngs::default, $alphabet, $size)
284 };
285
286 // complex
287 ($size:expr, $alphabet:expr, $random:expr) => {
288 $crate::format($random, $alphabet, $size)
289 };
290}
291
292#[cfg(test)]
293mod test_macros {
294 use super::*;
295
296 #[test]
297 fn simple() {
298 let id: String = nanoid!();
299
300 assert_eq!(id.len(), 21);
301 }
302
303 #[test]
304 fn generate() {
305 let id: String = nanoid!(42);
306
307 assert_eq!(id.len(), 42);
308 }
309
310 #[test]
311 fn custom() {
312 let id: String = nanoid!(42, &alphabet::SAFE);
313
314 assert_eq!(id.len(), 42);
315 }
316
317 #[test]
318 fn complex() {
319 let id: String = nanoid!(4, &alphabet::SAFE, rngs::default);
320
321 assert_eq!(id.len(), 4);
322 }
323
324 #[test]
325 fn closure() {
326 let uuid = "8936ad0c-9443-4007-9430-e223c64d4629";
327
328 let id1 = nanoid!(20, &alphabet::SAFE, |_| uuid.as_bytes().to_vec());
329 let id2 = nanoid!(20, &alphabet::SAFE, |_| uuid.as_bytes().to_vec());
330
331 assert_eq!(id1, id2);
332 }
333
334 #[test]
335 fn simple_expression() {
336 let id: String = nanoid!(42 / 2);
337
338 assert_eq!(id.len(), 21);
339 }
340
341 #[test]
342 fn fnmut_closure() {
343 // Test FnMut closures with mutable state
344 let mut counter = 0u8;
345
346 let id = nanoid!(10, &alphabet::SAFE, |size| {
347 let mut bytes = vec![0u8; size];
348 for byte in &mut bytes {
349 *byte = counter;
350 counter = counter.wrapping_add(1);
351 }
352 bytes
353 });
354
355 assert_eq!(id.len(), 10);
356 assert!(counter > 0); // Verify the closure actually mutated the counter
357 }
358}
359
360#[cfg(doctest)]
361doc_comment::doctest!("../README.md");