everruns_core/
resource_names.rs1use rand::RngExt;
4
5const SUFFIX_ALPHABET: &[u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";
6const SUFFIX_LEN: usize = 6;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct UniqueResourceName {
10 pub requested_name: String,
11 pub canonical_name: String,
12}
13
14pub fn unique_resource_name(requested_name: &str) -> UniqueResourceName {
15 let requested_name = requested_name.trim().to_string();
16 let base_name = requested_name.trim_end_matches('-');
17 let mut rng = rand::rng();
18 let suffix: String = (0..SUFFIX_LEN)
19 .map(|_| {
20 let index = rng.random_range(0..SUFFIX_ALPHABET.len());
21 SUFFIX_ALPHABET[index] as char
22 })
23 .collect();
24 let canonical_name = if base_name.is_empty() {
25 suffix
26 } else {
27 format!("{base_name}-{suffix}")
28 };
29
30 UniqueResourceName {
31 requested_name,
32 canonical_name,
33 }
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39
40 #[test]
41 fn unique_resource_name_appends_lowercase_alphanumeric_suffix() {
42 let name = unique_resource_name("sandbox-name");
43 let suffix = name
44 .canonical_name
45 .strip_prefix("sandbox-name-")
46 .expect("suffix should be appended");
47
48 assert_eq!(name.requested_name, "sandbox-name");
49 assert_eq!(suffix.len(), 6);
50 assert!(
51 suffix
52 .chars()
53 .all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit())
54 );
55 }
56
57 #[test]
58 fn unique_resource_name_trims_input_and_trailing_dash() {
59 let name = unique_resource_name(" sandbox-name- ");
60
61 assert_eq!(name.requested_name, "sandbox-name-");
62 assert!(name.canonical_name.starts_with("sandbox-name-"));
63 assert!(!name.canonical_name.starts_with("sandbox-name--"));
64 }
65}