Files
galaxy/crates/galaxy_util/src/worktree_names.rs
T

263 lines
5.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use std::collections::HashSet;
use rand::seq::SliceRandom;
use rand::Rng;
/// Desert/southwest-themed words used to generate memorable worktree branch names.
/// Deduplicated across six categories.
const WORDS: &[&str] = &[
// ── Landforms & Terrain ─────────────────────────────────────────────────
"alcove",
"arch",
"arroyo",
"badlands",
"bajada",
"basin",
"bluff",
"bolson",
"butte",
"caldera",
"canyon",
"caprock",
"chasm",
"chimney",
"cinder",
"coulee",
"crag",
"crater",
"cuesta",
"dune",
"escarpment",
"flats",
"gap",
"gorge",
"gulch",
"hogback",
"inselberg",
"lava",
"ledge",
"malpais",
"mesa",
"mogote",
"monolith",
"notch",
"outcrop",
"pass",
"pediment",
"pinnacle",
"plateau",
"playa",
"ravine",
"ridge",
"rimrock",
"saddle",
"scree",
"spire",
"switchback",
"talus",
"tepui",
"wash",
// ── Desert Plants ───────────────────────────────────────────────────────
"agave",
"barrel",
"brittlebush",
"cactus",
"candelilla",
"chamisa",
"chaparral",
"cholla",
"claret",
"creosote",
"fishhook",
"hedgehog",
"ironwood",
"jojoba",
"joshua",
"juniper",
"lechuguilla",
"lupine",
"madrone",
"mallow",
"manzanita",
"mariposa",
"mesquite",
"ocotillo",
"organ-pipe",
"palo-verde",
"pinyon",
"prickly",
"rabbitbrush",
"sagebrush",
"saguaro",
"saltbush",
"sotol",
"tumbleweed",
"yucca",
// ── Desert Animals ──────────────────────────────────────────────────────
"armadillo",
"badger",
"bighorn",
"bobcat",
"burrowing-owl",
"centipede",
"coachwhip",
"cottontail",
"cougar",
"coyote",
"falcon",
"gecko",
"gila",
"hawk",
"horned-toad",
"jackrabbit",
"javelina",
"kingsnake",
"kit-fox",
"mule-deer",
"nighthawk",
"prairie-dog",
"pronghorn",
"quail",
"racer",
"rattler",
"ringtail",
"roadrunner",
"scorpion",
"sidewinder",
"swift",
"tarantula",
"thrasher",
"tortoise",
"vulture",
"wren",
// ── Minerals, Rocks & Metals ────────────────────────────────────────────
"agate",
"basalt",
"calcite",
"cinnabar",
"cobalt",
"copper",
"feldspar",
"flint",
"garnet",
"granite",
"gypsum",
"iron",
"jasper",
"limestone",
"malachite",
"mica",
"obsidian",
"onyx",
"opal",
"petrified",
"pumice",
"pyrite",
"quartz",
"sandstone",
"shale",
"tin",
"topaz",
"travertine",
"turquoise",
"zinc",
// ── Southwest Culture & Spanish ─────────────────────────────────────────
"acequia",
"adobe",
"cumbre",
"equinox",
"hacienda",
"latilla",
"luminaria",
"metate",
"mirador",
"nicho",
"olla",
"oz",
"petroglyph",
"pictograph",
"portal",
"ramada",
"rio",
"ristra",
"sierra",
"siesta",
"solstice",
"tierra",
"tinaja",
"viga",
// ── Weather & Sky ───────────────────────────────────────────────────────
"brushfire",
"corona",
"dawn",
"drought",
"dry-lightning",
"dusk",
"dust-devil",
"ember",
"firestorm",
"flash-flood",
"haze",
"mirage",
"monsoon",
"moonrise",
"shimmer",
"smoke",
"starlight",
"sundog",
"sundowner",
"thermal",
"twilight",
"wildfire",
"zephyr",
];
/// Maximum number of random attempts at a given word count before escalating.
const MAX_RETRIES_PER_LEVEL: usize = 2;
/// Maximum word count before falling back to a numeric suffix.
const MAX_WORD_COUNT: usize = 5;
/// Generates a name with `word_count` distinct random words joined by `-`.
/// Returns `Some(name)` if a name not in `existing` is found within
/// `MAX_RETRIES_PER_LEVEL` attempts, or `None` if all attempts collided.
fn generate_name(
word_count: usize,
existing: &HashSet<&str>,
rng: &mut impl Rng,
) -> Option<String> {
for _ in 0..MAX_RETRIES_PER_LEVEL {
let chosen: Vec<&str> = WORDS.choose_multiple(rng, word_count).copied().collect();
let name = chosen.join("-");
if !existing.contains(name.as_str()) {
return Some(name);
}
}
None
}
/// Generates a unique worktree branch name that does not collide with any name
/// in `existing`. Starts with 2-word names and escalates to more words on
/// collision. Accepts an explicit RNG for deterministic testing.
pub fn generate_unique_name(existing: &HashSet<&str>, rng: &mut impl Rng) -> String {
for word_count in 2..=MAX_WORD_COUNT {
if let Some(name) = generate_name(word_count, existing, rng) {
return name;
}
}
// Practically unreachable — 198^5 ≈ 2.9 × 10^11 possibilities.
// Fall back to a numeric suffix as a safety net.
let n: u32 = rng.gen();
format!("worktree-{n}")
}
/// Generates a unique worktree branch name using the thread-local RNG.
/// This is the primary entry point for call sites.
pub fn generate_worktree_branch_name(existing: &HashSet<&str>) -> String {
generate_unique_name(existing, &mut rand::thread_rng())
}
#[cfg(test)]
#[path = "worktree_names_tests.rs"]
mod tests;