Skip to content

Commit

Permalink
chore: Pass Package to traits
Browse files Browse the repository at this point in the history
So the commands can be tweaked. Eg. if a package is installed from an
URL, we can chose to ignore the gpg signing.
  • Loading branch information
dilawar committed Apr 7, 2024
1 parent f1833c5 commit 63bac87
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 39 deletions.
46 changes: 28 additions & 18 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ pub trait PackageManager: PackageManagerCommands + std::fmt::Debug + std::fmt::D
/// Return the list of supported package extensions.
fn supported_pkg_formats(&self) -> Vec<PkgFormat>;

/// Get a formatted string of the package that can be passed into package manager's cli.
/// Get a formatted string of the package that can be passed into package
/// manager's cli.
///
/// If package URL is set, the url is passed to cli. Note that not all package manager supports
/// installing using url. For now, we rely on package manager to handle it.
fn reformat_for_command<P: Into<Package>>(&self, pkg: P) -> String {
let pkg: Package = pkg.into();
/// If package URL is set, the url is passed to cli. Note that not all
/// package manager supports installing using url. For now, we rely on
/// package manager to handle it.
fn reformat_for_command(&self, pkg: &Package) -> String {
if let Some(url) = pkg.url() {
return url.to_string();
}
Expand Down Expand Up @@ -78,45 +79,48 @@ pub trait PackageManager: PackageManagerCommands + std::fmt::Debug + std::fmt::D

/// General package search
fn search(&self, query: &str) -> Vec<Package> {
let cmds = self.consolidated(Cmd::Search, &[query.to_string()]);
let cmds = self.consolidated(Cmd::Search, None, &[query.to_string()]);
let out = self.exec_cmds(&cmds);
self.parse_output(&out.stdout)
}

/// Sync package manaager repositories
fn sync(&self) -> std::process::ExitStatus {
self.exec_cmds_status(&self.consolidated::<&str>(Cmd::Sync, &[]))
self.exec_cmds_status(&self.consolidated::<&str>(Cmd::Sync, None, &[]))
}

/// Update/upgrade all packages
fn update_all(&self) -> std::process::ExitStatus {
self.exec_cmds_status(&self.consolidated::<&str>(Cmd::UpdateAll, &[]))
self.exec_cmds_status(&self.consolidated::<&str>(Cmd::UpdateAll, None, &[]))
}

/// Install a single package
///
/// For multi-package operations, see [``PackageManager::execute_pkg_command``]
/// For multi-package operations, see
/// [``PackageManager::execute_pkg_command``]
fn install<P: Into<Package> + Clone>(&self, pkg: P) -> std::process::ExitStatus {
self.execute_pkg_command(pkg, Operation::Install)
}

/// Uninstall a single package
///
/// For multi-package operations, see [``PackageManager::execute_pkg_command``]
/// For multi-package operations, see
/// [``PackageManager::execute_pkg_command``]
fn uninstall<P: Into<Package> + Clone>(&self, pkg: P) -> std::process::ExitStatus {
self.execute_pkg_command(pkg, Operation::Uninstall)
}

/// Update a single package
///
/// For multi-package operations, see [``PackageManager::execute_pkg_command``]
/// For multi-package operations, see
/// [``PackageManager::execute_pkg_command``]
fn update<P: Into<Package> + Clone>(&self, pkg: P) -> std::process::ExitStatus {
self.execute_pkg_command(pkg, Operation::Update)
}

/// List installed packages
fn list_installed(&self) -> Vec<Package> {
let out = self.exec_cmds(&self.consolidated::<&str>(Cmd::List, &[]));
let out = self.exec_cmds(&self.consolidated::<&str>(Cmd::List, None, &[]));
self.parse_output(&out.stdout)
}

Expand All @@ -132,8 +136,9 @@ pub trait PackageManager: PackageManagerCommands + std::fmt::Debug + std::fmt::D
Operation::Update => Cmd::Update,
};

let fmt = self.reformat_for_command(pkg);
let cmds = self.consolidated(command, &[fmt]);
let pkg = pkg.into();
let fmt = self.reformat_for_command(&pkg);
let cmds = self.consolidated(command, Some(&pkg), &[fmt]);
self.exec_cmds_status(&cmds)
}

Expand All @@ -143,7 +148,7 @@ pub trait PackageManager: PackageManagerCommands + std::fmt::Debug + std::fmt::D
/// managers this method returns a `Result` instead of the usual
/// `std::process::ExitStatus`.
fn add_repo(&self, repo: &str) -> anyhow::Result<()> {
let cmds = self.consolidated(Cmd::AddRepo, &[repo.to_string()]);
let cmds = self.consolidated(Cmd::AddRepo, None, &[repo.to_string()]);
let s = self.exec_cmds_status(&cmds);
anyhow::ensure!(s.success(), "Error adding repo");
Ok(())
Expand Down Expand Up @@ -228,7 +233,7 @@ pub trait PackageManagerCommands {

/// Returns the appropriate command/s for the given supported command type.
/// Check [``crate::common::Cmd``] enum to see all supported commands.
fn get_cmds(&self, cmd: Cmd) -> Vec<String>;
fn get_cmds(&self, cmd: Cmd, pkg: Option<&Package>) -> Vec<String>;

/// Returns the appropriate flags for the given command type. Check
/// [``crate::common::Cmd``] enum to see all supported commands.
Expand All @@ -247,8 +252,13 @@ pub trait PackageManagerCommands {
/// enum [``crate::common::Cmd``] For finer control, a general purpose
/// function [``consolidated_args``] is also provided.
#[inline]
fn consolidated<S: AsRef<str>>(&self, cmd: Cmd, args: &[S]) -> Vec<String> {
let mut commands = self.get_cmds(cmd);
fn consolidated<S: AsRef<str>>(
&self,
cmd: Cmd,
pkg: Option<&Package>,
args: &[S],
) -> Vec<String> {
let mut commands = self.get_cmds(cmd, pkg);
commands.append(&mut self.get_flags(cmd));
commands.append(&mut args.iter().map(|x| x.as_ref().to_string()).collect());
commands
Expand Down
13 changes: 8 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ mod tests {
fn cmd(&self) -> Command {
Command::new("")
}
fn get_cmds(&self, _: crate::Cmd) -> Vec<String> {
fn get_cmds(&self, _: crate::Cmd, _: Option<&Package>) -> Vec<String> {
vec!["command".to_string()]
}
fn get_flags(&self, _: crate::Cmd) -> Vec<String> {
Expand Down Expand Up @@ -91,7 +91,7 @@ mod tests {
fn cmd(&self) -> Command {
Command::new("")
}
fn get_cmds(&self, _: Cmd) -> Vec<String> {
fn get_cmds(&self, _: Cmd, _: Option<&Package>) -> Vec<String> {
vec!["".to_string()]
}
fn exec_cmds(&self, _: &[String]) -> Output {
Expand All @@ -111,7 +111,7 @@ mod tests {
#[test]
fn default_cmd_consolidated_order() {
let mock = MockCommands;
let con = mock.consolidated(Cmd::Install, &["arg"]);
let con = mock.consolidated(Cmd::Install, None, &["arg"]);
let mut coniter = con.into_iter();
assert_eq!(coniter.next(), Some("command".to_string()));
assert_eq!(coniter.next(), Some("flag".to_string()));
Expand All @@ -134,9 +134,12 @@ mod tests {

#[test]
fn package_formatting() {
assert_eq!(MockPackageManager.reformat_for_command("foo"), "foo");
assert_eq!(
MockPackageManager.reformat_for_command("foo@0.1.2"),
MockPackageManager.reformat_for_command(&"foo".into()),
"foo"
);
assert_eq!(
MockPackageManager.reformat_for_command(&"foo@0.1.2".into()),
"foo+0.1.2"
);
}
Expand Down
7 changes: 5 additions & 2 deletions src/managers/apt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl PackageManagerCommands for AdvancedPackageTool {
fn cmd(&self) -> Command {
Command::new("apt-get")
}
fn get_cmds(&self, cmd: Cmd) -> Vec<String> {
fn get_cmds(&self, cmd: Cmd, _: Option<&Package>) -> Vec<String> {
match cmd {
Cmd::Install => vec!["install"],
Cmd::Uninstall => vec!["remove"],
Expand Down Expand Up @@ -165,7 +165,10 @@ mysql-common/now 5.8+1.1.0 all [installed,local]"#;
Cmd::Search | Cmd::List => alt,
_ => reg,
};
assert_eq!(apt.alt_cmd(&apt.get_cmds(*cmd)).get_program(), should_match);
assert_eq!(
apt.alt_cmd(&apt.get_cmds(*cmd, None)).get_program(),
should_match
);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/managers/brew.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{fmt::Display, process::Command};

use crate::{Cmd, PackageManager, PackageManagerCommands, PkgFormat};
use crate::{Cmd, Package, PackageManager, PackageManagerCommands, PkgFormat};

/// Wrapper for the Homebrew package manager.
///
Expand All @@ -23,7 +23,7 @@ impl PackageManagerCommands for Homebrew {
Command::new("brew")
}

fn get_cmds(&self, cmd: Cmd) -> Vec<String> {
fn get_cmds(&self, cmd: Cmd, _pkg: Option<&Package>) -> Vec<String> {
match cmd {
Cmd::Install => vec!["install"],
Cmd::Uninstall => vec!["uninstall"],
Expand Down
9 changes: 4 additions & 5 deletions src/managers/choco.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ impl PackageManager for Chocolatey {
fn pkg_delimiter(&self) -> char {
'|'
}
fn reformat_for_command<P: Into<Package>>(&self, pkg: P) -> String {
let pkg = pkg.into();
fn reformat_for_command(&self, pkg: &Package) -> String {
if let Some(v) = pkg.version() {
format!("{} --version {}", pkg.name(), v)
} else {
Expand All @@ -30,7 +29,7 @@ impl PackageManagerCommands for Chocolatey {
fn cmd(&self) -> Command {
Command::new("choco")
}
fn get_cmds(&self, cmd: Cmd) -> Vec<String> {
fn get_cmds(&self, cmd: Cmd, _pkg: Option<&Package>) -> Vec<String> {
match cmd {
Cmd::Install => vec!["install"],
Cmd::Uninstall => vec!["uninstall"],
Expand Down Expand Up @@ -73,11 +72,11 @@ mod tests {
#[test]
fn test_choco_pkg_fmt() {
assert_eq!(
Chocolatey.reformat_for_command("package"),
Chocolatey.reformat_for_command(&"package".into()),
"package".to_string()
);
assert_eq!(
&Chocolatey.reformat_for_command("package@0.1.0"),
&Chocolatey.reformat_for_command(&"package@0.1.0".into()),
"package --version 0.1.0"
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/managers/dnf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl PackageManager for DandifiedYUM {
"failed to install config-manager plugin"
);

let s = self.exec_cmds_status(&self.consolidated(Cmd::AddRepo, &[repo]));
let s = self.exec_cmds_status(&self.consolidated(Cmd::AddRepo, None, &[repo]));
anyhow::ensure!(s.success(), "failed to add repo");
Ok(())
}
Expand All @@ -58,7 +58,7 @@ impl PackageManagerCommands for DandifiedYUM {
Command::new("dnf")
}

fn get_cmds(&self, cmd: Cmd) -> Vec<String> {
fn get_cmds(&self, cmd: Cmd, _pkg: Option<&Package>) -> Vec<String> {
match cmd {
Cmd::Install => vec!["install"],
Cmd::Uninstall => vec!["remove"],
Expand Down
8 changes: 5 additions & 3 deletions src/managers/yum.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::{fmt::Display, process::Command};

use crate::{managers::DandifiedYUM, Cmd, PackageManager, PackageManagerCommands, PkgFormat};
use crate::{
managers::DandifiedYUM, Cmd, Package, PackageManager, PackageManagerCommands, PkgFormat,
};

/// Wrapper for Yellowdog Updater Modified (YUM) package manager.
///
Expand Down Expand Up @@ -45,8 +47,8 @@ impl PackageManagerCommands for YellowdogUpdaterModified {
fn cmd(&self) -> Command {
Command::new("yum")
}
fn get_cmds(&self, cmd: crate::Cmd) -> Vec<String> {
self.0.get_cmds(cmd)
fn get_cmds(&self, cmd: crate::Cmd, pkg: Option<&Package>) -> Vec<String> {
self.0.get_cmds(cmd, pkg)
}
fn get_flags(&self, cmd: Cmd) -> Vec<String> {
self.0.get_flags(cmd)
Expand Down
7 changes: 5 additions & 2 deletions src/managers/zypper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl PackageManager for Zypper {
);

anyhow::ensure!(
self.exec_cmds_status(&self.consolidated(Cmd::AddRepo, &[repo]))
self.exec_cmds_status(&self.consolidated(Cmd::AddRepo, None, &[repo]))
.success(),
"Failed to add repo"
);
Expand All @@ -86,7 +86,7 @@ impl PackageManagerCommands for Zypper {
Command::new("zypper")
}

fn get_cmds(&self, cmd: Cmd) -> Vec<String> {
fn get_cmds(&self, cmd: Cmd, pkg: Option<&Package>) -> Vec<String> {
let mut cmd: Vec<_> = match cmd {
Cmd::Install => vec!["install"],
Cmd::Uninstall => vec!["remove"],
Expand All @@ -103,6 +103,9 @@ impl PackageManagerCommands for Zypper {

// run zypper in non-interactive mode.
cmd.insert(0, "-n".to_string());
if pkg.and_then(|p| p.url()).is_some() {
cmd.insert(1, "--no-gpg-check".to_string());
}
cmd
}

Expand Down

0 comments on commit 63bac87

Please sign in to comment.