mod macros; mod pacman; mod rust; mod todo_per_backend; use std::collections::HashSet; use std::os::unix::process::CommandExt; use std::process::Command; use anyhow::{Context, Result}; use crate::register_backends; use crate::{Group, Package}; pub(crate) use todo_per_backend::ToDoPerBackend; pub(in crate::backend) type Switches = &'static [&'static str]; pub(in crate::backend) type Text = &'static str; register_backends!(Pacman, Rust); // TODO find a way to include this in the `register_backends!` macro pub(crate) use pacman::Pacman; pub(crate) use rust::Rust; // TODO find a way to include this in the `register_backends!` macro impl Backends { fn next(&self) -> Option { match self { Self::Pacman => Some(Self::Rust), Self::Rust => None, } } } #[derive(Debug)] pub(crate) struct BackendIter { next: Option, } impl Iterator for BackendIter { type Item = Box; fn next(&mut self) -> Option { match &self.next { None => None, Some(b) => { let result = b.get_backend(); self.next = b.next(); Some(result) } } } } pub(crate) trait Backend { fn get_binary(&self) -> Text; fn get_section(&self) -> Text; fn get_switches_install(&self) -> Switches; fn get_switches_remove(&self) -> Switches; fn get_managed_packages(&self) -> &HashSet; fn load(&mut self, groups: &HashSet); /// Get all packages that are installed in the system. fn get_all_installed_packages(&self) -> Result>; /// Get all packages that were installed in the system explicitly. fn get_explicitly_installed_packages(&self) -> Result>; /// Install the specified packages. fn install_packages(&self, packages: &[Package]) { if packages.is_empty() { return; } let mut cmd = Command::new(self.get_binary()); cmd.args(self.get_switches_install()); for p in packages { cmd.arg(format!("{p}")); } cmd.exec(); } /// Remove the specified packages. fn remove_packages(&self, packages: Vec) { let mut cmd = Command::new(self.get_binary()); cmd.args(self.get_switches_remove()); for p in packages { cmd.arg(format!("{p}")); } cmd.exec(); } /// extract packages from its own section as read from group files fn extract_packages_from_group_file_content(&self, content: &str) -> HashSet { 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> { 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); fn get_unmanaged_packages_sorted(&self) -> Result> { let installed = self .get_explicitly_installed_packages() .context("could not get explicitly installed packages")?; let required = self.get_managed_packages(); let mut diff: Vec<_> = installed.difference(required).cloned().collect(); diff.sort_unstable(); Ok(diff) } }