rename all crates
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
pub mod pacman;
|
||||
pub mod rust;
|
||||
@@ -0,0 +1,115 @@
|
||||
use std::collections::HashSet;
|
||||
use std::process::{Command, ExitStatus};
|
||||
|
||||
use alpm::Alpm;
|
||||
use alpm::PackageReason::Explicit;
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use crate::backend::backend_trait::*;
|
||||
use crate::{impl_backend_constants, Group, Package};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Pacman {
|
||||
pub(crate) binary: String,
|
||||
pub(crate) aur_rm_args: Option<Vec<String>>,
|
||||
pub(crate) packages: HashSet<Package>,
|
||||
}
|
||||
|
||||
const BINARY: Text = "paru";
|
||||
const SECTION: Text = "pacman";
|
||||
|
||||
const SWITCHES_INFO: Switches = &["--query", "--info"];
|
||||
const SWITCHES_INSTALL: Switches = &["--sync"];
|
||||
const SWITCHES_MAKE_DEPENDENCY: Switches = &["--database", "--asdeps"];
|
||||
const SWITCHES_REMOVE: Switches = &["--remove", "--recursive"];
|
||||
|
||||
impl Backend for Pacman {
|
||||
impl_backend_constants!();
|
||||
|
||||
fn get_all_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
let alpm_packages = get_all_installed_packages_from_alpm()
|
||||
.context("getting all installed packages from alpm")?;
|
||||
|
||||
let result = convert_to_pacdef_packages(alpm_packages);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn get_explicitly_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
let alpm_packages = get_explicitly_installed_packages_from_alpm()
|
||||
.context("getting all installed packages from alpm")?;
|
||||
let result = convert_to_pacdef_packages(alpm_packages);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Install the specified packages.
|
||||
fn install_packages(&self, packages: &[Package]) -> Result<ExitStatus> {
|
||||
let mut cmd = Command::new(&self.binary);
|
||||
|
||||
cmd.args(self.get_switches_install());
|
||||
|
||||
for p in packages {
|
||||
cmd.arg(format!("{p}"));
|
||||
}
|
||||
|
||||
cmd.status()
|
||||
.with_context(|| format!("running command {cmd:?}"))
|
||||
}
|
||||
|
||||
/// Remove the specified packages.
|
||||
fn remove_packages(&self, packages: &[Package]) -> Result<ExitStatus> {
|
||||
let mut cmd = Command::new(&self.binary);
|
||||
|
||||
cmd.args(self.get_switches_remove());
|
||||
if let Some(rm_args) = &self.aur_rm_args {
|
||||
cmd.args(rm_args);
|
||||
}
|
||||
|
||||
for p in packages {
|
||||
cmd.arg(format!("{p}"));
|
||||
}
|
||||
|
||||
cmd.status()
|
||||
.with_context(|| format!("running command [{cmd:?}]"))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_all_installed_packages_from_alpm() -> Result<HashSet<String>> {
|
||||
let db = get_db_handle().context("getting DB handle")?;
|
||||
let result = db
|
||||
.localdb()
|
||||
.pkgs()
|
||||
.iter()
|
||||
.map(|p| p.name().to_string())
|
||||
.collect();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn get_explicitly_installed_packages_from_alpm() -> Result<HashSet<String>> {
|
||||
let db = get_db_handle().context("getting DB handle")?;
|
||||
let result = db
|
||||
.localdb()
|
||||
.pkgs()
|
||||
.iter()
|
||||
.filter(|p| p.reason() == Explicit)
|
||||
.map(|p| p.name().to_string())
|
||||
.collect();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn convert_to_pacdef_packages(packages: HashSet<String>) -> HashSet<Package> {
|
||||
packages.into_iter().map(Package::from).collect()
|
||||
}
|
||||
|
||||
fn get_db_handle() -> Result<Alpm> {
|
||||
Alpm::new("/", "/var/lib/pacman").context("connecting to DB using expected default values")
|
||||
}
|
||||
|
||||
impl Pacman {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
binary: BINARY.to_string(),
|
||||
aur_rm_args: None,
|
||||
packages: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
use std::collections::HashSet;
|
||||
use std::fs::read_to_string;
|
||||
use std::path::PathBuf;
|
||||
use std::process::ExitStatus;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::backend::backend_trait::*;
|
||||
use crate::{impl_backend_constants, Group, Package};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Rust {
|
||||
pub(crate) packages: HashSet<Package>,
|
||||
}
|
||||
|
||||
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_REMOVE: Switches = &["uninstall"];
|
||||
|
||||
impl Backend for Rust {
|
||||
impl_backend_constants!();
|
||||
|
||||
fn get_all_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
let file = get_crates_file().context("getting path to crates file")?;
|
||||
let content = read_to_string(file).context("reading crates file")?;
|
||||
let json: Value =
|
||||
serde_json::from_str(&content).context("parsing JSON from crates file")?;
|
||||
extract_packages(&json).context("extracing packages from crates file")
|
||||
}
|
||||
|
||||
fn get_explicitly_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
self.get_all_installed_packages()
|
||||
.context("getting all installed packages")
|
||||
}
|
||||
|
||||
fn make_dependency(&self, _: &[Package]) -> Result<ExitStatus> {
|
||||
unreachable!("cargo does not have unmanaged packages")
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_packages(json: &Value) -> Result<HashSet<Package>> {
|
||||
let result: HashSet<_> = json
|
||||
.get("installs")
|
||||
.context("get 'installs' field from json")?
|
||||
.as_object()
|
||||
.context("getting object")?
|
||||
.into_iter()
|
||||
.map(|(name, _)| name)
|
||||
.map(|name| {
|
||||
name.split_whitespace()
|
||||
.next()
|
||||
.expect("identifier is whitespace-delimited")
|
||||
})
|
||||
.map(|name| Package::try_from(name).expect("name is valid"))
|
||||
.collect();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
impl Rust {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
packages: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_crates_file() -> Result<PathBuf> {
|
||||
let mut result = crate::path::get_home_dir().context("getting home dir")?;
|
||||
result.push(".cargo");
|
||||
result.push(".crates2.json");
|
||||
Ok(result)
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::process::Command;
|
||||
use std::rc::Rc;
|
||||
use std::{collections::HashSet, process::ExitStatus};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use crate::{Group, Package};
|
||||
|
||||
pub(in crate::backend) type Switches = &'static [&'static str];
|
||||
pub(in crate::backend) type Text = &'static str;
|
||||
|
||||
pub trait Backend: Debug {
|
||||
fn get_binary(&self) -> Text;
|
||||
fn get_section(&self) -> Text;
|
||||
|
||||
fn get_switches_info(&self) -> Switches;
|
||||
fn get_switches_install(&self) -> Switches;
|
||||
fn get_switches_remove(&self) -> Switches;
|
||||
fn get_switches_make_dependency(&self) -> Switches;
|
||||
|
||||
fn load(&mut self, groups: &HashSet<Group>);
|
||||
|
||||
fn get_managed_packages(&self) -> &HashSet<Package>;
|
||||
|
||||
/// Get all packages that are installed in the system.
|
||||
fn get_all_installed_packages(&self) -> Result<HashSet<Package>>;
|
||||
|
||||
/// Get all packages that were installed in the system explicitly.
|
||||
fn get_explicitly_installed_packages(&self) -> Result<HashSet<Package>>;
|
||||
|
||||
fn assign_group(&self, to_assign: Vec<(Package, Rc<Group>)>) -> Result<()> {
|
||||
let group_package_map = get_group_packages_map(to_assign);
|
||||
let section_header = format!("[{}]", self.get_section());
|
||||
|
||||
for (group, packages) in group_package_map {
|
||||
group.save_packages(§ion_header, &packages)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Install the specified packages.
|
||||
fn install_packages(&self, packages: &[Package]) -> Result<ExitStatus> {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
cmd.args(self.get_switches_install());
|
||||
for p in packages {
|
||||
cmd.arg(format!("{p}"));
|
||||
}
|
||||
cmd.status()
|
||||
.with_context(|| format!("running command {cmd:?}"))
|
||||
}
|
||||
|
||||
fn make_dependency(&self, packages: &[Package]) -> Result<ExitStatus> {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
cmd.args(self.get_switches_make_dependency());
|
||||
for p in packages {
|
||||
cmd.arg(format!("{p}"));
|
||||
}
|
||||
cmd.status()
|
||||
.with_context(|| format!("running command [{cmd:?}]"))
|
||||
}
|
||||
|
||||
/// Remove the specified packages.
|
||||
fn remove_packages(&self, packages: &[Package]) -> Result<ExitStatus> {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
cmd.args(self.get_switches_remove());
|
||||
for p in packages {
|
||||
cmd.arg(format!("{p}"));
|
||||
}
|
||||
cmd.status()
|
||||
.with_context(|| format!("running command [{cmd:?}]"))
|
||||
}
|
||||
|
||||
/// extract packages from its own section as read from group files
|
||||
fn extract_packages_from_group_file_content(&self, content: &str) -> HashSet<Package> {
|
||||
content
|
||||
.lines()
|
||||
.skip_while(|line| !line.starts_with(&format!("[{}]", self.get_section())))
|
||||
.skip(1)
|
||||
.filter(|line| !line.starts_with('['))
|
||||
.fuse()
|
||||
.filter_map(Package::try_from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fn add_packages(&mut self, packages: HashSet<Package>);
|
||||
|
||||
/// Show information from package manager for package.
|
||||
fn show_package_info(&self, package: &Package) -> Result<ExitStatus> {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
cmd.args(self.get_switches_info());
|
||||
cmd.arg(format!("{package}"));
|
||||
cmd.status()
|
||||
.with_context(|| format!("running command {cmd:?}"))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_group_packages_map(
|
||||
to_assign: Vec<(Package, Rc<Group>)>,
|
||||
) -> HashMap<Rc<Group>, Vec<Package>> {
|
||||
let mut group_package_map = HashMap::new();
|
||||
|
||||
for (p, group) in to_assign {
|
||||
if !group_package_map.contains_key(&group) {
|
||||
group_package_map.insert(group.clone(), vec![]);
|
||||
}
|
||||
|
||||
let inner = group_package_map
|
||||
.get_mut(&group)
|
||||
.expect("either it was already there or we created it");
|
||||
inner.push(p);
|
||||
}
|
||||
|
||||
for vecs in group_package_map.values_mut() {
|
||||
vecs.sort();
|
||||
}
|
||||
group_package_map
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::{Backend, Backends};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BackendIter {
|
||||
pub(crate) next: Option<Backends>,
|
||||
}
|
||||
|
||||
impl Iterator for BackendIter {
|
||||
type Item = Box<dyn Backend>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match &self.next {
|
||||
None => None,
|
||||
Some(b) => {
|
||||
let result = b.get_backend();
|
||||
self.next = b.next();
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#[macro_export]
|
||||
macro_rules! impl_backend_constants {
|
||||
() => {
|
||||
fn get_binary(&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_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: &HashSet<Group>) {
|
||||
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 add_packages(&mut self, packages: HashSet<Package>) {
|
||||
for p in packages {
|
||||
self.packages.insert(p);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
mod actual;
|
||||
mod backend_trait;
|
||||
mod iter;
|
||||
mod macros;
|
||||
mod todo_per_backend;
|
||||
|
||||
pub use backend_trait::Backend;
|
||||
pub use iter::BackendIter;
|
||||
pub use todo_per_backend::ToDoPerBackend;
|
||||
|
||||
use pacdef_macros::Register;
|
||||
|
||||
#[derive(Debug, Register)]
|
||||
pub enum Backends {
|
||||
Pacman,
|
||||
Rust,
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
use std::process::ExitStatus;
|
||||
|
||||
use anyhow::{bail, ensure, Context, Result};
|
||||
|
||||
use super::Backend;
|
||||
use crate::Package;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ToDoPerBackend(Vec<(Box<dyn Backend>, Vec<Package>)>);
|
||||
|
||||
impl ToDoPerBackend {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self(vec![])
|
||||
}
|
||||
|
||||
pub(crate) fn push(&mut self, item: (Box<dyn Backend>, Vec<Package>)) {
|
||||
self.0.push(item);
|
||||
}
|
||||
|
||||
pub(crate) fn into_iter(self) -> impl Iterator<Item = (Box<dyn Backend>, Vec<Package>)> {
|
||||
self.0.into_iter()
|
||||
}
|
||||
|
||||
pub(crate) fn iter(&self) -> impl Iterator<Item = &(Box<dyn Backend>, Vec<Package>)> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
pub(crate) fn nothing_to_do_for_all_backends(&self) -> bool {
|
||||
self.0.iter().all(|(_, diff)| diff.is_empty())
|
||||
}
|
||||
|
||||
pub(crate) fn install_missing_packages(&self) -> Result<()> {
|
||||
self.handle_backend_command(Backend::install_packages, "install", "installing")
|
||||
.context("installing packages")
|
||||
}
|
||||
|
||||
pub(crate) fn remove_unmanaged_packages(&self) -> Result<()> {
|
||||
self.handle_backend_command(Backend::remove_packages, "remove", "removing")
|
||||
.context("removing packages")
|
||||
}
|
||||
|
||||
fn handle_backend_command<'a, F>(
|
||||
&'a self,
|
||||
func: F,
|
||||
verb: &'_ str,
|
||||
verb_continuous: &'_ str,
|
||||
) -> Result<()>
|
||||
where
|
||||
F: Fn(&'a dyn Backend, &'a [Package]) -> Result<ExitStatus>,
|
||||
{
|
||||
for (backend, packages) in &self.0 {
|
||||
if packages.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let exit_status = func(&**backend, packages).with_context(|| {
|
||||
format!("{verb_continuous} packages for {}", backend.get_binary())
|
||||
})?;
|
||||
|
||||
match exit_status.code() {
|
||||
Some(val) => ensure!(val == 0, "command returned with exit code {val}"),
|
||||
None => bail!("could not {verb} packages for {}", backend.get_binary()),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn show(&self, indentend: bool) {
|
||||
for (backend, packages) in self.iter() {
|
||||
if packages.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if indentend {
|
||||
print!(" ");
|
||||
}
|
||||
println!("[{}]", backend.get_section());
|
||||
for package in packages {
|
||||
if indentend {
|
||||
print!(" ");
|
||||
}
|
||||
println!(" {package}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user