diff --git a/crates/pacdef_core/src/args.rs b/crates/pacdef_core/src/args.rs index 0c9c9e7..faefea8 100644 --- a/crates/pacdef_core/src/args.rs +++ b/crates/pacdef_core/src/args.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use anyhow::{Context, Result}; -use clap::{Arg, ArgMatches, Command}; +use clap::{Arg, ArgAction, ArgMatches, Command}; use path_absolutize::Absolutize; use crate::action::*; @@ -100,16 +100,22 @@ fn get_group_cmd() -> Command { fn get_package_cmd() -> Command { let sync = Command::new(SYNC) .about("install packages from all imported groups") - .visible_alias("sy"); + .visible_alias("sy") + .arg(build_noconfirm_arg()); + let clean = Command::new(CLEAN) .about("remove unmanaged packages") - .visible_alias("c"); + .visible_alias("c") + .arg(build_noconfirm_arg()); + let unmanaged = Command::new(UNMANAGED) .about("show explicitly installed packages not managed by pacdef") .visible_alias("u"); + let review = Command::new(REVIEW) .about("review unmanaged packages") .visible_alias("r"); + let search = Command::new(SEARCH) .visible_alias("se") .about("search for packages which match a provided regex") @@ -128,6 +134,13 @@ fn get_package_cmd() -> Command { .subcommands([clean, review, unmanaged, search, sync]) } +fn build_noconfirm_arg() -> Arg { + Arg::new("noconfirm") + .long("noconfirm") + .help("do not ask for any confirmation") + .action(ArgAction::SetTrue) +} + /// Get and parse the CLI arguments. #[must_use] pub fn get() -> clap::ArgMatches { diff --git a/crates/pacdef_core/src/backend/actual/arch.rs b/crates/pacdef_core/src/backend/actual/arch.rs index 4c0757a..e77afd6 100644 --- a/crates/pacdef_core/src/backend/actual/arch.rs +++ b/crates/pacdef_core/src/backend/actual/arch.rs @@ -21,6 +21,7 @@ const SECTION: Text = "arch"; const SWITCHES_INFO: Switches = &["--query", "--info"]; const SWITCHES_INSTALL: Switches = &["--sync"]; const SWITCHES_MAKE_DEPENDENCY: Switches = &["--database", "--asdeps"]; +const SWITCHES_NOCONFIRM: Switches = &["--noconfirm"]; const SWITCHES_REMOVE: Switches = &["--remove", "--recursive"]; const SUPPORTS_AS_DEPENDENCY: bool = true; @@ -49,11 +50,15 @@ impl Backend for Arch { } /// Install the specified packages. - fn install_packages(&self, packages: &[Package]) -> Result { + fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result { let mut cmd = Command::new(&self.binary); cmd.args(self.get_switches_install()); + if noconfirm { + cmd.args(self.get_switches_noconfirm()); + } + for p in packages { cmd.arg(format!("{p}")); } @@ -63,7 +68,7 @@ impl Backend for Arch { } /// Remove the specified packages. - fn remove_packages(&self, packages: &[Package]) -> Result { + fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result { let mut cmd = Command::new(&self.binary); cmd.args(self.get_switches_remove()); @@ -71,6 +76,10 @@ impl Backend for Arch { cmd.args(rm_args); } + if noconfirm { + cmd.args(self.get_switches_noconfirm()); + } + for p in packages { cmd.arg(format!("{p}")); } diff --git a/crates/pacdef_core/src/backend/actual/debian.rs b/crates/pacdef_core/src/backend/actual/debian.rs index 19c45cc..d62824f 100644 --- a/crates/pacdef_core/src/backend/actual/debian.rs +++ b/crates/pacdef_core/src/backend/actual/debian.rs @@ -20,6 +20,7 @@ const SECTION: Text = "debian"; const SWITCHES_INFO: Switches = &["show"]; const SWITCHES_INSTALL: Switches = &["install"]; const SWITCHES_MAKE_DEPENDENCY: Switches = &[]; // not needed +const SWITCHES_NOCONFIRM: Switches = &["--yes"]; const SWITCHES_REMOVE: Switches = &["remove"]; const SUPPORTS_AS_DEPENDENCY: bool = true; diff --git a/crates/pacdef_core/src/backend/actual/python.rs b/crates/pacdef_core/src/backend/actual/python.rs index b6def6c..2472514 100644 --- a/crates/pacdef_core/src/backend/actual/python.rs +++ b/crates/pacdef_core/src/backend/actual/python.rs @@ -20,6 +20,7 @@ const SECTION: Text = "python"; const SWITCHES_INFO: Switches = &["show"]; const SWITCHES_INSTALL: Switches = &["install"]; const SWITCHES_MAKE_DEPENDENCY: Switches = &[]; // not needed +const SWITCHES_NOCONFIRM: Switches = &[]; // not needed const SWITCHES_REMOVE: Switches = &["uninstall"]; const SUPPORTS_AS_DEPENDENCY: bool = false; diff --git a/crates/pacdef_core/src/backend/actual/rust.rs b/crates/pacdef_core/src/backend/actual/rust.rs index a95a9c6..1ffc656 100644 --- a/crates/pacdef_core/src/backend/actual/rust.rs +++ b/crates/pacdef_core/src/backend/actual/rust.rs @@ -20,6 +20,7 @@ const SECTION: Text = "rust"; const SWITCHES_INSTALL: Switches = &["install"]; const SWITCHES_INFO: Switches = &["search", "--limit", "1"]; const SWITCHES_MAKE_DEPENDENCY: Switches = &[]; +const SWITCHES_NOCONFIRM: Switches = &[]; // not needed const SWITCHES_REMOVE: Switches = &["uninstall"]; const SUPPORTS_AS_DEPENDENCY: bool = false; diff --git a/crates/pacdef_core/src/backend/backend_trait.rs b/crates/pacdef_core/src/backend/backend_trait.rs index 1ff7711..8e6be52 100644 --- a/crates/pacdef_core/src/backend/backend_trait.rs +++ b/crates/pacdef_core/src/backend/backend_trait.rs @@ -37,6 +37,10 @@ pub trait Backend: Debug { /// Get CLI switches for the package manager to install packages. fn get_switches_install(&self) -> Switches; + /// Get CLI switches for the package manager to perform `sync` and `clean` without + /// confirmation. + fn get_switches_noconfirm(&self) -> Switches; + /// Get CLI switches for the package manager to remove packages. fn get_switches_remove(&self) -> Switches; @@ -73,12 +77,18 @@ pub trait Backend: Debug { } /// Install the specified packages. - fn install_packages(&self, packages: &[Package]) -> Result { + fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result { let mut cmd = Command::new(self.get_binary()); cmd.args(self.get_switches_install()); + + if noconfirm { + cmd.args(self.get_switches_noconfirm()); + } + for p in packages { cmd.arg(format!("{p}")); } + cmd.status() .with_context(|| format!("running command {cmd:?}")) } @@ -92,20 +102,28 @@ pub trait Backend: Debug { fn make_dependency(&self, packages: &[Package]) -> Result { let mut cmd = Command::new(self.get_binary()); cmd.args(self.get_switches_make_dependency()); + for p in packages { cmd.arg(format!("{p}")); } + cmd.status() .with_context(|| format!("running command [{cmd:?}]")) } /// Remove the specified packages. - fn remove_packages(&self, packages: &[Package]) -> Result { + fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result { let mut cmd = Command::new(self.get_binary()); cmd.args(self.get_switches_remove()); + + if noconfirm { + cmd.args(self.get_switches_noconfirm()); + } + for p in packages { cmd.arg(format!("{p}")); } + cmd.status() .with_context(|| format!("running command [{cmd:?}]")) } diff --git a/crates/pacdef_core/src/backend/macros.rs b/crates/pacdef_core/src/backend/macros.rs index 5051519..c8bf2ca 100644 --- a/crates/pacdef_core/src/backend/macros.rs +++ b/crates/pacdef_core/src/backend/macros.rs @@ -19,6 +19,10 @@ macro_rules! impl_backend_constants { SWITCHES_INSTALL } + fn get_switches_noconfirm(&self) -> Switches { + SWITCHES_NOCONFIRM + } + fn get_switches_remove(&self) -> Switches { SWITCHES_REMOVE } diff --git a/crates/pacdef_core/src/backend/todo_per_backend.rs b/crates/pacdef_core/src/backend/todo_per_backend.rs index 2809a28..88ea796 100644 --- a/crates/pacdef_core/src/backend/todo_per_backend.rs +++ b/crates/pacdef_core/src/backend/todo_per_backend.rs @@ -35,31 +35,37 @@ impl ToDoPerBackend { self.0.iter().all(|(_, diff)| diff.is_empty()) } - pub(crate) fn install_missing_packages(&self) -> Result<()> { - self.handle_backend_command(Backend::install_packages, "install", "installing") - .context("installing packages") + pub(crate) fn install_missing_packages(&self, noconfirm: bool) -> Result<()> { + self.handle_backend_command( + Backend::install_packages, + noconfirm, + "install", + "installing", + ) + .context("installing packages") } - pub(crate) fn remove_unmanaged_packages(&self) -> Result<()> { - self.handle_backend_command(Backend::remove_packages, "remove", "removing") + pub(crate) fn remove_unmanaged_packages(&self, noconfirm: bool) -> Result<()> { + self.handle_backend_command(Backend::remove_packages, noconfirm, "remove", "removing") .context("removing packages") } fn handle_backend_command<'a, F>( &'a self, func: F, + noconfirm: bool, verb: &'_ str, verb_continuous: &'_ str, ) -> Result<()> where - F: Fn(&'a dyn Backend, &'a [Package]) -> Result, + F: Fn(&'a dyn Backend, &'a [Package], bool) -> Result, { for (backend, packages) in &self.0 { if packages.is_empty() { continue; } - let exit_status = func(&**backend, packages).with_context(|| { + let exit_status = func(&**backend, packages, noconfirm).with_context(|| { format!("{verb_continuous} packages for {}", backend.get_section()) })?; diff --git a/crates/pacdef_core/src/core.rs b/crates/pacdef_core/src/core.rs index 2528e27..7a1cb67 100644 --- a/crates/pacdef_core/src/core.rs +++ b/crates/pacdef_core/src/core.rs @@ -71,13 +71,13 @@ impl Pacdef { }, Some(("package", args)) => match args.subcommand() { - Some((CLEAN, _)) => self.clean_packages(), + Some((CLEAN, args)) => self.clean_packages(&args.clone()), Some((REVIEW, _)) => review::review(self.get_unmanaged_packages()?, self.groups) .context("review unmanaged packages"), Some((SEARCH, args)) => { search::search_packages(args, &self.groups).context("searching packages") } - Some((SYNC, _)) => self.install_packages(), + Some((SYNC, args)) => self.install_packages(&args.clone()), // TODO fix cloning Some((UNMANAGED, _)) => self.show_unmanaged_packages(), Some((_, _)) => panic!("{ACTION_NOT_MATCHED}"), @@ -130,7 +130,7 @@ impl Pacdef { } } - fn install_packages(&mut self) -> Result<()> { + fn install_packages(&mut self, args: &ArgMatches) -> Result<()> { let to_install = self.get_missing_packages()?; if to_install.nothing_to_do_for_all_backends() { @@ -141,12 +141,18 @@ impl Pacdef { println!("Would install the following packages:\n"); to_install.show().context("printing things to do")?; - println!(); - if !get_user_confirmation()? { - return Ok(()); - }; + let noconfirm = *args + .get_one::("noconfirm") + .expect("has a default value"); - to_install.install_missing_packages() + println!(); + if noconfirm { + println!("proceeding without confirmation"); + } else if !get_user_confirmation()? { + return Ok(()); + } + + to_install.install_missing_packages(noconfirm) } fn edit_group_files(&self, arg_matches: &ArgMatches) -> Result<()> { @@ -220,7 +226,7 @@ impl Pacdef { } } - fn clean_packages(mut self) -> Result<()> { + fn clean_packages(&mut self, args: &ArgMatches) -> Result<()> { let to_remove = self.get_unmanaged_packages()?; if to_remove.nothing_to_do_for_all_backends() { @@ -231,12 +237,18 @@ impl Pacdef { println!("Would remove the following packages:\n"); to_remove.show().context("printing things to do")?; - println!(); - if !get_user_confirmation()? { - return Ok(()); - }; + let noconfirm = *args + .get_one::("noconfirm") + .expect("has a default value"); - to_remove.remove_unmanaged_packages() + println!(); + if noconfirm { + println!("proceeding without confirmation"); + } else if !get_user_confirmation()? { + return Ok(()); + } + + to_remove.remove_unmanaged_packages(noconfirm) } fn show_group_content(&self, groups: &ArgMatches) -> Result<()> { diff --git a/crates/pacdef_core/src/review/strategy.rs b/crates/pacdef_core/src/review/strategy.rs index 395c9ed..4ae69ad 100644 --- a/crates/pacdef_core/src/review/strategy.rs +++ b/crates/pacdef_core/src/review/strategy.rs @@ -30,7 +30,7 @@ impl Strategy { pub(super) fn execute(self) -> Result<()> { if !self.delete.is_empty() { - ensure!(self.backend.remove_packages(&self.delete)?.success()); + ensure!(self.backend.remove_packages(&self.delete, false)?.success()); } if !self.as_dependency.is_empty() {