diff --git a/Cargo.lock b/Cargo.lock index a0c5b75..db03bc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,6 +82,17 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core", +] + [[package]] name = "clap" version = "4.6.0" @@ -104,6 +115,15 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_complete" +version = "4.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f84a88507dbd05c695f2cb5e8558e747179134005e9893882dec964190ed89" +dependencies = [ + "clap", +] + [[package]] name = "clap_derive" version = "4.6.0" @@ -139,6 +159,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "dirs" version = "6.0.0" @@ -234,6 +263,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", + "rand_core", "wasip2", "wasip3", ] @@ -499,6 +529,29 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "petname" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1201345ec6f6630308d0549e198c58aff61df8033afe094f072def1ebe43eafa" +dependencies = [ + "clap", + "clap_complete", + "petname-macros", + "rand", +] + +[[package]] +name = "petname-macros" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9b4bcfb5a86125ae8f1342b95f8b64d050f0a4385c9533c5a17970b9e0479e7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "potential_utf" version = "0.1.5" @@ -542,6 +595,23 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "redox_users" version = "0.5.2" @@ -1037,6 +1107,7 @@ dependencies = [ "humantime", "humantime-serde", "insta", + "petname", "serde", "tempfile", "toml", diff --git a/Cargo.toml b/Cargo.toml index 61e198c..de160ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ anyhow = "1" url = "2" dirs = "6" uuid = { version = "1", features = ["v4"] } +petname = "3" humantime = "2" humantime-serde = "1" which = "8.0.2" diff --git a/src/name_gen.rs b/src/name_gen.rs index 28d8bd6..59fa24e 100644 --- a/src/name_gen.rs +++ b/src/name_gen.rs @@ -1,45 +1,12 @@ //! Random human-friendly workspace name generator (e.g. `"bold_turing"`). -use uuid::Uuid; - -const ADJECTIVES: &[&str] = &[ - "bold", "bright", "brave", "calm", "clever", "eager", "fair", "gentle", "happy", "keen", - "kind", "lively", "merry", "noble", "proud", "quiet", "swift", "warm", "wise", "free", -]; - -const NOUNS: &[&str] = &[ - "turing", - "neumann", - "lovelace", - "hopper", - "curie", - "darwin", - "euler", - "gauss", - "newton", - "pascal", - "ramanujan", - "knuth", - "dijkstra", - "boole", - "babbage", - "shannon", - "hawking", - "einstein", - "feynman", - "tesla", -]; /// Generate a random workspace name in `adjective_noun` format. /// -/// Uses UUID v4 entropy so each call produces a unique name with high -/// probability. Example outputs: `"bold_turing"`, `"calm_lovelace"`. +/// Backed by the `petname` crate's curated word lists and RNG. +/// Example outputs: `"bold_turing"`, `"calm_lovelace"`. #[must_use] pub fn generate_name() -> String { - let id = Uuid::new_v4(); - let b = id.as_bytes(); - let adj = ADJECTIVES[b[0] as usize % ADJECTIVES.len()]; - let noun = NOUNS[b[1] as usize % NOUNS.len()]; - format!("{adj}_{noun}") + petname::petname(2, "_").unwrap_or_else(|| "workspace".to_string()) } #[cfg(test)] @@ -47,19 +14,19 @@ mod tests { use super::*; #[test] - fn format_is_adjective_underscore_noun() { + fn format_is_two_words_joined_by_underscore() { let name = generate_name(); let mut parts = name.splitn(2, '_'); - let adj = parts.next().expect("adjective part"); - let noun = parts.next().expect("noun part"); - assert!(ADJECTIVES.contains(&adj), "unknown adjective: {adj}"); - assert!(NOUNS.contains(&noun), "unknown noun: {noun}"); + let adj = parts.next().unwrap_or_default(); + let noun = parts.next().unwrap_or_default(); + assert!(!adj.is_empty(), "missing adjective part"); + assert!(!noun.is_empty(), "missing noun part"); } #[test] fn generates_distinct_names() { - // 20×20 = 400 possible names; P(all 5 identical) ≈ (1/400)^4 ≈ 0 - let names: std::collections::HashSet<_> = (0..5).map(|_| generate_name()).collect(); - assert!(names.len() > 1, "5 calls all returned the same name"); + // Curated word lists are large; 20 draws matching is effectively impossible. + let names: std::collections::HashSet<_> = (0..20).map(|_| generate_name()).collect(); + assert!(names.len() > 1, "20 calls all returned the same name"); } }