Skip to main content

fakecloud_core/
ids.rs

1//! Shared random resource-id generation.
2//!
3//! Many services mint short resource ids by truncating a v4 UUID. Before this
4//! module each did it inline (`Uuid::new_v4().simple().to_string()[..N]`) or in
5//! a per-crate `short_id` helper with a slightly different length, which was a
6//! maintenance trap. These helpers centralise the idiom so a caller states the
7//! length it needs explicitly.
8
9/// A lowercase-hex id of `len` characters, drawn from a v4 UUID.
10///
11/// AWS short resource ids in this shape match `^[0-9a-f]{len}$`. `len` must be
12/// at most 32 (a UUID has 32 hex digits); larger values are clamped to 32.
13pub fn short_id(len: usize) -> String {
14    let hex = uuid::Uuid::new_v4().simple().to_string();
15    hex[..len.min(hex.len())].to_string()
16}
17
18/// A base-36 (`[0-9a-z]`) id of `len` characters, drawn from UUID entropy.
19///
20/// Used where AWS ids are lowercase-alphanumeric rather than hex. Draws from as
21/// many v4 UUIDs as needed to cover `len` characters.
22pub fn alnum_id(len: usize) -> String {
23    const ALPHABET: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";
24    let mut out = String::with_capacity(len);
25    while out.len() < len {
26        for b in uuid::Uuid::new_v4().into_bytes() {
27            out.push(ALPHABET[(b as usize) % ALPHABET.len()] as char);
28            if out.len() == len {
29                break;
30            }
31        }
32    }
33    out
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn short_id_has_requested_length_and_is_hex() {
42        for n in [6, 10, 12, 16, 20, 32] {
43            let id = short_id(n);
44            assert_eq!(id.len(), n);
45            assert!(id
46                .chars()
47                .all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase()));
48        }
49    }
50
51    #[test]
52    fn short_id_clamps_over_32() {
53        assert_eq!(short_id(64).len(), 32);
54    }
55
56    #[test]
57    fn alnum_id_has_requested_length_and_is_base36() {
58        for n in [1, 26, 40] {
59            let id = alnum_id(n);
60            assert_eq!(id.len(), n);
61            assert!(id
62                .chars()
63                .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit()));
64        }
65    }
66}