1use rand::RngExt;
9
10const ADJECTIVES: &[&str] = &[
12 "brave", "calm", "clever", "cosmic", "crisp", "curious", "eager", "gentle", "glowing", "happy", "hidden", "jolly",
13 "kind", "lucky", "mighty", "misty", "neon", "nimble", "playful", "proud", "quick", "quiet", "shiny", "silent",
14 "stellar", "sunny", "swift", "tidy", "witty", "bright",
15];
16
17const NOUNS: &[&str] = &[
19 "cabin", "cactus", "canyon", "circuit", "comet", "eagle", "engine", "falcon", "forest", "garden", "harbor",
20 "island", "knight", "lagoon", "meadow", "moon", "mountain", "nebula", "orchid", "otter", "panda", "pixel",
21 "planet", "river", "rocket", "sailor", "squid", "star", "tiger", "wizard", "wolf", "stream",
22];
23
24pub fn create() -> String {
36 let mut rng = rand::rng();
37 let adj_idx = rng.random_range(0..ADJECTIVES.len());
38 let noun_idx = rng.random_range(0..NOUNS.len());
39
40 format!("{}-{}", ADJECTIVES[adj_idx], NOUNS[noun_idx])
41}
42
43pub fn create_timestamped() -> String {
56 let timestamp = std::time::SystemTime::now()
57 .duration_since(std::time::UNIX_EPOCH)
58 .map(|d| d.as_millis())
59 .unwrap_or(0);
60
61 format!("{}-{}", timestamp, create())
62}
63
64pub fn create_with_prefix(prefix: &str) -> String {
75 format!("{}-{}", prefix, create())
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn test_create_slug() {
84 let slug = create();
85 assert!(slug.contains('-'));
86 let parts: Vec<&str> = slug.split('-').collect();
87 assert_eq!(parts.len(), 2);
88 assert!(ADJECTIVES.contains(&parts[0]));
89 assert!(NOUNS.contains(&parts[1]));
90 }
91
92 #[test]
93 fn test_create_timestamped() {
94 let slug = create_timestamped();
95 let parts: Vec<&str> = slug.split('-').collect();
96 assert_eq!(parts.len(), 3);
97 assert!(parts[0].parse::<u128>().is_ok());
98 }
99
100 #[test]
101 fn test_create_with_prefix() {
102 let slug = create_with_prefix("plan");
103 assert!(slug.starts_with("plan-"));
104 let parts: Vec<&str> = slug.split('-').collect();
105 assert_eq!(parts.len(), 3);
106 assert_eq!(parts[0], "plan");
107 }
108
109 #[test]
110 fn test_uniqueness() {
111 let slugs: Vec<String> = (0..100).map(|_| create()).collect();
112 let unique_count = slugs.iter().collect::<hashbrown::HashSet<_>>().len();
113 assert!(unique_count > 50, "Expected mostly unique slugs");
114 }
115}