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}