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.4.0"
12//! ```
13//!
14//! ```rust
15//! use nanoid::nanoid;
16//!
17//! fn main() {
18//! let id = nanoid!(); //=> "Yo1Tr9F3iF-LFHX9i9GvA"
19//! }
20//! ```
21//!
22//! ## Usage
23//!
24//! ### Simple
25//!
26//! The main module uses URL-friendly symbols (`A-Za-z0-9_~`) and returns an ID
27//! with 21 characters.
28//!
29//! ```rust
30//! use nanoid::nanoid;
31//!
32//! fn main() {
33//! let id = nanoid!(); //=> "Yo1Tr9F3iF-LFHX9i9GvA"
34//! }
35//! ```
36//!
37//! Symbols `-,.()` are not encoded in the URL. If used at the end of a link
38//! they could be identified as a punctuation symbol.
39//!
40//! ### Custom length
41//!
42//! If you want to reduce ID length (and increase collisions probability),
43//! you can pass the length as an argument generate function:
44//!
45//! ```rust
46//! use nanoid::nanoid;
47//!
48//! fn main() {
49//! let id = nanoid!(10); //=> "IRFa~VaY2b"
50//! }
51//! ```
52//!
53//! ### Custom Alphabet or Length
54//!
55//! If you want to change the ID's alphabet or length
56//! you can use the low-level `custom` module.
57//!
58//! ```rust
59//! use nanoid::nanoid;
60//!
61//! fn main() {
62//! let alphabet: [char; 16] = [
63//! '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f'
64//! ];
65//!
66//! let id = nanoid!(10, &alphabet); //=> "4f90d13a42"
67//! }
68//! ```
69//!
70//! Alphabet must contain 256 symbols or less.
71//! Otherwise, the generator will not be secure.
72//!
73//! ### Custom Random Bytes Generator
74//!
75//! You can replace the default safe random generator using the `complex` module.
76//! For instance, to use a seed-based generator.
77//!
78//! ```rust
79//! use nanoid::nanoid;
80//!
81//! fn random_byte () -> u8 {
82//! 0
83//! }
84//!
85//! fn main() {
86//! fn random (size: usize) -> Vec<u8> {
87//! let mut bytes: Vec<u8> = vec![0; size];
88//!
89//! for i in 0..size {
90//! bytes[i] = random_byte();
91//! }
92//!
93//! bytes
94//! }
95//!
96//! nanoid!(10, &['a', 'b', 'c', 'd', 'e', 'f'], random); //=> "fbaefaadeb"
97//! }
98//! ```
99//!
100//! `random` function must accept the array size and return an vector
101//! with random numbers.
102//!
103//! If you want to use the same URL-friendly symbols with `format`,
104//! you can get the default alphabet from the `url` module:
105//!
106//! ```rust
107//! use nanoid::nanoid;
108//!
109//! fn random (size: usize) -> Vec<u8> {
110//! let result: Vec<u8> = vec![0; size];
111//!
112//! result
113//! }
114//!
115//! fn main() {
116//! nanoid!(10, &nanoid::alphabet::SAFE, random); //=> "93ce_Ltuub"
117//! }
118//! ```
119//!
120
121#![doc(
122 html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
123 html_favicon_url = "https://www.rust-lang.org/favicon.ico",
124 html_root_url = "https://docs.rs/nanoid"
125)]
126
127pub mod alphabet;
128pub mod rngs;
129
130pub fn format(random: fn(usize) -> Vec<u8>, alphabet: &[char], size: usize) -> String {
131 assert!(
132 alphabet.len() <= u8::max_value() as usize,
133 "The alphabet cannot be longer than a `u8` (to comply with the `random` function)"
134 );
135
136 let mask = alphabet.len().next_power_of_two() - 1;
137 let step: usize = 8 * size / 5;
138
139 // Assert that the masking does not truncate the alphabet. (See #9)
140 debug_assert!(alphabet.len() <= mask + 1);
141
142 let mut id = String::with_capacity(size);
143
144 loop {
145 let bytes = random(step);
146
147 for &byte in &bytes {
148 let byte = byte as usize & mask;
149
150 if alphabet.len() > byte {
151 id.push(alphabet[byte]);
152
153 if id.len() == size {
154 return id;
155 }
156 }
157 }
158 }
159}
160
161#[cfg(test)]
162mod test_format {
163 use super::*;
164
165 #[test]
166 fn generates_random_string() {
167 fn random(size: usize) -> Vec<u8> {
168 [2, 255, 0, 1].iter().cloned().cycle().take(size).collect()
169 }
170
171 assert_eq!(format(random, &['a', 'b', 'c'], 4), "cabc");
172 }
173
174 #[test]
175 #[should_panic]
176 fn bad_alphabet() {
177 let alphabet: Vec<char> = (0..32_u8).cycle().map(|i| i as char).take(1000).collect();
178 nanoid!(21, &alphabet);
179 }
180
181 #[test]
182 fn non_power_2() {
183 let id: String = nanoid!(42, &alphabet::SAFE[0..62]);
184
185 assert_eq!(id.len(), 42);
186 }
187}
188
189#[macro_export]
190macro_rules! nanoid {
191 // simple
192 () => {
193 $crate::format($crate::rngs::default, &$crate::alphabet::SAFE, 21)
194 };
195
196 // generate
197 ($size:tt) => {
198 $crate::format($crate::rngs::default, &$crate::alphabet::SAFE, $size)
199 };
200
201 // custom
202 ($size:tt, $alphabet:expr) => {
203 $crate::format($crate::rngs::default, $alphabet, $size)
204 };
205
206 // complex
207 ($size:tt, $alphabet:expr, $random:expr) => {
208 $crate::format($random, $alphabet, $size)
209 };
210}
211
212#[cfg(test)]
213mod test_macros {
214 use super::*;
215
216 #[test]
217 fn simple() {
218 let id: String = nanoid!();
219
220 assert_eq!(id.len(), 21);
221 }
222
223 #[test]
224 fn generate() {
225 let id: String = nanoid!(42);
226
227 assert_eq!(id.len(), 42);
228 }
229
230 #[test]
231 fn custom() {
232 let id: String = nanoid!(42, &alphabet::SAFE);
233
234 assert_eq!(id.len(), 42);
235 }
236
237 #[test]
238 fn complex() {
239 let id: String = nanoid!(4, &alphabet::SAFE, rngs::default);
240
241 assert_eq!(id.len(), 4);
242 }
243}
244
245#[cfg(doctest)]
246doc_comment::doctest!("../README.md");