Skip to main content

rig_core/
id.rs

1//! Lightweight generation of short, unique, URL-safe identifiers.
2//!
3//! These IDs are used purely to disambiguate things like in-flight tool calls
4//! and request headers — they are *not* security-sensitive and do not need a
5//! cryptographic source of randomness. We therefore generate them with
6//! [`fastrand`] (already a hard dependency of this crate) rather than pulling in
7//! `nanoid` → `rand` → `getrandom`, which would add a cryptographic RNG (and a
8//! `getrandom/js` shim on wasm) for no benefit here.
9
10/// The URL-safe alphabet used by `nanoid` (`A-Za-z0-9_-`), preserved so the
11/// shape of generated IDs is unchanged from the previous implementation.
12const ALPHABET: &[u8; 64] = b"_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
13
14/// Default ID length, matching `nanoid`'s default.
15const DEFAULT_LEN: usize = 21;
16
17/// Generate a 21-character, URL-safe, non-cryptographic identifier.
18///
19/// This is a drop-in replacement for the previous `nanoid!()` usage for
20/// internal, non-security-sensitive use.
21pub fn generate() -> String {
22    generate_with_len(DEFAULT_LEN)
23}
24
25/// Generate a `len`-character, URL-safe, non-cryptographic identifier.
26pub fn generate_with_len(len: usize) -> String {
27    std::iter::repeat_with(|| {
28        let idx = fastrand::usize(..ALPHABET.len());
29        // `idx` is always in bounds, but use `get` to avoid the `indexing_slicing` lint.
30        ALPHABET.get(idx).copied().unwrap_or(b'_') as char
31    })
32    .take(len)
33    .collect()
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn default_length_and_alphabet() {
42        let id = generate();
43        assert_eq!(id.len(), DEFAULT_LEN);
44        assert!(id.bytes().all(|b| ALPHABET.contains(&b)));
45    }
46
47    #[test]
48    fn ids_are_unique() {
49        let a = generate();
50        let b = generate();
51        assert_ne!(a, b);
52    }
53
54    #[test]
55    fn custom_length() {
56        assert_eq!(generate_with_len(8).len(), 8);
57    }
58}