Backend trait refactors
- Refactor the confusing/bodgy consts backend macro to use a single `BackendInfo` struct - Combine `supports_as_dependency()` and the `switches_dependency` into a single `Option` type as that is a better representation as it is an optional backend feature. - Add more types like `Packages`, `Sections` for uniformity. - Added a crate-private prelude and switches all imports to use a prelude glob import (excluding functions because it's nice to only be importing types) - Refactored Backend section name in logging by implementing Display for `AnyBackend` that just prints the `section`
This commit is contained in:
@@ -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<String>,
|
||||
pub packages: HashSet<Package>,
|
||||
}
|
||||
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<HashSet<Package>> {
|
||||
fn get_all_installed_packages(&self) -> Result<Packages> {
|
||||
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<HashSet<Package>> {
|
||||
fn get_explicitly_installed_packages(&self) -> Result<Packages> {
|
||||
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<HashSet<String>> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn convert_to_pacdef_packages(packages: HashSet<String>) -> HashSet<Package> {
|
||||
fn convert_to_pacdef_packages(packages: HashSet<String>) -> Packages {
|
||||
packages.into_iter().map(Package::from).collect()
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Package>,
|
||||
}
|
||||
#[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<HashSet<Package>> {
|
||||
fn get_all_installed_packages(&self) -> Result<Packages> {
|
||||
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<HashSet<Package>> {
|
||||
fn get_explicitly_installed_packages(&self) -> Result<Packages> {
|
||||
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 {
|
||||
|
||||
@@ -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<Package>,
|
||||
}
|
||||
#[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<HashSet<Package>> {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
fn get_all_installed_packages(&self) -> Result<Packages> {
|
||||
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<HashSet<Package>> {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
fn get_explicitly_installed_packages(&self) -> Result<Packages> {
|
||||
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)
|
||||
|
||||
@@ -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<Package>,
|
||||
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<HashSet<Package>> {
|
||||
let mut cmd = Command::new(BINARY);
|
||||
fn get_installed_packages(&self, include_implicit: bool) -> Result<Packages> {
|
||||
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::<HashSet<Package>>())
|
||||
Ok(output.lines().map(Package::from).collect::<Packages>())
|
||||
}
|
||||
}
|
||||
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<HashSet<Package>> {
|
||||
fn get_all_installed_packages(&self) -> Result<Packages> {
|
||||
self.get_installed_packages(true)
|
||||
}
|
||||
|
||||
fn get_explicitly_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
fn get_explicitly_installed_packages(&self) -> Result<Packages> {
|
||||
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}"));
|
||||
|
||||
|
||||
@@ -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<Package>,
|
||||
}
|
||||
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<HashSet<Package>> {
|
||||
match self.get_binary() {
|
||||
fn extract_packages(&self, output: Value) -> Result<Packages> {
|
||||
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<HashSet<Package>> {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
fn get_all_installed_packages(&self) -> Result<Packages> {
|
||||
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<HashSet<Package>> {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
fn get_explicitly_installed_packages(&self) -> Result<Packages> {
|
||||
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<Value> {
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn extract_pacdef_packages(value: Value) -> Result<HashSet<Package>> {
|
||||
fn extract_pacdef_packages(value: Value) -> Result<Packages> {
|
||||
let result = value
|
||||
.as_array()
|
||||
.context("getting inner json array")?
|
||||
@@ -111,7 +95,7 @@ fn extract_pacdef_packages(value: Value) -> Result<HashSet<Package>> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn extract_pacdef_packages_pipx(value: Value) -> Result<HashSet<Package>> {
|
||||
fn extract_pacdef_packages_pipx(value: Value) -> Result<Packages> {
|
||||
let result = value["venvs"]
|
||||
.as_object()
|
||||
.context("getting inner json object")?
|
||||
|
||||
@@ -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<Package>,
|
||||
}
|
||||
#[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<HashSet<Package>> {
|
||||
fn get_all_installed_packages(&self) -> Result<Packages> {
|
||||
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<HashSet<Package>> {
|
||||
fn get_explicitly_installed_packages(&self) -> Result<Packages> {
|
||||
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<HashSet<Package>> {
|
||||
let result: HashSet<_> = json
|
||||
fn extract_packages(json: &Value) -> Result<Packages> {
|
||||
let result: Packages = json
|
||||
.get("installs")
|
||||
.context("get 'installs' field from json")?
|
||||
.as_object()
|
||||
@@ -83,6 +75,7 @@ fn extract_packages(json: &Value) -> Result<HashSet<Package>> {
|
||||
})
|
||||
.map(|name| Package::try_from(name).expect("name is valid"))
|
||||
.collect();
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Package>,
|
||||
}
|
||||
#[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<HashSet<Package>> {
|
||||
fn get_all_installed_packages(&self) -> Result<Packages> {
|
||||
let toolchains_vec = self
|
||||
.run_toolchain_command(Repotype::Toolchain.get_info_switches())
|
||||
.context("Getting installed toolchains")?;
|
||||
|
||||
let toolchains: HashSet<Package> = toolchains_vec
|
||||
let toolchains: Packages = toolchains_vec
|
||||
.iter()
|
||||
.map(|name| ["toolchain", name].join("/").into())
|
||||
.collect();
|
||||
|
||||
let components: HashSet<Package> = 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<HashSet<Package>> {
|
||||
fn get_explicitly_installed_packages(&self) -> Result<Packages> {
|
||||
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<Vec<String>> {
|
||||
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<RustupPackage>) -> Result<Vec<String>> {
|
||||
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<String>,
|
||||
) -> 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) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use anyhow::{bail, Context, Result};
|
||||
|
||||
use crate::{backend::backend_trait::Switches, Package};
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Repotype {
|
||||
|
||||
@@ -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<Package>,
|
||||
}
|
||||
#[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<HashSet<Package>> {
|
||||
fn get_all_installed_packages(&self) -> Result<Packages> {
|
||||
// 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<HashSet<Package>> {
|
||||
fn get_explicitly_installed_packages(&self) -> Result<Packages> {
|
||||
// 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)
|
||||
|
||||
@@ -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<Switches>,
|
||||
}
|
||||
|
||||
/// 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<Package>;
|
||||
|
||||
/// 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<HashSet<Package>>;
|
||||
fn get_all_installed_packages(&self) -> Result<Packages>;
|
||||
|
||||
/// 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<HashSet<Package>>;
|
||||
fn get_explicitly_installed_packages(&self) -> Result<Packages>;
|
||||
|
||||
/// 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<Vec<Package>> {
|
||||
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<Vec<Package>> {
|
||||
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
|
||||
|
||||
@@ -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<Package> {
|
||||
&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;
|
||||
@@ -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<Vec<Package>> {
|
||||
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<Vec<Package>> {
|
||||
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<Item = Self> {
|
||||
pub fn all(config: &Config) -> impl Iterator<Item = Self> {
|
||||
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<Self> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}"))?;
|
||||
}
|
||||
|
||||
@@ -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:?}");
|
||||
}
|
||||
|
||||
+48
-65
@@ -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, <version> (<hash>)`.
|
||||
/// Otherwise return `pacdef, <version>`.
|
||||
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<ToDoPerBackend> {
|
||||
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<ToDoPerBackend> {
|
||||
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<Vec<&
|
||||
/// Show the error chain for an error that has occurred when a backend was queried
|
||||
/// if the `RUST_BACKTRACE` env variable is set to `1` or `full`.
|
||||
fn show_backend_query_error(error: &anyhow::Error, backend: &AnyBackend) {
|
||||
let section = backend.get_section();
|
||||
if should_print_debug_info() {
|
||||
log::warn!(
|
||||
"skipping backend '{section}': {}",
|
||||
"skipping backend '{backend}': {}",
|
||||
error.chain().map(|x| x.to_string()).collect::<String>()
|
||||
);
|
||||
} 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
|
||||
|
||||
@@ -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<Group>;
|
||||
|
||||
pub type BackendPackages = BTreeMap<AnyBackend, Packages>;
|
||||
pub fn groups_to_backend_packages(groups: &Groups, config: &Config) -> Result<BackendPackages> {
|
||||
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<Section>,
|
||||
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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt::{Display, Write};
|
||||
use std::hash::Hash;
|
||||
|
||||
pub type Packages = BTreeSet<Package>;
|
||||
|
||||
/// A struct to represent a single package, consisting of a `name`, and
|
||||
/// optionally a `repo`.
|
||||
#[derive(Debug, Eq, PartialOrd, Ord, Clone)]
|
||||
|
||||
@@ -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<Section>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Section {
|
||||
pub name: String,
|
||||
pub packages: HashSet<Package>,
|
||||
pub packages: Packages,
|
||||
}
|
||||
|
||||
impl Section {
|
||||
pub fn new(name: String, packages: HashSet<Package>) -> Self {
|
||||
pub fn new(name: String, packages: Packages) -> Self {
|
||||
Self { name, packages }
|
||||
}
|
||||
|
||||
pub fn try_from_lines<'a>(iter: &mut Peekable<impl Iterator<Item = &'a str>>) -> Result<Self> {
|
||||
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<Package>) {
|
||||
fn insert_package(package: Package, packages: &mut Packages) {
|
||||
let package_name = package.name.clone();
|
||||
let newly_inserted = packages.insert(package);
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{backend::AnyBackend, Group, Package};
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::strategy::Strategy;
|
||||
|
||||
|
||||
@@ -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(()),
|
||||
|
||||
@@ -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:");
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user