Skip to main content

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");