1use rand::Rng;
2
3const ADJECTIVES: &[&str] = &[
4 "amber", "bold", "calm", "dark", "eager", "fast", "glad", "hazy", "iron", "jade", "keen",
5 "lush", "mild", "neat", "opal", "pale", "quiet", "rare", "sage", "teal", "ultra", "vast",
6 "warm", "zinc", "aqua", "blue", "crisp", "deep", "fair", "gold", "hush", "icy", "just", "kind",
7 "lean", "moss", "navy", "oak", "pure", "quick", "rosy", "slim", "true", "used", "vine", "wide",
8 "young", "zeal", "ashen", "brave", "clear", "dusty", "elite", "fresh", "green", "happy",
9 "ivory", "jolly", "lunar", "maple", "noble", "olive", "prime", "royal", "solar", "terra",
10 "urban", "vivid", "windy", "xenon", "misty", "coral",
11];
12
13const NOUNS: &[&str] = &[
14 "ant", "bat", "cat", "dove", "elk", "fox", "goat", "hare", "ibis", "jay", "koi", "lark",
15 "moth", "newt", "owl", "puma", "quail", "ram", "seal", "toad", "urchin", "vole", "wasp", "yak",
16 "ape", "bass", "crab", "deer", "eel", "frog", "gull", "hawk", "iguana", "jackal", "koala",
17 "lion", "mule", "narwhal", "otter", "parrot", "raven", "shark", "tiger", "tern", "viper",
18 "whale", "wren", "zebra", "bear", "crane", "duck", "eagle", "finch", "gecko", "horse", "imp",
19 "jaguar", "kite", "lynx", "mink", "osprey", "panda", "robin", "swan", "trout", "unicorn",
20 "walrus", "wolf", "falcon", "heron", "lemur", "moose",
21];
22
23pub fn generate_codename() -> String {
25 let mut rng = rand::thread_rng();
26 let adj = ADJECTIVES[rng.gen_range(0..ADJECTIVES.len())];
27 let noun = NOUNS[rng.gen_range(0..NOUNS.len())];
28 format!("{adj}-{noun}")
29}
30
31pub fn generate_unique_codename(existing: &[String]) -> String {
33 for _ in 0..100 {
34 let name = generate_codename();
35 if !existing.contains(&name) {
36 return name;
37 }
38 }
39 let base = generate_codename();
41 let mut rng = rand::thread_rng();
42 format!("{base}-{}", rng.gen_range(100..999))
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48
49 #[test]
50 fn codename_format() {
51 let name = generate_codename();
52 assert!(name.contains('-'), "codename should contain a dash: {name}");
53 let parts: Vec<&str> = name.split('-').collect();
54 assert_eq!(
55 parts.len(),
56 2,
57 "codename should have exactly two parts: {name}"
58 );
59 }
60
61 #[test]
62 fn unique_codename_avoids_collisions() {
63 let existing = vec!["iron-falcon".into()];
64 let name = generate_unique_codename(&existing);
65 assert_ne!(name, "iron-falcon");
66 }
67}