diff --git a/README.md b/README.md index 512fed5..96a6975 100644 --- a/README.md +++ b/README.md @@ -85,12 +85,13 @@ Note that the name of the section corresponds to the ecosystem it relates to, ra At the moment, supported backends are the following. Pull requests for additional backends are welcome! -| Application | Package Manager | Section | feature flag | Notes | -|-------------|-----------------|-----------|--------------|-----------------------------------------------------------------------------------------------------------| -| Arch Linux | `pacman` | `[arch]` | `arch` | includes pacman-wrapping AUR helpers (configurable) | -| Debian | `apt` | `[debian]`| `debian` | minimum supported apt-version unknown ([upstream issue](https://gitlab.com/volian/rust-apt/-/issues/20)) | -| Python | `pip` | `[python]`| built-in | | -| Rust | `cargo` | `[rust]` | built-in | | +| Application | Package Manager | Section | feature flag | Notes | +|-------------|-----------------|-------------|--------------|-----------------------------------------------------------------------------------------------------------| +| Arch Linux | `pacman` | `[arch]` | `arch` | includes pacman-wrapping AUR helpers (configurable) | +| Debian | `apt` | `[debian]` | `debian` | minimum supported apt-version unknown ([upstream issue](https://gitlab.com/volian/rust-apt/-/issues/20)) | +| Flatpak | `flatpak` | `[flatpak]` | built-in | can manage either system-wide or per-user installation (configurable) | +| Python | `pip` | `[python]` | built-in | | +| Rust | `cargo` | `[rust]` | built-in | | Backends that have a `feature flag` require setting the respective flag for the build process. The appropriate system libraries and their header files must be present on the machine and be detectable by `pkg-config`. diff --git a/crates/pacdef_core/src/backend/actual/flatpak.rs b/crates/pacdef_core/src/backend/actual/flatpak.rs new file mode 100644 index 0000000..47bcf9e --- /dev/null +++ b/crates/pacdef_core/src/backend/actual/flatpak.rs @@ -0,0 +1,121 @@ +use std::collections::HashSet; +use std::process::Command; +use std::process::ExitStatus; + +use anyhow::{Context, Result}; + +use crate::backend::backend_trait::{Backend, Switches, Text}; +use crate::{impl_backend_constants, Group, Package}; + +#[derive(Debug, Clone)] +pub struct Flatpak { + pub(crate) packages: HashSet, + pub(crate) systemwide: bool, +} + +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 get_all_installed_packages(&self) -> Result> { + self.get_installed_packages(true) + } + + fn get_explicitly_installed_packages(&self) -> Result> { + self.get_installed_packages(false) + } + + /// Install the specified packages. + fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result { + let mut cmd = Command::new(self.get_binary()); + cmd.args(self.get_switches_install()); + cmd.args(self.get_switches_runtime()); + + if noconfirm { + cmd.args(self.get_switches_noconfirm()); + } + + for p in packages { + cmd.arg(format!("{p}")); + } + + cmd.status() + .with_context(|| format!("running command {cmd:?}")) + } + + fn make_dependency(&self, _: &[Package]) -> Result { + panic!("not supported by {}", 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()); + cmd.args(self.get_switches_runtime()); + + if noconfirm { + cmd.args(self.get_switches_noconfirm()); + } + + for p in packages { + cmd.arg(format!("{p}")); + } + + cmd.status() + .with_context(|| format!("running command [{cmd:?}]")) + } + + /// 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.args(self.get_switches_runtime()); + cmd.arg(format!("{package}")); + cmd.status() + .with_context(|| format!("running command {cmd:?}")) + } +} + +impl Flatpak { + pub(crate) fn new() -> Self { + Self { + packages: HashSet::new(), + systemwide: true, + } + } + + fn get_switches_runtime(&self) -> Switches { + if self.systemwide { + &[] + } else { + &["--user"] + } + } + + fn get_installed_packages(&self, include_implicit: bool) -> Result> { + let mut cmd = Command::new(BINARY); + cmd.args(["list", "--columns=application"]); + if !include_implicit { + cmd.arg("--app"); + } + if !self.systemwide { + cmd.arg("--user"); + } + + let output = String::from_utf8(cmd.output()?.stdout)?; + Ok(output + .lines() + .map(Package::from) + .collect::>()) + } +} diff --git a/crates/pacdef_core/src/backend/actual/mod.rs b/crates/pacdef_core/src/backend/actual/mod.rs index c714208..a30c8c1 100644 --- a/crates/pacdef_core/src/backend/actual/mod.rs +++ b/crates/pacdef_core/src/backend/actual/mod.rs @@ -2,5 +2,6 @@ pub mod arch; #[cfg(feature = "debian")] pub mod debian; +pub mod flatpak; pub mod python; pub mod rust; diff --git a/crates/pacdef_core/src/backend/backend_trait.rs b/crates/pacdef_core/src/backend/backend_trait.rs index 8e6be52..98cfddb 100644 --- a/crates/pacdef_core/src/backend/backend_trait.rs +++ b/crates/pacdef_core/src/backend/backend_trait.rs @@ -177,7 +177,7 @@ where let mut map = HashMap::new(); for (value, key) in to_assign { - let inner = map.entry(key).or_insert(vec![]); + let inner: &mut Vec = map.entry(key).or_default(); inner.push(value); } diff --git a/crates/pacdef_core/src/backend/mod.rs b/crates/pacdef_core/src/backend/mod.rs index c8c10f1..7e39d3d 100644 --- a/crates/pacdef_core/src/backend/mod.rs +++ b/crates/pacdef_core/src/backend/mod.rs @@ -16,6 +16,7 @@ pub enum Backends { Arch, #[cfg(feature = "debian")] Debian, + Flatpak, Python, Rust, } diff --git a/crates/pacdef_core/src/config.rs b/crates/pacdef_core/src/config.rs index e0e39f8..d791425 100644 --- a/crates/pacdef_core/src/config.rs +++ b/crates/pacdef_core/src/config.rs @@ -13,6 +13,8 @@ pub struct Config { pub aur_helper: String, /// Additional arguments to pass to `aur_helper` when removing a package. pub aur_rm_args: Option>, + /// Install Flatpak packages system-wide + pub flatpak_systemwide: bool, /// Warn the user when a group is not a symlink. pub warn_not_symlinks: bool, /// Backends the user does not want to use even though the binary exists. @@ -68,6 +70,7 @@ impl Default for Config { Self { aur_helper: "paru".into(), aur_rm_args: None, + flatpak_systemwide: true, warn_not_symlinks: true, disabled_backends: vec![], } diff --git a/crates/pacdef_core/src/core.rs b/crates/pacdef_core/src/core.rs index 7a1cb67..63110d3 100644 --- a/crates/pacdef_core/src/core.rs +++ b/crates/pacdef_core/src/core.rs @@ -128,6 +128,13 @@ impl Pacdef { arch.aur_rm_args = self.config.aur_rm_args.take(); } } + + if let Some(flatpak) = backend + .as_any_mut() + .downcast_mut::() + { + flatpak.systemwide = self.config.flatpak_systemwide; + } } fn install_packages(&mut self, args: &ArgMatches) -> Result<()> {