Coverage Report

Created: 2025-02-21 07:11

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