1#![expect(
2 clippy::indexing_slicing,
3 reason = "Random indices are generated from the exact lengths of the static slug tables."
4)]
5
6use rand::RngExt;
14
15const ADJECTIVES: &[&str] = &[
17 "brave", "calm", "clever", "cosmic", "crisp", "curious", "eager", "gentle", "glowing", "happy", "hidden", "jolly",
18 "kind", "lucky", "mighty", "misty", "neon", "nimble", "playful", "proud", "quick", "quiet", "shiny", "silent",
19 "stellar", "sunny", "swift", "tidy", "witty", "bright",
20];
21
22const NOUNS: &[&str] = &[
24 "cabin", "cactus", "canyon", "circuit", "comet", "eagle", "engine", "falcon", "forest", "garden", "harbor",
25 "island", "knight", "lagoon", "meadow", "moon", "mountain", "nebula", "orchid", "otter", "panda", "pixel",
26 "planet", "river", "rocket", "sailor", "squid", "star", "tiger", "wizard", "wolf", "stream",
27];
28
29pub fn create() -> String {
41 let mut rng = rand::rng();
42 let adj_idx = rng.random_range(0..ADJECTIVES.len());
43 let noun_idx = rng.random_range(0..NOUNS.len());
44
45 format!("{}-{}", ADJECTIVES[adj_idx], NOUNS[noun_idx])
46}
47
48pub fn create_timestamped() -> String {
61 let timestamp = std::time::SystemTime::now()
62 .duration_since(std::time::UNIX_EPOCH)
63 .map(|d| d.as_millis())
64 .unwrap_or(0);
65
66 format!("{}-{}", timestamp, create())
67}
68
69pub fn create_with_prefix(prefix: &str) -> String {
80 format!("{}-{}", prefix, create())
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn test_create_slug() {
89 let slug = create();
90 assert!(slug.contains('-'));
91 let parts: Vec<&str> = slug.split('-').collect();
92 assert_eq!(parts.len(), 2);
93 assert!(ADJECTIVES.contains(&parts[0]));
94 assert!(NOUNS.contains(&parts[1]));
95 }
96
97 #[test]
98 fn test_create_timestamped() {
99 let slug = create_timestamped();
100 let parts: Vec<&str> = slug.split('-').collect();
101 assert_eq!(parts.len(), 3);
102 assert!(parts[0].parse::<u128>().is_ok());
103 }
104
105 #[test]
106 fn test_create_with_prefix() {
107 let slug = create_with_prefix("plan");
108 assert!(slug.starts_with("plan-"));
109 let parts: Vec<&str> = slug.split('-').collect();
110 assert_eq!(parts.len(), 3);
111 assert_eq!(parts[0], "plan");
112 }
113
114 #[test]
115 fn test_uniqueness() {
116 let slugs: Vec<String> = (0..100).map(|_| create()).collect();
117 let unique_count = slugs.iter().collect::<hashbrown::HashSet<_>>().len();
118 assert!(unique_count > 50, "Expected mostly unique slugs");
119 }
120}