restructure project into a virtual manifest

- remove redundant readme
- de-duplicate Cargo.toml information using workspace inheritance
- rename "pacdef_core" to "pacdef"
- move "crates/main/main.rs" to "crates/pacdef/src/main.rs"
This commit is contained in:
ripytide
2024-04-13 14:37:12 +01:00
parent bdfa9b6aeb
commit b473f8e432
48 changed files with 37 additions and 73 deletions
+203
View File
@@ -0,0 +1,203 @@
use std::any::Any;
use std::cmp::{Eq, Ord};
use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
use std::hash::Hash;
use std::process::Command;
use std::rc::Rc;
use anyhow::{Context, Result};
use crate::cmd::run_external_command;
use crate::{Group, Package};
pub(in crate::backend) type Switches = &'static [&'static str];
pub(in crate::backend) type Text = &'static str;
/// The trait of a struct that is used as a backend.
pub trait Backend: Debug {
/// 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 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: &HashSet<Group>);
/// 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>>;
/// Get all packages that were installed in the system explicitly.
///
/// # Errors
///
/// This function shall return an error if the explicitly installed packages
/// cannot be determined.
fn get_explicitly_installed_packages(&self) -> Result<HashSet<Package>>;
/// Assign each of the packages to an individual group by editing the
/// group files.
fn assign_group(&self, to_assign: Vec<(Package, Rc<Group>)>) -> Result<()> {
let group_package_map = to_hashmap(to_assign);
let section_header = format!("[{}]", self.get_section());
for (group, packages) in group_package_map {
group.save_packages(&section_header, &packages)?;
}
Ok(())
}
/// Install the specified packages. If `noconfirm` is `true`, pass the corresponding
/// switch to the package manager. Return the [`ExitStatus`] from the package manager.
///
/// # Errors
///
/// 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());
if noconfirm {
cmd.args(self.get_switches_noconfirm());
}
for p in packages {
cmd.arg(format!("{p}"));
}
run_external_command(cmd)
}
/// Mark the packages as non-explicit / dependency using the underlying
/// package manager.
///
/// # Panics
///
/// This method shall panic when the backend does not support depedent packages.
fn make_dependency(&self, packages: &[Package]) -> Result<()> {
let mut cmd = Command::new(self.get_binary());
cmd.args(self.get_switches_make_dependency());
for p in packages {
cmd.arg(format!("{p}"));
}
run_external_command(cmd)
}
/// 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());
if noconfirm {
cmd.args(self.get_switches_noconfirm());
}
for p in packages {
cmd.arg(format!("{p}"));
}
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.
fn show_package_info(&self, package: &Package) -> Result<()> {
let mut cmd = Command::new(self.get_binary());
cmd.args(self.get_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)
}
/// Return a mutable reference to self as `Any`. Required for downcasting.
fn as_any_mut(&mut self) -> &mut dyn Any;
/// 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
/// once and each `V` exactly once, create a `HashMap` that associates each `K` with
/// a `Vec<V>`.
fn to_hashmap<K, V>(to_assign: Vec<(V, K)>) -> HashMap<K, Vec<V>>
where
K: Hash + Eq,
V: Ord,
{
let mut map = HashMap::new();
for (value, key) in to_assign {
let inner: &mut Vec<V> = map.entry(key).or_default();
inner.push(value);
}
for vecs in map.values_mut() {
vecs.sort_unstable();
}
map
}