Skip to main content

opendev_runtime/
plan_names.rs

1//! Generate unique plan names using adjective-verb-noun pattern.
2//!
3//! Ported from `opendev/core/runtime/plan_names.py`.
4
5use rand::Rng;
6use rand::seq::SliceRandom;
7use std::path::Path;
8
9const ADJECTIVES: &[&str] = &[
10    "bold", "calm", "cool", "crisp", "dark", "deep", "fair", "fast", "fine", "free", "glad",
11    "gold", "gray", "keen", "kind", "lean", "mild", "neat", "pale", "pure", "rare", "rich", "safe",
12    "slim", "soft", "tall", "tidy", "warm", "wide", "wise",
13];
14
15const VERBS: &[&str] = &[
16    "blazing",
17    "dashing",
18    "diving",
19    "drifting",
20    "flying",
21    "gliding",
22    "growing",
23    "hiding",
24    "jumping",
25    "landing",
26    "leaping",
27    "lifting",
28    "moving",
29    "pacing",
30    "racing",
31    "rising",
32    "roaming",
33    "rowing",
34    "running",
35    "sailing",
36    "singing",
37    "sliding",
38    "soaring",
39    "spinning",
40    "splashing",
41    "standing",
42    "surfing",
43    "swimming",
44    "swinging",
45    "waving",
46];
47
48const NOUNS: &[&str] = &[
49    "badger", "crane", "dolphin", "eagle", "falcon", "gecko", "hawk", "heron", "jaguar", "koala",
50    "lark", "lemur", "lynx", "mantis", "otter", "panda", "parrot", "puffin", "quail", "raven",
51    "robin", "salmon", "seal", "shark", "sparrow", "tiger", "turtle", "viper", "walrus", "whale",
52];
53
54/// Generate a unique plan name like `bold-blazing-badger`.
55///
56/// If `existing_dir` is given, checks for collision with `{name}.md` files
57/// in that directory. Falls back to a numeric suffix after `max_attempts`.
58pub fn generate_plan_name(existing_dir: Option<&Path>, max_attempts: u32) -> String {
59    let mut rng = rand::thread_rng();
60
61    for _ in 0..max_attempts {
62        // SAFETY: ADJECTIVES, VERBS, NOUNS are non-empty compile-time arrays
63        let adj = ADJECTIVES
64            .choose(&mut rng)
65            .expect("ADJECTIVES is non-empty");
66        let verb = VERBS.choose(&mut rng).expect("VERBS is non-empty");
67        let noun = NOUNS.choose(&mut rng).expect("NOUNS is non-empty");
68        let name = format!("{}-{}-{}", adj, verb, noun);
69
70        if let Some(dir) = existing_dir {
71            if !dir.join(format!("{}.md", name)).exists() {
72                return name;
73            }
74        } else {
75            return name;
76        }
77    }
78
79    // Fallback: append random digits
80    let adj = ADJECTIVES
81        .choose(&mut rng)
82        .expect("ADJECTIVES is non-empty");
83    let verb = VERBS.choose(&mut rng).expect("VERBS is non-empty");
84    let noun = NOUNS.choose(&mut rng).expect("NOUNS is non-empty");
85    let suffix: u32 = rng.gen_range(1000..10000);
86    format!("{}-{}-{}-{}", adj, verb, noun, suffix)
87}
88
89#[cfg(test)]
90#[path = "plan_names_tests.rs"]
91mod tests;