feat: implement --noconfirm

This commit is contained in:
steven-omaha
2023-05-12 16:05:49 +02:00
parent f03805446e
commit 7d9f444ea6
10 changed files with 94 additions and 29 deletions
+16 -3
View File
@@ -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 {
+11 -2
View File
@@ -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<ExitStatus> {
fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result<ExitStatus> {
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<ExitStatus> {
fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result<ExitStatus> {
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}"));
}
@@ -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;
@@ -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;
@@ -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;
@@ -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<ExitStatus> {
fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result<ExitStatus> {
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<ExitStatus> {
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<ExitStatus> {
fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result<ExitStatus> {
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:?}]"))
}
+4
View File
@@ -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
}
@@ -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")
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<ExitStatus>,
F: Fn(&'a dyn Backend, &'a [Package], bool) -> Result<ExitStatus>,
{
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())
})?;
+26 -14
View File
@@ -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::<bool>("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::<bool>("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<()> {
+1 -1
View File
@@ -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() {