diff --git a/crates/pacdef/src/backend/actual/arch.rs b/crates/pacdef/src/backend/actual/arch.rs index 8615e24..c6b01a8 100644 --- a/crates/pacdef/src/backend/actual/arch.rs +++ b/crates/pacdef/src/backend/actual/arch.rs @@ -5,52 +5,37 @@ use alpm::Alpm; use alpm::PackageReason::Explicit; use anyhow::{Context, Result}; -use crate::backend::backend_trait::{Backend, Switches, Text}; -use crate::backend::macros::impl_backend_constants; use crate::cmd::run_external_command; -use crate::Package; +use crate::prelude::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Arch { pub binary: String, pub aur_rm_args: Vec, - pub packages: HashSet, } impl Arch { - pub fn new() -> Self { + pub fn new(config: &Config) -> Self { Self { - binary: BINARY.to_string(), - aur_rm_args: vec![], - packages: HashSet::new(), + binary: config.aur_helper.clone(), + aur_rm_args: config.aur_rm_args.clone(), } } } -impl Default for Arch { - fn default() -> Self { - Self::new() - } -} - -const BINARY: Text = "pacman"; -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; impl Backend for Arch { - impl_backend_constants!(); - - fn get_binary(&self) -> Text { - let r#box = self.binary.clone().into_boxed_str(); - Box::leak(r#box) + fn backend_info(&self) -> BackendInfo { + BackendInfo { + binary: self.binary.clone(), + section: "arch", + switches_info: &["--query", "--info"], + switches_install: &["--sync"], + switches_noconfirm: &["--noconfirm"], + switches_remove: &["--remove", "--recursive"], + switches_make_dependency: Some(&["--database", "--asdeps"]), + } } - fn get_all_installed_packages(&self) -> Result> { + fn get_all_installed_packages(&self) -> Result { let alpm_packages = get_all_installed_packages_from_alpm() .context("getting all installed packages from alpm")?; @@ -58,7 +43,7 @@ impl Backend for Arch { Ok(result) } - fn get_explicitly_installed_packages(&self) -> Result> { + fn get_explicitly_installed_packages(&self) -> Result { let alpm_packages = get_explicitly_installed_packages_from_alpm() .context("getting all installed packages from alpm")?; let result = convert_to_pacdef_packages(alpm_packages); @@ -67,12 +52,14 @@ impl Backend for Arch { /// Install the specified packages. fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> { + let backend_info = self.backend_info(); + let mut cmd = Command::new(&self.binary); - cmd.args(self.get_switches_install()); + cmd.args(backend_info.switches_install); if noconfirm { - cmd.args(self.get_switches_noconfirm()); + cmd.args(backend_info.switches_noconfirm); } for p in packages { @@ -84,13 +71,15 @@ impl Backend for Arch { /// Remove the specified packages. fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> { + let backend_info = self.backend_info(); + let mut cmd = Command::new(&self.binary); - cmd.args(self.get_switches_remove()); + cmd.args(backend_info.switches_remove); cmd.args(&self.aur_rm_args); if noconfirm { - cmd.args(self.get_switches_noconfirm()); + cmd.args(backend_info.switches_noconfirm); } for p in packages { @@ -124,7 +113,7 @@ fn get_explicitly_installed_packages_from_alpm() -> Result> { Ok(result) } -fn convert_to_pacdef_packages(packages: HashSet) -> HashSet { +fn convert_to_pacdef_packages(packages: HashSet) -> Packages { packages.into_iter().map(Package::from).collect() } diff --git a/crates/pacdef/src/backend/actual/debian.rs b/crates/pacdef/src/backend/actual/debian.rs index 70a556b..c207925 100644 --- a/crates/pacdef/src/backend/actual/debian.rs +++ b/crates/pacdef/src/backend/actual/debian.rs @@ -1,24 +1,16 @@ -use std::collections::HashSet; - use anyhow::Result; use rust_apt::cache::PackageSort; use rust_apt::new_cache; -use crate::backend::backend_trait::{Backend, Switches, Text}; -use crate::backend::macros::impl_backend_constants; use crate::backend::root::build_base_command_with_privileges; use crate::cmd::run_external_command; -use crate::Package; +use crate::prelude::*; -#[derive(Debug, Clone)] -pub struct Debian { - pub packages: HashSet, -} +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Debian {} impl Debian { pub fn new() -> Self { - Self { - packages: HashSet::new(), - } + Self {} } } impl Default for Debian { @@ -27,36 +19,35 @@ impl Default for Debian { } } -const BINARY: Text = "apt"; -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; - impl Backend for Debian { - impl_backend_constants!(); + fn backend_info(&self) -> BackendInfo { + BackendInfo { + binary: "apt".to_string(), + section: "debian", + switches_info: &["show"], + switches_install: &["install"], + switches_noconfirm: &["--yes"], + switches_remove: &["remove"], + switches_make_dependency: Some(&[]), + } + } - fn get_all_installed_packages(&self) -> Result> { + fn get_all_installed_packages(&self) -> Result { let cache = new_cache!()?; let sort = PackageSort::default().installed(); - let mut result = HashSet::new(); + let mut result = Packages::new(); for pkg in cache.packages(&sort)? { result.insert(Package::from(pkg.name().to_string())); } Ok(result) } - fn get_explicitly_installed_packages(&self) -> Result> { + fn get_explicitly_installed_packages(&self) -> Result { let cache = new_cache!()?; let sort = PackageSort::default().installed().manually_installed(); - let mut result = HashSet::new(); + let mut result = Packages::new(); for pkg in cache.packages(&sort)? { result.insert(Package::from(pkg.name().to_string())); } @@ -75,12 +66,14 @@ impl Backend for Debian { /// Install the specified packages. fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> { - let mut cmd = build_base_command_with_privileges(self.get_binary()); + let backend_info = self.backend_info(); - cmd.args(self.get_switches_install()); + let mut cmd = build_base_command_with_privileges(&backend_info.binary); + + cmd.args(backend_info.switches_install); if noconfirm { - cmd.args(self.get_switches_noconfirm()); + cmd.args(backend_info.switches_noconfirm); } for p in packages { @@ -92,11 +85,13 @@ impl Backend for Debian { /// Remove the specified packages. fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> { - let mut cmd = build_base_command_with_privileges(self.get_binary()); - cmd.args(self.get_switches_remove()); + let backend_info = self.backend_info(); + + let mut cmd = build_base_command_with_privileges(&backend_info.binary); + cmd.args(backend_info.switches_remove); if noconfirm { - cmd.args(self.get_switches_noconfirm()); + cmd.args(backend_info.switches_noconfirm); } for p in packages { diff --git a/crates/pacdef/src/backend/actual/fedora.rs b/crates/pacdef/src/backend/actual/fedora.rs index 34a7322..b91ae72 100644 --- a/crates/pacdef/src/backend/actual/fedora.rs +++ b/crates/pacdef/src/backend/actual/fedora.rs @@ -1,22 +1,15 @@ -use std::collections::HashSet; use std::process::Command; use anyhow::Result; -use crate::backend::backend_trait::{Backend, Switches, Text}; -use crate::backend::macros::impl_backend_constants; use crate::cmd::run_external_command; -use crate::Package; +use crate::prelude::*; -#[derive(Debug, Clone)] -pub struct Fedora { - pub packages: HashSet, -} +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Fedora {} impl Fedora { pub fn new() -> Self { - Self { - packages: HashSet::new(), - } + Self {} } } impl Default for Fedora { @@ -25,16 +18,9 @@ impl Default for Fedora { } } -const BINARY: Text = "dnf"; -const SECTION: Text = "fedora"; - -const SWITCHES_INFO: Switches = &["info"]; -const SWITCHES_INSTALL: Switches = &["install"]; -const SWITCHES_MAKE_DEPENDENCY: Switches = &[]; -const SWITCHES_NOCONFIRM: Switches = &["--assumeyes"]; -const SWITCHES_REMOVE: Switches = &["remove"]; - -const SUPPORTS_AS_DEPENDENCY: bool = false; +/// These repositories are ignored when storing the packages +/// as these are present by default on any sane fedora system +const DEFAULT_REPOS: [&str; 5] = ["koji", "fedora", "updates", "anaconda", "@"]; /// These switches are responsible for /// getting the packages explicitly installed by the user @@ -54,15 +40,21 @@ const SWITCHES_FETCH_GLOBAL: Switches = &[ "%{from_repo}/%{name}", ]; -/// These repositories are ignored when storing the packages -/// as these are present by default on any sane fedora system -const DEFAULT_REPOS: [&str; 5] = ["koji", "fedora", "updates", "anaconda", "@"]; - impl Backend for Fedora { - impl_backend_constants!(); + fn backend_info(&self) -> BackendInfo { + BackendInfo { + binary: "dnf".to_string(), + section: "fedora", + switches_info: &["info"], + switches_install: &["install"], + switches_noconfirm: &["--assumeyes"], + switches_remove: &["remove"], + switches_make_dependency: None, + } + } - fn get_all_installed_packages(&self) -> Result> { - let mut cmd = Command::new(self.get_binary()); + fn get_all_installed_packages(&self) -> Result { + let mut cmd = Command::new(self.backend_info().binary); cmd.args(SWITCHES_FETCH_GLOBAL); let output = String::from_utf8(cmd.output()?.stdout)?; @@ -71,8 +63,8 @@ impl Backend for Fedora { Ok(packages) } - fn get_explicitly_installed_packages(&self) -> Result> { - let mut cmd = Command::new(self.get_binary()); + fn get_explicitly_installed_packages(&self) -> Result { + let mut cmd = Command::new(self.backend_info().binary); cmd.args(SWITCHES_FETCH_USER); let output = String::from_utf8(cmd.output()?.stdout)?; @@ -83,12 +75,14 @@ impl Backend for Fedora { /// Install the specified packages. fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> { + let backend_info = self.backend_info(); + let mut cmd = Command::new("sudo"); - cmd.arg(self.get_binary()); - cmd.args(self.get_switches_install()); + cmd.arg(backend_info.binary); + cmd.args(backend_info.switches_install); if noconfirm { - cmd.args(self.get_switches_noconfirm()); + cmd.args(backend_info.switches_noconfirm); } for p in packages { @@ -107,12 +101,14 @@ impl Backend for Fedora { /// Show information from package manager for package. fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> { + let backend_info = self.backend_info(); + let mut cmd = Command::new("sudo"); - cmd.arg(self.get_binary()); - cmd.args(self.get_switches_remove()); + cmd.arg(backend_info.binary); + cmd.args(backend_info.switches_remove); if noconfirm { - cmd.args(self.get_switches_noconfirm()); + cmd.args(backend_info.switches_noconfirm); } for p in packages { @@ -123,8 +119,10 @@ impl Backend for Fedora { } fn show_package_info(&self, package: &Package) -> Result<()> { - let mut cmd = Command::new(self.get_binary()); - cmd.args(self.get_switches_info()); + let backend_info = self.backend_info(); + + let mut cmd = Command::new(backend_info.binary); + cmd.args(backend_info.switches_info); cmd.arg(&package.name); run_external_command(cmd) diff --git a/crates/pacdef/src/backend/actual/flatpak.rs b/crates/pacdef/src/backend/actual/flatpak.rs index b33c4dc..6a90743 100644 --- a/crates/pacdef/src/backend/actual/flatpak.rs +++ b/crates/pacdef/src/backend/actual/flatpak.rs @@ -1,23 +1,18 @@ -use std::collections::HashSet; use std::process::Command; use anyhow::Result; -use crate::backend::backend_trait::{Backend, Switches, Text}; -use crate::backend::macros::impl_backend_constants; use crate::cmd::run_external_command; -use crate::Package; +use crate::prelude::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Flatpak { - pub packages: HashSet, pub systemwide: bool, } impl Flatpak { - pub fn new() -> Self { + pub fn new(config: &Config) -> Self { Self { - packages: HashSet::new(), - systemwide: true, + systemwide: config.flatpak_systemwide, } } @@ -29,8 +24,8 @@ impl Flatpak { } } - fn get_installed_packages(&self, include_implicit: bool) -> Result> { - let mut cmd = Command::new(BINARY); + fn get_installed_packages(&self, include_implicit: bool) -> Result { + let mut cmd = Command::new(self.backend_info().binary); cmd.args(["list", "--columns=application"]); if !include_implicit { cmd.arg("--app"); @@ -40,48 +35,41 @@ impl Flatpak { } let output = String::from_utf8(cmd.output()?.stdout)?; - Ok(output - .lines() - .map(Package::from) - .collect::>()) + Ok(output.lines().map(Package::from).collect::()) } } -impl Default for Flatpak { - fn default() -> Self { - Self::new() - } -} - -const BINARY: Text = "flatpak"; -const SECTION: Text = "flatpak"; - -const SWITCHES_INSTALL: Switches = &["install"]; -const SWITCHES_INFO: Switches = &["info"]; -const SWITCHES_MAKE_DEPENDENCY: Switches = &[]; -const SWITCHES_NOCONFIRM: Switches = &["--assumeyes"]; -const SWITCHES_REMOVE: Switches = &["uninstall"]; - -const SUPPORTS_AS_DEPENDENCY: bool = false; impl Backend for Flatpak { - impl_backend_constants!(); + fn backend_info(&self) -> BackendInfo { + BackendInfo { + binary: "flatpak".to_string(), + section: "flatpak", + switches_info: &["info"], + switches_install: &["install"], + switches_noconfirm: &["--assumeyes"], + switches_remove: &["uninstall"], + switches_make_dependency: None, + } + } - fn get_all_installed_packages(&self) -> Result> { + fn get_all_installed_packages(&self) -> Result { self.get_installed_packages(true) } - fn get_explicitly_installed_packages(&self) -> Result> { + fn get_explicitly_installed_packages(&self) -> Result { self.get_installed_packages(false) } /// Install the specified packages. fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> { - let mut cmd = Command::new(self.get_binary()); - cmd.args(self.get_switches_install()); + let backend_info = self.backend_info(); + + let mut cmd = Command::new(backend_info.binary); + cmd.args(backend_info.switches_install); cmd.args(self.get_switches_runtime()); if noconfirm { - cmd.args(self.get_switches_noconfirm()); + cmd.args(backend_info.switches_noconfirm); } for p in packages { @@ -92,17 +80,19 @@ impl Backend for Flatpak { } fn make_dependency(&self, _: &[Package]) -> Result<()> { - panic!("not supported by {}", BINARY) + panic!("not supported by {}", self.backend_info().binary) } /// Remove the specified packages. fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> { - let mut cmd = Command::new(self.get_binary()); - cmd.args(self.get_switches_remove()); + let backend_info = self.backend_info(); + + let mut cmd = Command::new(backend_info.binary); + cmd.args(backend_info.switches_remove); cmd.args(self.get_switches_runtime()); if noconfirm { - cmd.args(self.get_switches_noconfirm()); + cmd.args(backend_info.switches_noconfirm); } for p in packages { @@ -114,8 +104,10 @@ impl Backend for Flatpak { /// Show information from package manager for package. fn show_package_info(&self, package: &Package) -> Result<()> { - let mut cmd = Command::new(self.get_binary()); - cmd.args(self.get_switches_info()); + let backend_info = self.backend_info(); + + let mut cmd = Command::new(backend_info.binary); + cmd.args(backend_info.switches_info); cmd.args(self.get_switches_runtime()); cmd.arg(format!("{package}")); diff --git a/crates/pacdef/src/backend/actual/python.rs b/crates/pacdef/src/backend/actual/python.rs index c2ab322..e286443 100644 --- a/crates/pacdef/src/backend/actual/python.rs +++ b/crates/pacdef/src/backend/actual/python.rs @@ -1,13 +1,10 @@ -use std::collections::HashSet; use std::process::Command; use anyhow::Context; use anyhow::Result; use serde_json::Value; -use crate::backend::backend_trait::{Backend, Switches, Text}; -use crate::backend::macros::impl_backend_constants; -use crate::Package; +use crate::prelude::*; macro_rules! ERROR{ ($bin:expr) => { @@ -15,81 +12,68 @@ macro_rules! ERROR{ }; } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Python { pub binary: String, - pub packages: HashSet, } impl Python { - pub fn new() -> Self { + pub fn new(config: &Config) -> Self { Self { - binary: BINARY.to_string(), - packages: HashSet::new(), + binary: config.pip_binary.to_string(), } } fn get_switches_runtime(&self) -> Switches { - match self.get_binary() { + match self.backend_info().binary.as_str() { "pip" => &["list", "--format", "json", "--not-required", "--user"], "pipx" => &["list", "--json"], - _ => ERROR!(self.get_binary()), + _ => ERROR!(self.backend_info().binary), } } fn get_switches_explicit(&self) -> Switches { - match self.get_binary() { + match self.backend_info().binary.as_str() { "pip" => &["list", "--format", "json", "--user"], "pipx" => &["list", "--json"], - _ => ERROR!(self.get_binary()), + _ => ERROR!(self.backend_info().binary), } } - fn extract_packages(&self, output: Value) -> Result> { - match self.get_binary() { + fn extract_packages(&self, output: Value) -> Result { + match self.backend_info().binary.as_str() { "pip" => extract_pacdef_packages(output), "pipx" => extract_pacdef_packages_pipx(output), - _ => ERROR!(self.get_binary()), + _ => ERROR!(self.backend_info().binary), } } } -impl Default for Python { - fn default() -> Self { - Self::new() - } -} - -const BINARY: Text = "pip"; -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; impl Backend for Python { - impl_backend_constants!(); - - fn get_binary(&self) -> Text { - let r#box = self.binary.clone().into_boxed_str(); - Box::leak(r#box) + fn backend_info(&self) -> BackendInfo { + BackendInfo { + binary: self.binary.clone(), + section: "python", + switches_info: &["show"], + switches_install: &["install"], + switches_noconfirm: &[], + switches_remove: &["uninstall"], + switches_make_dependency: None, + } } - fn get_all_installed_packages(&self) -> Result> { - let mut cmd = Command::new(self.get_binary()); + fn get_all_installed_packages(&self) -> Result { + let mut cmd = Command::new(self.backend_info().binary); let output = run_pip_command(&mut cmd, self.get_switches_runtime())?; self.extract_packages(output) } - fn get_explicitly_installed_packages(&self) -> Result> { - let mut cmd = Command::new(self.get_binary()); + fn get_explicitly_installed_packages(&self) -> Result { + let mut cmd = Command::new(self.backend_info().binary); let output = run_pip_command(&mut cmd, self.get_switches_explicit())?; self.extract_packages(output) } fn make_dependency(&self, _packages: &[Package]) -> Result<()> { - panic!("not supported by {}", BINARY) + panic!("not supported by {}", self.binary) } } @@ -100,7 +84,7 @@ fn run_pip_command(cmd: &mut Command, args: &[&str]) -> Result { Ok(val) } -fn extract_pacdef_packages(value: Value) -> Result> { +fn extract_pacdef_packages(value: Value) -> Result { let result = value .as_array() .context("getting inner json array")? @@ -111,7 +95,7 @@ fn extract_pacdef_packages(value: Value) -> Result> { Ok(result) } -fn extract_pacdef_packages_pipx(value: Value) -> Result> { +fn extract_pacdef_packages_pipx(value: Value) -> Result { let result = value["venvs"] .as_object() .context("getting inner json object")? diff --git a/crates/pacdef/src/backend/actual/rust.rs b/crates/pacdef/src/backend/actual/rust.rs index 9bf9612..c70459c 100644 --- a/crates/pacdef/src/backend/actual/rust.rs +++ b/crates/pacdef/src/backend/actual/rust.rs @@ -1,4 +1,3 @@ -use std::collections::HashSet; use std::fs::read_to_string; use std::io::ErrorKind::NotFound; use std::path::PathBuf; @@ -6,19 +5,13 @@ use std::path::PathBuf; use anyhow::{bail, Context, Result}; use serde_json::Value; -use crate::backend::backend_trait::{Backend, Switches, Text}; -use crate::backend::macros::impl_backend_constants; -use crate::Package; +use crate::prelude::*; -#[derive(Debug, Clone)] -pub struct Rust { - pub packages: HashSet, -} +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Rust {} impl Rust { pub fn new() -> Self { - Self { - packages: HashSet::new(), - } + Self {} } } impl Default for Rust { @@ -27,28 +20,27 @@ impl Default for Rust { } } -const BINARY: Text = "cargo"; -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; - impl Backend for Rust { - impl_backend_constants!(); + fn backend_info(&self) -> BackendInfo { + BackendInfo { + binary: "cargo".to_string(), + section: "rust", + switches_info: &["search", "--limit", "1"], + switches_install: &["install"], + switches_noconfirm: &[], + switches_remove: &["uninstall"], + switches_make_dependency: None, + } + } - fn get_all_installed_packages(&self) -> Result> { + fn get_all_installed_packages(&self) -> Result { let file = get_crates_file().context("getting path to crates file")?; let content = match read_to_string(file) { Ok(string) => string, Err(err) if err.kind() == NotFound => { log::warn!("no crates file found for cargo. Assuming no crates installed yet."); - return Ok(HashSet::new()); + return Ok(Packages::new()); } Err(err) => bail!(err), }; @@ -58,18 +50,18 @@ impl Backend for Rust { extract_packages(&json).context("extracting packages from crates file") } - fn get_explicitly_installed_packages(&self) -> Result> { + fn get_explicitly_installed_packages(&self) -> Result { self.get_all_installed_packages() .context("getting all installed packages") } fn make_dependency(&self, _: &[Package]) -> Result<()> { - panic!("not supported by {}", BINARY) + panic!("not supported by {}", self.backend_info().binary) } } -fn extract_packages(json: &Value) -> Result> { - let result: HashSet<_> = json +fn extract_packages(json: &Value) -> Result { + let result: Packages = json .get("installs") .context("get 'installs' field from json")? .as_object() @@ -83,6 +75,7 @@ fn extract_packages(json: &Value) -> Result> { }) .map(|name| Package::try_from(name).expect("name is valid")) .collect(); + Ok(result) } diff --git a/crates/pacdef/src/backend/actual/rustup/mod.rs b/crates/pacdef/src/backend/actual/rustup/mod.rs index f40511e..35bca2f 100644 --- a/crates/pacdef/src/backend/actual/rustup/mod.rs +++ b/crates/pacdef/src/backend/actual/rustup/mod.rs @@ -1,12 +1,9 @@ mod helpers; mod types; -use crate::backend::backend_trait::{Backend, Switches, Text}; -use crate::backend::macros::impl_backend_constants; use crate::cmd::run_external_command; -use crate::Package; +use crate::prelude::*; use anyhow::{bail, Context, Result}; -use std::collections::HashSet; use std::process::Command; use self::helpers::{ @@ -14,15 +11,11 @@ use self::helpers::{ }; use self::types::{Repotype, RustupPackage}; -#[derive(Debug, Clone)] -pub struct Rustup { - pub packages: HashSet, -} +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Rustup {} impl Rustup { pub fn new() -> Self { - Self { - packages: HashSet::new(), - } + Self {} } } impl Default for Rustup { @@ -31,38 +24,37 @@ impl Default for Rustup { } } -const BINARY: Text = "rustup"; -const SECTION: Text = "rustup"; - -const SWITCHES_INSTALL: Switches = &["component", "add"]; -const SWITCHES_INFO: Switches = &["component", "list", "--installed"]; -const SWITCHES_MAKE_DEPENDENCY: Switches = &[]; -const SWITCHES_NOCONFIRM: Switches = &[]; -const SWITCHES_REMOVE: Switches = &["component", "remove"]; - -const SUPPORTS_AS_DEPENDENCY: bool = false; - impl Backend for Rustup { - impl_backend_constants!(); + fn backend_info(&self) -> BackendInfo { + BackendInfo { + binary: "rustup".to_string(), + section: "rustup", + switches_install: &["component", "add"], + switches_info: &["component", "list", "--installed"], + switches_noconfirm: &[], + switches_remove: &["component", "remove"], + switches_make_dependency: None, + } + } - fn get_all_installed_packages(&self) -> Result> { + fn get_all_installed_packages(&self) -> Result { let toolchains_vec = self .run_toolchain_command(Repotype::Toolchain.get_info_switches()) .context("Getting installed toolchains")?; - let toolchains: HashSet = toolchains_vec + let toolchains: Packages = toolchains_vec .iter() .map(|name| ["toolchain", name].join("/").into()) .collect(); - let components: HashSet = self + let components: Packages = self .run_component_command(Repotype::Component.get_info_switches(), &toolchains_vec) .context("Getting installed components")? .iter() .map(|name| ["component", name].join("/").into()) .collect(); - let mut packages = HashSet::new(); + let mut packages = Packages::new(); packages.extend(toolchains); packages.extend(components); @@ -70,13 +62,13 @@ impl Backend for Rustup { Ok(packages) } - fn get_explicitly_installed_packages(&self) -> Result> { + fn get_explicitly_installed_packages(&self) -> Result { self.get_all_installed_packages() .context("Getting all installed packages") } fn make_dependency(&self, _: &[Package]) -> Result<()> { - panic!("Not supported by {}", self.get_binary()) + panic!("Not supported by {}", self.backend_info().binary) } fn install_packages(&self, packages: &[Package], _: bool) -> Result<()> { @@ -110,7 +102,7 @@ impl Rustup { let mut val = Vec::new(); for toolchain in toolchains { - let mut cmd = Command::new(self.get_binary()); + let mut cmd = Command::new(self.backend_info().binary); cmd.args(args).arg(toolchain); let output = String::from_utf8(cmd.output()?.stdout)?; @@ -124,7 +116,7 @@ impl Rustup { } fn run_toolchain_command(&self, args: &[&str]) -> Result> { - let mut cmd = Command::new(self.get_binary()); + let mut cmd = Command::new(self.backend_info().binary); cmd.args(args); let output = String::from_utf8(cmd.output()?.stdout)?; @@ -146,7 +138,7 @@ impl Rustup { if toolchains.is_empty() { return Ok(()); } - let mut cmd = Command::new(self.get_binary()); + let mut cmd = Command::new(self.backend_info().binary); cmd.args(Repotype::Toolchain.get_install_switches()); for toolchain in toolchains { @@ -166,7 +158,7 @@ impl Rustup { let components_by_toolchain = group_components_by_toolchains(components); for components_for_one_toolchain in components_by_toolchain { - let mut cmd = Command::new(self.get_binary()); + let mut cmd = Command::new(self.backend_info().binary); cmd.args(Repotype::Component.get_install_switches()); let the_toolchain = &components_for_one_toolchain @@ -195,7 +187,7 @@ impl Rustup { fn remove_toolchains(&self, toolchains: Vec) -> Result> { let mut removed_toolchains = vec![]; if !toolchains.is_empty() { - let mut cmd = Command::new(self.get_binary()); + let mut cmd = Command::new(self.backend_info().binary); cmd.args(Repotype::Toolchain.get_remove_switches()); for toolchain_package in &toolchains { @@ -216,7 +208,7 @@ impl Rustup { removed_toolchains: Vec, ) -> Result<()> { for component_package in components { - let mut cmd = Command::new(self.get_binary()); + let mut cmd = Command::new(self.backend_info().binary); cmd.args(Repotype::Component.get_remove_switches()); if toolchain_of_component_was_already_removed(&removed_toolchains, &component_package) { diff --git a/crates/pacdef/src/backend/actual/rustup/types.rs b/crates/pacdef/src/backend/actual/rustup/types.rs index b0a8433..4aa1272 100644 --- a/crates/pacdef/src/backend/actual/rustup/types.rs +++ b/crates/pacdef/src/backend/actual/rustup/types.rs @@ -1,6 +1,6 @@ use anyhow::{bail, Context, Result}; -use crate::{backend::backend_trait::Switches, Package}; +use crate::prelude::*; #[derive(Debug)] pub enum Repotype { diff --git a/crates/pacdef/src/backend/actual/void.rs b/crates/pacdef/src/backend/actual/void.rs index f84784c..023f524 100644 --- a/crates/pacdef/src/backend/actual/void.rs +++ b/crates/pacdef/src/backend/actual/void.rs @@ -1,24 +1,17 @@ -use std::collections::HashSet; use std::process::Command; use anyhow::Result; use regex::Regex; -use crate::backend::backend_trait::{Backend, Switches, Text}; -use crate::backend::macros::impl_backend_constants; use crate::backend::root::build_base_command_with_privileges; use crate::cmd::run_external_command; -use crate::Package; +use crate::prelude::*; -#[derive(Debug, Clone)] -pub struct Void { - pub packages: HashSet, -} +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Void {} impl Void { pub fn new() -> Self { - Self { - packages: HashSet::new(), - } + Self {} } } impl Default for Void { @@ -27,25 +20,25 @@ impl Default for Void { } } -const BINARY: Text = "xbps-install"; const INSTALL_BINARY: Text = "xbps-install"; const REMOVE_BINARY: Text = "xbps-remove"; const QUERY_BINARY: Text = "xbps-query"; const PKGDB_BINARY: Text = "xbps-pkgdb"; -const SECTION: Text = "void"; - -const SWITCHES_INFO: Switches = &[]; -const SWITCHES_INSTALL: Switches = &["-S"]; -const SWITCHES_MAKE_DEPENDENCY: Switches = &["-m", "auto"]; -const SWITCHES_NOCONFIRM: Switches = &["-y"]; -const SWITCHES_REMOVE: Switches = &["-R"]; - -const SUPPORTS_AS_DEPENDENCY: bool = true; impl Backend for Void { - impl_backend_constants!(); + fn backend_info(&self) -> BackendInfo { + BackendInfo { + binary: "xbps-install".to_string(), + section: "void", + switches_info: &[], + switches_install: &["-S"], + switches_noconfirm: &["-y"], + switches_remove: &["-R"], + switches_make_dependency: Some(&["-m", "auto"]), + } + } - fn get_all_installed_packages(&self) -> Result> { + fn get_all_installed_packages(&self) -> Result { // Removes the package status and description from output let re_str_1 = r"^ii |^uu |^hr |^\?\? | .*"; // Removes the package version from output @@ -67,7 +60,7 @@ impl Backend for Void { Ok(packages) } - fn get_explicitly_installed_packages(&self) -> Result> { + fn get_explicitly_installed_packages(&self) -> Result { // Removes the package version from output let re_str = r"-[^-]*$"; let re = Regex::new(re_str)?; @@ -87,11 +80,13 @@ impl Backend for Void { /// Install the specified packages. fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> { + let backend_info = self.backend_info(); + let mut cmd = build_base_command_with_privileges(INSTALL_BINARY); - cmd.args(self.get_switches_install()); + cmd.args(backend_info.switches_install); if noconfirm { - cmd.args(self.get_switches_noconfirm()); + cmd.args(backend_info.switches_noconfirm); } for p in packages { @@ -102,11 +97,13 @@ impl Backend for Void { } fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> { + let backend_info = self.backend_info(); + let mut cmd = build_base_command_with_privileges(REMOVE_BINARY); - cmd.args(self.get_switches_remove()); + cmd.args(backend_info.switches_remove); if noconfirm { - cmd.args(self.get_switches_noconfirm()); + cmd.args(backend_info.switches_noconfirm); } for p in packages { @@ -117,8 +114,14 @@ impl Backend for Void { } fn make_dependency(&self, packages: &[Package]) -> Result<()> { + let backend_info = self.backend_info(); + let mut cmd = build_base_command_with_privileges(PKGDB_BINARY); - cmd.args(self.get_switches_make_dependency()); + cmd.args( + backend_info + .switches_make_dependency + .expect("void should support make make dependency"), + ); for p in packages { cmd.arg(format!("{p}")); @@ -129,8 +132,10 @@ impl Backend for Void { /// Show information from package manager for package. fn show_package_info(&self, package: &Package) -> Result<()> { + let backend_info = self.backend_info(); + let mut cmd = Command::new(QUERY_BINARY); - cmd.args(self.get_switches_info()); + cmd.args(backend_info.switches_info); cmd.arg(format!("{package}")); run_external_command(cmd) diff --git a/crates/pacdef/src/backend/backend_trait.rs b/crates/pacdef/src/backend/backend_trait.rs index 859dfc2..7f9f203 100644 --- a/crates/pacdef/src/backend/backend_trait.rs +++ b/crates/pacdef/src/backend/backend_trait.rs @@ -1,68 +1,54 @@ use std::cmp::{Eq, Ord}; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::hash::Hash; use std::process::Command; -use anyhow::{Context, Result}; +use anyhow::Result; use crate::cmd::run_external_command; -use crate::{Group, Groups, Package}; +use crate::prelude::*; pub type Switches = &'static [&'static str]; pub type Text = &'static str; +// A bundle of small of bits of info associated with a backend. +pub struct BackendInfo { + /// The binary name when calling the backend. + pub binary: String, + /// The name of the section in the group files. + pub section: Text, + /// CLI switches for the package manager to show information for + /// packages. + pub switches_info: Switches, + /// CLI switches for the package manager to install packages. + pub switches_install: Switches, + /// CLI switches for the package manager to perform `sync` and `clean` without + /// confirmation. + pub switches_noconfirm: Switches, + /// CLI switches for the package manager to remove packages. + pub switches_remove: Switches, + /// CLI switches for the package manager to mark packages as + /// dependency. This is not supported by all package managers. + pub switches_make_dependency: Option, +} + /// The trait of a struct that is used as a backend. #[enum_dispatch::enum_dispatch] pub trait Backend { - /// Return the actual binary. Iff the backend supports different - /// binaries, you will need to overwrite this implementation to return - /// the binary that was loaded at runtime. See - /// [`Backend::get_binary_default()`]. - fn get_binary(&self) -> Text { - self.get_binary_default() + /// Return the [`BackendInfo`] associated with this backend. + fn backend_info(&self) -> BackendInfo; + + fn supports_as_dependency(&self) -> bool { + self.backend_info().switches_make_dependency.is_some() } - /// Return the default binary as defined in the constant of the module. - /// See [`Backend::get_binary()`]. - fn get_binary_default(&self) -> Text; - - /// Get the name of the section in the group files. - fn get_section(&self) -> Text; - - /// Get CLI switches for the package manager to show information for - /// packages. - fn get_switches_info(&self) -> Switches; - - /// 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; - - /// Get CLI switches for the package manager to mark packages as - /// dependency. This is not supported by all package managers. See - /// [`Backend::supports_as_dependency()`]. - fn get_switches_make_dependency(&self) -> Switches; - - /// Load all packages from a set of groups. The backend will visit all groups, - /// find its own section, and clone all packages into its own struct. - fn load(&mut self, groups: &Groups); - - /// Get all managed packages for this backend, i.e. all packages - /// under the corresponding section in all group files. - fn get_managed_packages(&self) -> &HashSet; - /// Get all packages that are installed in the system. /// /// # Errors /// /// This function shall return an error if the installed packages cannot be /// determined. - fn get_all_installed_packages(&self) -> Result>; + fn get_all_installed_packages(&self) -> Result; /// Get all packages that were installed in the system explicitly. /// @@ -70,13 +56,17 @@ pub trait Backend { /// /// This function shall return an error if the explicitly installed packages /// cannot be determined. - fn get_explicitly_installed_packages(&self) -> Result>; + fn get_explicitly_installed_packages(&self) -> Result; /// Assign each of the packages to an individual group by editing the /// group files. + /// + /// # Errors + /// + /// Returns an Error if any of the groups fails to save their given packages. fn assign_group(&self, to_assign: Vec<(Package, Group)>) -> Result<()> { let group_package_map = to_hashmap(to_assign); - let section_header = format!("[{}]", self.get_section()); + let section_header = format!("[{}]", self.backend_info().section); for (group, packages) in group_package_map { group.save_packages(§ion_header, &packages)?; @@ -93,11 +83,13 @@ pub trait Backend { /// This function will return an error if the package manager cannot be run or it /// returns an error. fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> { - let mut cmd = Command::new(self.get_binary()); - cmd.args(self.get_switches_install()); + let backend_info = self.backend_info(); + + let mut cmd = Command::new(self.backend_info().binary); + cmd.args(backend_info.switches_install); if noconfirm { - cmd.args(self.get_switches_noconfirm()); + cmd.args(backend_info.switches_noconfirm); } for p in packages { @@ -113,9 +105,18 @@ pub trait Backend { /// # Panics /// /// This method shall panic when the backend does not support dependent packages. + /// + /// # Errors + /// + /// Returns an error if the external command fails. fn make_dependency(&self, packages: &[Package]) -> Result<()> { - let mut cmd = Command::new(self.get_binary()); - cmd.args(self.get_switches_make_dependency()); + let backend_info = self.backend_info(); + + let mut cmd = Command::new(backend_info.binary); + + if let Some(switches_make_dependency) = backend_info.switches_make_dependency { + cmd.args(switches_make_dependency); + } for p in packages { cmd.arg(format!("{p}")); @@ -126,12 +127,17 @@ pub trait Backend { /// Remove the specified packages. /// + /// # Errors + /// + /// Returns an error if the external command fails. fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> { - let mut cmd = Command::new(self.get_binary()); - cmd.args(self.get_switches_remove()); + let backend_info = self.backend_info(); + + let mut cmd = Command::new(backend_info.binary); + cmd.args(backend_info.switches_remove); if noconfirm { - cmd.args(self.get_switches_noconfirm()); + cmd.args(backend_info.switches_noconfirm); } for p in packages { @@ -141,39 +147,20 @@ pub trait Backend { run_external_command(cmd) } - /// Get missing packages, sorted alphabetically. - fn get_missing_packages_sorted(&self) -> Result> { - let installed = self - .get_all_installed_packages() - .context("could not get installed packages")?; - let managed = self.get_managed_packages(); - let mut diff: Vec<_> = managed.difference(&installed).cloned().collect(); - diff.sort_unstable(); - Ok(diff) - } - /// Show information from package manager for package. + /// + /// # Errors + /// + /// Returns an error if the external command fails. fn show_package_info(&self, package: &Package) -> Result<()> { - let mut cmd = Command::new(self.get_binary()); - cmd.args(self.get_switches_info()); + let backend_info = self.backend_info(); + + let mut cmd = Command::new(backend_info.binary); + cmd.args(backend_info.switches_info); cmd.arg(format!("{package}")); run_external_command(cmd) } - - /// Get unmanaged packages, sorted alphabetically. - fn get_unmanaged_packages_sorted(&self) -> Result> { - let installed = self - .get_explicitly_installed_packages() - .context("could not get explicitly installed packages")?; - let required = self.get_managed_packages(); - let mut diff: Vec<_> = installed.difference(required).cloned().collect(); - diff.sort_unstable(); - Ok(diff) - } - - /// Whether the underlying package manager supports dependency packages. - fn supports_as_dependency(&self) -> bool; } /// For a vector of tuples containing a `V` and `K`, where a `K` may occur more than diff --git a/crates/pacdef/src/backend/macros.rs b/crates/pacdef/src/backend/macros.rs deleted file mode 100644 index 81920cc..0000000 --- a/crates/pacdef/src/backend/macros.rs +++ /dev/null @@ -1,56 +0,0 @@ -/// Used to implement parts of the `Backend` trait that should not change between the actual -/// backends (boilerplate). -macro_rules! impl_backend_constants { - () => { - fn get_binary_default(&self) -> Text { - BINARY - } - - fn get_section(&self) -> Text { - SECTION - } - - fn get_switches_info(&self) -> Switches { - SWITCHES_INFO - } - - fn get_switches_install(&self) -> Switches { - SWITCHES_INSTALL - } - - fn get_switches_noconfirm(&self) -> Switches { - SWITCHES_NOCONFIRM - } - - fn get_switches_remove(&self) -> Switches { - SWITCHES_REMOVE - } - - fn get_switches_make_dependency(&self) -> Switches { - SWITCHES_MAKE_DEPENDENCY - } - - fn get_managed_packages(&self) -> &HashSet { - &self.packages - } - - fn load(&mut self, groups: &crate::Groups) { - let own_section_name = self.get_section(); - - groups - .iter() - .flat_map(|g| &g.sections) - .filter(|section| section.name == own_section_name) - .flat_map(|section| §ion.packages) - .for_each(|package| { - self.packages.insert(package.clone()); - }) - } - - fn supports_as_dependency(&self) -> bool { - SUPPORTS_AS_DEPENDENCY - } - }; -} - -pub(crate) use impl_backend_constants; diff --git a/crates/pacdef/src/backend/mod.rs b/crates/pacdef/src/backend/mod.rs index 78e15ef..20cede0 100644 --- a/crates/pacdef/src/backend/mod.rs +++ b/crates/pacdef/src/backend/mod.rs @@ -1,23 +1,53 @@ pub mod actual; pub mod backend_trait; -pub mod macros; mod root; pub mod todo_per_backend; -use crate::backend::backend_trait::Switches; -use crate::backend::backend_trait::Text; -use crate::Group; -use crate::Groups; -use crate::Package; -use anyhow::Result; -use backend_trait::Backend; -use std::collections::HashSet; +use std::fmt::Display; -use self::actual::{ - fedora::Fedora, flatpak::Flatpak, python::Python, rust::Rust, rustup::Rustup, void::Void, -}; +use crate::prelude::*; +use anyhow::{Context, Result}; -#[derive(Debug)] +// A backend with its associated managed packages +pub struct ManagedBackend { + /// All managed packages for this backend, i.e. all packages + /// under the corresponding section in all group files. + pub packages: Packages, + pub any_backend: AnyBackend, +} +impl ManagedBackend { + /// Get unmanaged packages, sorted alphabetically. + /// + /// # Errors + /// + /// Returns an error if the backend fails to get the explicitly installed packages. + pub fn get_unmanaged_packages_sorted(&self) -> Result> { + let installed = self + .any_backend + .get_explicitly_installed_packages() + .context("could not get explicitly installed packages")?; + let mut diff: Vec<_> = installed.difference(&self.packages).cloned().collect(); + diff.sort_unstable(); + Ok(diff) + } + + /// Get missing packages, sorted alphabetically. + /// + /// # Errors + /// + /// Returns an error if the backend fails to get the installed packages. + pub fn get_missing_packages_sorted(&self) -> Result> { + let installed = self + .any_backend + .get_all_installed_packages() + .context("could not get installed packages")?; + let mut diff: Vec<_> = self.packages.difference(&installed).cloned().collect(); + diff.sort_unstable(); + Ok(diff) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] #[enum_dispatch::enum_dispatch(Backend)] pub enum AnyBackend { #[cfg(feature = "arch")] @@ -31,22 +61,44 @@ pub enum AnyBackend { Rustup(Rustup), Void(Void), } - impl AnyBackend { /// Returns an iterator of every variant of backend. - pub fn iter() -> impl Iterator { + pub fn all(config: &Config) -> impl Iterator { vec![ #[cfg(feature = "arch")] - Self::Arch(actual::arch::Arch::new()), + Self::Arch(actual::arch::Arch::new(config)), #[cfg(feature = "debian")] Self::Debian(actual::debian::Debian::new()), - Self::Flatpak(Flatpak::new()), + Self::Flatpak(Flatpak::new(config)), Self::Fedora(Fedora::new()), - Self::Python(Python::new()), + Self::Python(Python::new(config)), Self::Rust(Rust::new()), Self::Rustup(Rustup::new()), Self::Void(Void::new()), ] .into_iter() } + + pub fn from_section(section: &str, config: &Config) -> Result { + match section { + #[cfg(feature = "arch")] + "arch" => Ok(Self::Arch(actual::arch::Arch::new(config))), + #[cfg(feature = "debian")] + "debian" => Ok(Self::Debian(actual::debian::Debian::new())), + "flatpak" => Ok(Self::Flatpak(Flatpak::new(config))), + "fedora" => Ok(Self::Fedora(Fedora::new())), + "python" => Ok(Self::Python(Python::new(config))), + "rust" => Ok(Self::Rust(Rust::new())), + "rustup" => Ok(Self::Rustup(Rustup::new())), + "void" => Ok(Self::Void(Void::new())), + _ => Err(anyhow::anyhow!( + "no matching backend for the section: {section}" + )), + } + } +} +impl Display for AnyBackend { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.backend_info().section) + } } diff --git a/crates/pacdef/src/backend/todo_per_backend.rs b/crates/pacdef/src/backend/todo_per_backend.rs index 4bce5df..085bf42 100644 --- a/crates/pacdef/src/backend/todo_per_backend.rs +++ b/crates/pacdef/src/backend/todo_per_backend.rs @@ -2,8 +2,7 @@ use std::fmt::Write; use anyhow::{Context, Result}; -use super::{AnyBackend, Backend}; -use crate::Package; +use crate::prelude::*; /// A vector of tuples containing a Backends and a vector of unmanaged packages /// for that backend. @@ -37,7 +36,7 @@ impl ToDoPerBackend { backend .install_packages(packages, noconfirm) - .with_context(|| format!("installing packages for {}", backend.get_section()))?; + .with_context(|| format!("installing packages for {backend}"))?; } Ok(()) } @@ -50,7 +49,7 @@ impl ToDoPerBackend { backend .remove_packages(packages, noconfirm) - .with_context(|| format!("removing packages for {}", backend.get_section()))?; + .with_context(|| format!("removing packages for {backend}"))?; } Ok(()) } @@ -65,7 +64,7 @@ impl ToDoPerBackend { let mut segment = String::new(); - segment.write_str(&format!("[{}]", backend.get_section()))?; + segment.write_str(&format!("[{backend}]"))?; for package in packages { segment.write_str(&format!("\n{package}"))?; } diff --git a/crates/pacdef/src/config.rs b/crates/pacdef/src/config.rs index 50f7e88..0ec9928 100644 --- a/crates/pacdef/src/config.rs +++ b/crates/pacdef/src/config.rs @@ -5,6 +5,8 @@ use std::path::Path; use anyhow::{bail, Context, Result}; use serde::{Deserialize, Serialize}; +use crate::prelude::*; + // Update the master README if fields change. /// Config for the program, as listed in `$XDG_CONFIG_HOME/pacdef/pacdef.toml`. #[derive(Debug, Serialize, Deserialize)] @@ -55,7 +57,7 @@ impl Config { Ok(content) => content, Err(e) => { if e.kind() == ErrorKind::NotFound { - bail!(crate::Error::ConfigFileNotFound) + bail!(Error::ConfigFileNotFound) } bail!("unexpected error occurred: {e:?}"); } diff --git a/crates/pacdef/src/core.rs b/crates/pacdef/src/core.rs index 2d121ca..284418a 100644 --- a/crates/pacdef/src/core.rs +++ b/crates/pacdef/src/core.rs @@ -8,23 +8,14 @@ use std::process::Command; use anyhow::{bail, ensure, Context, Result}; use const_format::formatcp; -use crate::backend::backend_trait::Backend; -use crate::backend::todo_per_backend::ToDoPerBackend; -use crate::backend::AnyBackend; -use crate::cli::{ - CleanPackageAction, EditGroupAction, ExportGroupAction, GroupAction, GroupArguments, - ImportGroupAction, ListGroupAction, MainArguments, MainSubcommand, NewGroupAction, - PackageAction, PackageArguments, RemoveGroupAction, ReviewPackageAction, SearchPackageAction, - ShowGroupAction, SyncPackageAction, UnmanagedPackageAction, VersionArguments, -}; use crate::cmd::{run_edit_command, run_external_command}; use crate::env::{get_editor, should_print_debug_info}; +use crate::grouping::group::groups_to_backend_packages; use crate::path::{binary_in_path, get_absolutized_file_paths, get_group_dir}; +use crate::prelude::*; use crate::review::review; use crate::search::search_packages; use crate::ui::get_user_confirmation; -use crate::Group; -use crate::{Config, Error, Groups}; impl MainArguments { /// Run the action that was provided by the user as first argument. @@ -39,7 +30,7 @@ impl MainArguments { match self.subcommand { MainSubcommand::Group(group) => group.run(groups), MainSubcommand::Package(package) => package.run(groups, config), - MainSubcommand::Version(version) => version.run(), + MainSubcommand::Version(version) => version.run(config), } } } @@ -47,8 +38,8 @@ impl MainArguments { impl VersionArguments { /// If the crate was compiled from git, return `pacdef, ()`. /// Otherwise return `pacdef, `. - fn run(self) -> Result<()> { - let backends = get_included_backends(); + fn run(self, config: &Config) -> Result<()> { + let backends = get_included_backends(config); let mut result = format!("pacdef, version: {}\n", get_version_string()); result.push_str("supported backends:"); for b in backends { @@ -221,7 +212,7 @@ impl NewGroupAction { for new_group in &self.new_groups { ensure!( new_group != "." && new_group != "..", - crate::Error::InvalidGroupName(new_group.clone()) + Error::InvalidGroupName(new_group.clone()) ); } @@ -236,10 +227,7 @@ impl NewGroupAction { .collect(); for file in &paths { - ensure!( - !file.exists(), - crate::Error::GroupAlreadyExists(file.clone()) - ); + ensure!(!file.exists(), Error::GroupAlreadyExists(file.clone())); } for file in &paths { @@ -284,10 +272,7 @@ impl ShowGroupAction { } // return an error if any arg was not found - ensure!( - errors.is_empty(), - crate::Error::MultipleGroupsNotFound(errors) - ); + ensure!(errors.is_empty(), Error::MultipleGroupsNotFound(errors)); let show_more_than_one_group = self.show_groups.len() > 1; @@ -398,50 +383,38 @@ impl UnmanagedPackageAction { } fn get_missing_packages(groups: &Groups, config: &Config) -> Result { + let backend_packages = groups_to_backend_packages(groups, config)?; + let mut to_install = ToDoPerBackend::new(); - for mut backend in AnyBackend::iter() { + for (any_backend, packages) in &backend_packages { + let backend_info = any_backend.backend_info(); + if config .disabled_backends - .contains(&backend.get_section().to_string()) + .contains(&backend_info.section.to_string()) { continue; } - if !binary_in_path(backend.get_binary())? { + if !binary_in_path(&backend_info.binary)? { continue; } - overwrite_values_from_config(&mut backend, config); - backend.load(groups); + let managed_backend = ManagedBackend { + packages: packages.clone(), + any_backend: any_backend.clone(), + }; - match backend.get_missing_packages_sorted() { - Ok(diff) => to_install.push((backend, diff)), - Err(error) => show_backend_query_error(&error, &backend), + match managed_backend.get_missing_packages_sorted() { + Ok(diff) => to_install.push((any_backend.clone(), diff)), + Err(error) => show_backend_query_error(&error, any_backend), }; } Ok(to_install) } -fn overwrite_values_from_config(backend: &mut AnyBackend, config: &Config) { - #[cfg(feature = "arch")] - { - if let AnyBackend::Arch(arch) = backend { - arch.binary.clone_from(&config.aur_helper); - arch.aur_rm_args.clone_from(&config.aur_rm_args); - } - } - - if let AnyBackend::Flatpak(flatpak) = backend { - flatpak.systemwide = config.flatpak_systemwide; - } - - if let AnyBackend::Python(python) = backend { - python.binary.clone_from(&config.pip_binary); - } -} - /// Get a list of unmanaged packages per backend. /// /// This method loops through all enabled `Backend`s whose binary is in `PATH`. @@ -450,29 +423,40 @@ fn overwrite_values_from_config(backend: &mut AnyBackend, config: &Config) { /// /// This function will propagate errors from the individual backends. fn get_unmanaged_packages(groups: &Groups, config: &Config) -> Result { - let mut result = ToDoPerBackend::new(); + let backend_packages = groups_to_backend_packages(groups, config)?; - for mut backend in AnyBackend::iter() { + let mut todo_unmanaged = ToDoPerBackend::new(); + + for (any_backend, packages) in &backend_packages { + let backend_info = any_backend.backend_info(); if config .disabled_backends - .contains(&backend.get_section().to_string()) + .contains(&backend_info.section.to_string()) { continue; } - if !binary_in_path(backend.get_binary())? { + if !binary_in_path(&backend_info.binary)? { continue; } - overwrite_values_from_config(&mut backend, config); - backend.load(groups); + let managed_backend = ManagedBackend { + packages: packages.clone(), + any_backend: any_backend.clone(), + }; - match backend.get_unmanaged_packages_sorted() { - Ok(unmanaged) => result.push((backend, unmanaged)), - Err(error) => show_backend_query_error(&error, &backend), + match managed_backend.get_unmanaged_packages_sorted() { + Ok(diff) => todo_unmanaged.push((any_backend.clone(), diff)), + Err(error) => show_backend_query_error(&error, any_backend), + }; + + match managed_backend.get_unmanaged_packages_sorted() { + Ok(unmanaged) => todo_unmanaged.push((any_backend.clone(), unmanaged)), + Err(error) => show_backend_query_error(&error, any_backend), }; } - Ok(result) + + Ok(todo_unmanaged) } /// Create the parent directory of the `path` if that directory does not exist. @@ -553,14 +537,13 @@ fn find_groups_by_name<'a>(names: &[String], groups: &'a Groups) -> Result() ); } else { - log::warn!("skipping backend '{section}': {error}"); + log::warn!("skipping backend '{backend}': {error}"); } } @@ -578,10 +561,10 @@ pub const fn get_version_string() -> &'static str { } /// Get a vector with the names of all backends, sorted alphabetically. -fn get_included_backends() -> Vec<&'static str> { +fn get_included_backends(config: &Config) -> Vec<&'static str> { let mut result = vec![]; - for backend in AnyBackend::iter() { - result.push(backend.get_section()); + for backend in AnyBackend::all(config) { + result.push(backend.backend_info().section); } result.sort_unstable(); result diff --git a/crates/pacdef/src/grouping/group.rs b/crates/pacdef/src/grouping/group.rs index a579ae4..820020d 100644 --- a/crates/pacdef/src/grouping/group.rs +++ b/crates/pacdef/src/grouping/group.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeSet, HashSet}; +use std::collections::{BTreeMap, BTreeSet}; use std::fmt::Display; use std::fs::{create_dir, read_to_string, File}; use std::hash::Hash; @@ -11,11 +11,27 @@ use walkdir::WalkDir; use crate::path::get_relative_path; -use super::{Package, Section}; +use crate::prelude::*; /// A set of groups pub type Groups = BTreeSet; +pub type BackendPackages = BTreeMap; +pub fn groups_to_backend_packages(groups: &Groups, config: &Config) -> Result { + let mut backend_packages = BackendPackages::new(); + + for group in groups { + for section in &group.sections { + backend_packages + .entry(AnyBackend::from_section(§ion.name, config)?) + .or_default() + .extend(section.packages.iter().cloned()); + } + } + + Ok(backend_packages) +} + /// Representation of a group file. #[derive(Debug, Clone)] pub struct Group { @@ -23,7 +39,7 @@ pub struct Group { /// base dir). pub name: String, /// The sections in the file which in turn hold the packages. - pub sections: HashSet
, + pub sections: Sections, /// The absolute path of the original file. pub path: PathBuf, /// Whether the main program should warn this group being loaded from a symlink. @@ -143,7 +159,7 @@ impl Group { let name = extract_group_name(path, group_dir.as_ref()); let mut lines = content.lines().peekable(); - let mut sections = HashSet::new(); + let mut sections = Sections::new(); while lines.peek().is_some() { let result = Section::try_from_lines(&mut lines).context("reading section"); diff --git a/crates/pacdef/src/grouping/mod.rs b/crates/pacdef/src/grouping/mod.rs index 02a5e08..5816ee8 100644 --- a/crates/pacdef/src/grouping/mod.rs +++ b/crates/pacdef/src/grouping/mod.rs @@ -9,11 +9,6 @@ all groups using [`Group::load`], which in turn will get all packages from all sections. */ -mod group; -mod package; -mod section; - -pub use group::Group; -pub use group::Groups; -pub use package::Package; -pub use section::Section; +pub mod group; +pub mod package; +pub mod section; diff --git a/crates/pacdef/src/grouping/package.rs b/crates/pacdef/src/grouping/package.rs index 078a922..1055e99 100644 --- a/crates/pacdef/src/grouping/package.rs +++ b/crates/pacdef/src/grouping/package.rs @@ -1,6 +1,9 @@ +use std::collections::BTreeSet; use std::fmt::{Display, Write}; use std::hash::Hash; +pub type Packages = BTreeSet; + /// A struct to represent a single package, consisting of a `name`, and /// optionally a `repo`. #[derive(Debug, Eq, PartialOrd, Ord, Clone)] diff --git a/crates/pacdef/src/grouping/section.rs b/crates/pacdef/src/grouping/section.rs index bbb8e07..47af195 100644 --- a/crates/pacdef/src/grouping/section.rs +++ b/crates/pacdef/src/grouping/section.rs @@ -1,27 +1,29 @@ -use std::collections::HashSet; +use std::collections::BTreeSet; use std::fmt::{Display, Write}; use std::hash::Hash; use std::iter::Peekable; use anyhow::{ensure, Context, Result}; -use super::Package; +use crate::prelude::*; + +pub type Sections = BTreeSet
; #[derive(Debug, Clone)] pub struct Section { pub name: String, - pub packages: HashSet, + pub packages: Packages, } impl Section { - pub fn new(name: String, packages: HashSet) -> Self { + pub fn new(name: String, packages: Packages) -> Self { Self { name, packages } } pub fn try_from_lines<'a>(iter: &mut Peekable>) -> Result { let name = find_next_section_name(iter)?; - let mut packages = HashSet::new(); + let mut packages = Packages::new(); while next_line_might_be_package(iter) { if let Some(package) = Package::try_from(iter.next().expect("we checked this is some")) @@ -36,7 +38,7 @@ impl Section { } } -fn insert_package(package: Package, packages: &mut HashSet) { +fn insert_package(package: Package, packages: &mut Packages) { let package_name = package.name.clone(); let newly_inserted = packages.insert(package); diff --git a/crates/pacdef/src/lib.rs b/crates/pacdef/src/lib.rs index 7d69559..b45fd73 100644 --- a/crates/pacdef/src/lib.rs +++ b/crates/pacdef/src/lib.rs @@ -16,12 +16,10 @@ clippy::unwrap_used, clippy::use_debug, clippy::use_self, - clippy::wildcard_dependencies, - missing_docs + clippy::wildcard_dependencies )] pub(crate) mod backend; -#[allow(missing_docs)] pub mod cli; mod cmd; @@ -35,10 +33,9 @@ mod review; mod search; mod ui; +#[allow(unused_imports)] +mod prelude; + pub mod path; -pub use crate::config::Config; -pub use crate::errors::Error; -pub use crate::grouping::Group; -pub use crate::grouping::Groups; -pub use crate::grouping::Package; +pub use prelude::{Config, Error, Group}; diff --git a/crates/pacdef/src/main.rs b/crates/pacdef/src/main.rs index e716c13..61eef18 100644 --- a/crates/pacdef/src/main.rs +++ b/crates/pacdef/src/main.rs @@ -10,8 +10,7 @@ clippy::unwrap_used, clippy::use_debug, clippy::use_self, - clippy::wildcard_dependencies, - missing_docs + clippy::wildcard_dependencies )] use std::path::Path; diff --git a/crates/pacdef/src/prelude.rs b/crates/pacdef/src/prelude.rs new file mode 100644 index 0000000..7e5e29e --- /dev/null +++ b/crates/pacdef/src/prelude.rs @@ -0,0 +1,46 @@ +#[cfg(feature = "arch")] +pub use crate::backend::actual::arch::Arch; +#[cfg(feature = "debian")] +pub use crate::backend::actual::debian::Debian; +pub use crate::backend::actual::{ + fedora::Fedora, flatpak::Flatpak, python::Python, rust::Rust, rustup::Rustup, void::Void, +}; +pub use crate::backend::backend_trait::{Backend, BackendInfo, Switches, Text}; +pub use crate::backend::todo_per_backend::ToDoPerBackend; +pub use crate::backend::AnyBackend; +pub use crate::backend::ManagedBackend; +pub use crate::cli::CleanPackageAction; +pub use crate::cli::EditGroupAction; +pub use crate::cli::ExportGroupAction; +pub use crate::cli::GroupAction; +pub use crate::cli::GroupArguments; +pub use crate::cli::ImportGroupAction; +pub use crate::cli::ListGroupAction; +pub use crate::cli::MainArguments; +pub use crate::cli::MainSubcommand; +pub use crate::cli::NewGroupAction; +pub use crate::cli::PackageAction; +pub use crate::cli::PackageArguments; +pub use crate::cli::RemoveGroupAction; +pub use crate::cli::ReviewPackageAction; +pub use crate::cli::SearchPackageAction; +pub use crate::cli::ShowGroupAction; +pub use crate::cli::SyncPackageAction; +pub use crate::cli::UnmanagedPackageAction; +pub use crate::cli::VersionArguments; +pub use crate::config::Config; +pub use crate::errors::Error; +pub use crate::grouping::{ + group::{Group, Groups}, + package::{Package, Packages}, + section::{Section, Sections}, +}; +pub use crate::path::binary_in_path; +pub use crate::path::get_absolutized_file_paths; +pub use crate::path::get_cargo_home; +pub use crate::path::get_config_path; +pub use crate::path::get_config_path_old_version; +pub use crate::path::get_group_dir; +pub use crate::path::get_home_dir; +pub use crate::path::get_pacdef_base_dir; +pub use crate::path::get_relative_path; diff --git a/crates/pacdef/src/review/datastructures.rs b/crates/pacdef/src/review/datastructures.rs index f42926b..a2f5d57 100644 --- a/crates/pacdef/src/review/datastructures.rs +++ b/crates/pacdef/src/review/datastructures.rs @@ -1,4 +1,4 @@ -use crate::{backend::AnyBackend, Group, Package}; +use crate::prelude::*; use super::strategy::Strategy; diff --git a/crates/pacdef/src/review/mod.rs b/crates/pacdef/src/review/mod.rs index 7a54b33..106bdfd 100644 --- a/crates/pacdef/src/review/mod.rs +++ b/crates/pacdef/src/review/mod.rs @@ -5,10 +5,8 @@ use std::io::{stdin, stdout, Write}; use anyhow::Result; -use crate::backend::backend_trait::Backend; -use crate::backend::todo_per_backend::ToDoPerBackend; +use crate::prelude::*; use crate::ui::{get_user_confirmation, read_single_char_from_terminal}; -use crate::{Group, Groups, Package}; use self::datastructures::{ContinueWithReview, ReviewAction, ReviewIntention, ReviewsPerBackend}; use self::strategy::Strategy; @@ -24,7 +22,7 @@ pub fn review(todo_per_backend: ToDoPerBackend, groups: &Groups) -> Result<()> { 'outer: for (backend, packages) in todo_per_backend { let mut actions = vec![]; for package in packages { - println!("{}: {package}", backend.get_section()); + println!("{}: {package}", backend.backend_info().section); match get_action_for_package(package, groups, &mut actions, &backend)? { ContinueWithReview::Yes => continue, ContinueWithReview::No => return Ok(()), diff --git a/crates/pacdef/src/review/strategy.rs b/crates/pacdef/src/review/strategy.rs index b0fd4f6..a4145b0 100644 --- a/crates/pacdef/src/review/strategy.rs +++ b/crates/pacdef/src/review/strategy.rs @@ -1,9 +1,6 @@ use anyhow::Result; -use crate::{ - backend::{backend_trait::Backend, AnyBackend}, - Group, Package, -}; +use crate::prelude::*; #[derive(Debug)] pub struct Strategy { @@ -49,7 +46,7 @@ impl Strategy { return; } - println!("[{}]", self.backend.get_section()); + println!("[{}]", self.backend.backend_info().section); if !self.delete.is_empty() { println!("delete:"); diff --git a/crates/pacdef/src/search.rs b/crates/pacdef/src/search.rs index a56a839..5fba7fe 100644 --- a/crates/pacdef/src/search.rs +++ b/crates/pacdef/src/search.rs @@ -1,14 +1,10 @@ use std::iter::Peekable; use std::vec::IntoIter; +use crate::prelude::*; use anyhow::{bail, Result}; use regex::Regex; -use crate::{ - grouping::{Group, Package, Section}, - Groups, -}; - /// Find all packages in all groups whose name match the regex from the /// command-line arguments. Print the name of the packages per group and /// section.