/rust/registry/src/index.crates.io-6f17d22bba15001f/nanoid-0.4.0/src/lib.rs
Line | Count | Source (jump to first uncovered line) |
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 | | |
127 | | pub mod alphabet; |
128 | | pub mod rngs; |
129 | | |
130 | 540k | pub fn format(random: fn(usize) -> Vec<u8>, alphabet: &[char], size: usize) -> String { |
131 | 540k | assert!( |
132 | 540k | alphabet.len() <= u8::max_value() as usize, |
133 | 540k | "The alphabet cannot be longer than a `u8` (to comply with the `random` function)" |
134 | 540k | ); |
135 | | |
136 | 540k | let mask = alphabet.len().next_power_of_two() - 1; |
137 | 540k | let step: usize = 8 * size / 5; |
138 | 540k | |
139 | 540k | // Assert that the masking does not truncate the alphabet. (See #9) |
140 | 540k | debug_assert!(alphabet.len() <= mask + 1); |
141 | | |
142 | 540k | let mut id = String::with_capacity(size); |
143 | | |
144 | | loop { |
145 | 919k | let bytes = random(step); |
146 | | |
147 | 19.5M | for &byte in &bytes { |
148 | 19.2M | let byte = byte as usize & mask; |
149 | 19.2M | |
150 | 19.2M | if alphabet.len() > byte { |
151 | 10.8M | id.push(alphabet[byte]); |
152 | 10.8M | |
153 | 10.8M | if id.len() == size { |
154 | 540k | return id; |
155 | 10.2M | } |
156 | 8.40M | } |
157 | | } |
158 | | } |
159 | 540k | } |
160 | | |
161 | | #[cfg(test)] |
162 | | mod 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] |
190 | | macro_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)] |
213 | | mod 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)] |
246 | | doc_comment::doctest!("../README.md"); |