diff --git a/Cargo.lock b/Cargo.lock index 2f3ec6f8d..d81cc61e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1316,6 +1316,7 @@ dependencies = [ "filetime", "flate2", "fslock", + "globset", "globwalk 0.9.1", "home", "humantime", diff --git a/Cargo.toml b/Cargo.toml index e114c608f..11a7ccbdd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ eyre = "0.6.11" filetime = "0.2.23" flate2 = "1.0.28" fslock = "0.2.1" +globset = "0.4.14" globwalk = "0.9.1" home = "0.5.9" humantime = "2.1.0" diff --git a/src/cli/run.rs b/src/cli/run.rs index 1b3950817..31424907c 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -13,6 +13,7 @@ use console::Color; use demand::{DemandOption, Select}; use duct::IntoExecutablePath; use eyre::Result; +use globset::Glob; use globwalk::GlobWalkerBuilder; use itertools::Itertools; use once_cell::sync::Lazy; @@ -134,11 +135,21 @@ impl Run { } }) .flat_map(|args| args.split_first().map(|(t, a)| (t.clone(), a.to_vec()))) - .map(|(t, args)| match config.tasks_with_aliases().get(&t) { - Some(task) => Ok(task.clone().with_args(args.to_vec())), - None if t == "default" => self.prompt_for_task(config), - None => bail!("no task {} found", style::ered(t)), + .map(|(t, args)| { + let tasks = config.tasks_with_aliases().get_matching(&t)?; + if tasks.is_empty() { + ensure!(t == "default", "no task {} found", style::ered(t)); + + Ok(vec![self.prompt_for_task(config)?]) + } else { + Ok(tasks + .iter() + .cloned() + .map(|t| t.clone().with_args(args.to_vec())) + .collect()) + } }) + .flatten_ok() .collect() } @@ -521,6 +532,26 @@ fn get_color() -> Color { static COLOR_IDX: AtomicUsize = AtomicUsize::new(0); COLORS[COLOR_IDX.fetch_add(1, Ordering::Relaxed) % COLORS.len()] } +trait GetMatchingExt { + fn get_matching(&self, pat: &str) -> Result>; +} + +impl GetMatchingExt for std::collections::HashMap { + fn get_matching(&self, pat: &str) -> Result> { + let normalized = pat.split(':').collect::(); + let matcher = Glob::new(&normalized.to_string_lossy())?.compile_matcher(); + + Ok(self + .keys() + .filter(|k| { + let p: PathBuf = k.split(':').collect(); + + matcher.is_match(p) + }) + .flat_map(|k| self.get(k)) + .collect()) + } +} #[cfg(test)] mod tests {