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:
@@ -0,0 +1,129 @@
|
||||
use std::collections::HashSet;
|
||||
use std::process::Command;
|
||||
|
||||
use alpm::Alpm;
|
||||
use alpm::PackageReason::Explicit;
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use crate::backend::backend_trait::{Backend, Switches, Text};
|
||||
use crate::backend::macros::impl_backend_constants;
|
||||
use crate::cmd::run_external_command;
|
||||
use crate::{Group, Package};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Arch {
|
||||
pub(crate) binary: String,
|
||||
pub(crate) aur_rm_args: Vec<String>,
|
||||
pub(crate) packages: HashSet<Package>,
|
||||
}
|
||||
|
||||
const BINARY: Text = "pacman";
|
||||
const SECTION: Text = "arch";
|
||||
|
||||
const SWITCHES_INFO: Switches = &["--query", "--info"];
|
||||
const SWITCHES_INSTALL: Switches = &["--sync"];
|
||||
const SWITCHES_MAKE_DEPENDENCY: Switches = &["--database", "--asdeps"];
|
||||
const SWITCHES_NOCONFIRM: Switches = &["--noconfirm"];
|
||||
const SWITCHES_REMOVE: Switches = &["--remove", "--recursive"];
|
||||
|
||||
const SUPPORTS_AS_DEPENDENCY: bool = true;
|
||||
|
||||
impl Backend for Arch {
|
||||
impl_backend_constants!();
|
||||
|
||||
fn get_binary(&self) -> Text {
|
||||
let r#box = self.binary.clone().into_boxed_str();
|
||||
Box::leak(r#box)
|
||||
}
|
||||
|
||||
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], noconfirm: bool) -> Result<()> {
|
||||
let mut cmd = Command::new(&self.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)
|
||||
}
|
||||
|
||||
/// Remove the specified packages.
|
||||
fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> {
|
||||
let mut cmd = Command::new(&self.binary);
|
||||
|
||||
cmd.args(self.get_switches_remove());
|
||||
cmd.args(&self.aur_rm_args);
|
||||
|
||||
if noconfirm {
|
||||
cmd.args(self.get_switches_noconfirm());
|
||||
}
|
||||
|
||||
for p in packages {
|
||||
cmd.arg(format!("{p}"));
|
||||
}
|
||||
|
||||
run_external_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 Arch {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
binary: BINARY.to_string(),
|
||||
aur_rm_args: vec![],
|
||||
packages: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use anyhow::Result;
|
||||
use rust_apt::cache::PackageSort;
|
||||
use rust_apt::new_cache;
|
||||
|
||||
use crate::backend::backend_trait::{Backend, Switches, Text};
|
||||
use crate::backend::macros::impl_backend_constants;
|
||||
use crate::backend::root::build_base_command_with_privileges;
|
||||
use crate::cmd::run_external_command;
|
||||
use crate::{Group, Package};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Debian {
|
||||
pub(crate) packages: HashSet<Package>,
|
||||
}
|
||||
|
||||
const BINARY: Text = "apt";
|
||||
const SECTION: Text = "debian";
|
||||
|
||||
const SWITCHES_INFO: Switches = &["show"];
|
||||
const SWITCHES_INSTALL: Switches = &["install"];
|
||||
const SWITCHES_MAKE_DEPENDENCY: Switches = &[]; // not needed
|
||||
const SWITCHES_NOCONFIRM: Switches = &["--yes"];
|
||||
const SWITCHES_REMOVE: Switches = &["remove"];
|
||||
|
||||
const SUPPORTS_AS_DEPENDENCY: bool = true;
|
||||
|
||||
impl Backend for Debian {
|
||||
impl_backend_constants!();
|
||||
|
||||
fn get_all_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
let cache = new_cache!()?;
|
||||
let sort = PackageSort::default().installed();
|
||||
|
||||
let mut result = HashSet::new();
|
||||
for pkg in cache.packages(&sort)? {
|
||||
result.insert(Package::from(pkg.name().to_string()));
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn get_explicitly_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
let cache = new_cache!()?;
|
||||
let sort = PackageSort::default().installed().manually_installed();
|
||||
|
||||
let mut result = HashSet::new();
|
||||
for pkg in cache.packages(&sort)? {
|
||||
result.insert(Package::from(pkg.name().to_string()));
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn make_dependency(&self, packages: &[Package]) -> Result<()> {
|
||||
let mut cmd = build_base_command_with_privileges("apt-mark");
|
||||
cmd.arg("auto");
|
||||
for p in packages {
|
||||
cmd.arg(format!("{p}"));
|
||||
}
|
||||
|
||||
run_external_command(cmd)
|
||||
}
|
||||
|
||||
/// Install the specified packages.
|
||||
fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> {
|
||||
let mut cmd = build_base_command_with_privileges(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)
|
||||
}
|
||||
|
||||
/// Remove the specified packages.
|
||||
fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> {
|
||||
let mut cmd = build_base_command_with_privileges(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)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debian {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
packages: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
use std::collections::HashSet;
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::backend::backend_trait::{Backend, Switches, Text};
|
||||
use crate::backend::macros::impl_backend_constants;
|
||||
use crate::cmd::run_external_command;
|
||||
use crate::{Group, Package};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Fedora {
|
||||
pub(crate) packages: HashSet<Package>,
|
||||
}
|
||||
|
||||
const BINARY: Text = "dnf";
|
||||
const SECTION: Text = "fedora";
|
||||
|
||||
const SWITCHES_INFO: Switches = &["info"];
|
||||
const SWITCHES_INSTALL: Switches = &["install"];
|
||||
const SWITCHES_MAKE_DEPENDENCY: Switches = &[];
|
||||
const SWITCHES_NOCONFIRM: Switches = &["--assumeyes"];
|
||||
const SWITCHES_REMOVE: Switches = &["remove"];
|
||||
|
||||
const SUPPORTS_AS_DEPENDENCY: bool = false;
|
||||
|
||||
/// These switches are responsible for
|
||||
/// getting the packages explicitly installed by the user
|
||||
const SWITCHES_FETCH_USER: Switches = &[
|
||||
"repoquery",
|
||||
"--userinstalled",
|
||||
"--queryformat",
|
||||
"%{from_repo}/%{name}",
|
||||
];
|
||||
|
||||
/// These switches are responsible for
|
||||
/// getting all the packages installed on the system
|
||||
const SWITCHES_FETCH_GLOBAL: Switches = &[
|
||||
"repoquery",
|
||||
"--installed",
|
||||
"--queryformat",
|
||||
"%{from_repo}/%{name}",
|
||||
];
|
||||
|
||||
/// These repositories are ignored when storing the packages
|
||||
/// as these are present by default on any sane fedora system
|
||||
const DEFAULT_REPOS: [&str; 5] = ["koji", "fedora", "updates", "anaconda", "@"];
|
||||
|
||||
impl Backend for Fedora {
|
||||
impl_backend_constants!();
|
||||
|
||||
fn get_all_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
cmd.args(SWITCHES_FETCH_GLOBAL);
|
||||
|
||||
let output = String::from_utf8(cmd.output()?.stdout)?;
|
||||
let packages = output.lines().map(create_package).collect();
|
||||
|
||||
Ok(packages)
|
||||
}
|
||||
|
||||
fn get_explicitly_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
cmd.args(SWITCHES_FETCH_USER);
|
||||
|
||||
let output = String::from_utf8(cmd.output()?.stdout)?;
|
||||
let packages = output.lines().map(create_package).collect();
|
||||
|
||||
Ok(packages)
|
||||
}
|
||||
|
||||
/// Install the specified packages.
|
||||
fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> {
|
||||
let mut cmd = Command::new("sudo");
|
||||
cmd.arg(self.get_binary());
|
||||
cmd.args(self.get_switches_install());
|
||||
|
||||
if noconfirm {
|
||||
cmd.args(self.get_switches_noconfirm());
|
||||
}
|
||||
|
||||
for p in packages {
|
||||
cmd.arg(&p.name);
|
||||
if let Some(repo) = p.repo.as_ref() {
|
||||
cmd.args(["--repo", repo]);
|
||||
}
|
||||
}
|
||||
|
||||
// add these two repositories as these are needed for many dependencies
|
||||
cmd.args(["--repo", "updates"]);
|
||||
cmd.args(["--repo", "fedora"]);
|
||||
|
||||
run_external_command(cmd)
|
||||
}
|
||||
|
||||
/// Show information from package manager for package.
|
||||
fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> {
|
||||
let mut cmd = Command::new("sudo");
|
||||
cmd.arg(self.get_binary());
|
||||
cmd.args(self.get_switches_remove());
|
||||
|
||||
if noconfirm {
|
||||
cmd.args(self.get_switches_noconfirm());
|
||||
}
|
||||
|
||||
for p in packages {
|
||||
cmd.arg(&p.name);
|
||||
}
|
||||
|
||||
run_external_command(cmd)
|
||||
}
|
||||
|
||||
fn show_package_info(&self, package: &Package) -> Result<()> {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
cmd.args(self.get_switches_info());
|
||||
cmd.arg(&package.name);
|
||||
|
||||
run_external_command(cmd)
|
||||
}
|
||||
|
||||
fn make_dependency(&self, _: &[Package]) -> Result<()> {
|
||||
panic!("Not supported by the package manager!")
|
||||
}
|
||||
}
|
||||
|
||||
impl Fedora {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
packages: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_package(package: &str) -> Package {
|
||||
if DEFAULT_REPOS.iter().any(|repo| package.contains(repo)) && !package.contains("copr") {
|
||||
let package = package.split('/').nth(1).expect("Cannot be empty!");
|
||||
package.into()
|
||||
} else {
|
||||
package.into()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
use std::collections::HashSet;
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::backend::backend_trait::{Backend, Switches, Text};
|
||||
use crate::backend::macros::impl_backend_constants;
|
||||
use crate::cmd::run_external_command;
|
||||
use crate::{Group, Package};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Flatpak {
|
||||
pub(crate) packages: HashSet<Package>,
|
||||
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<HashSet<Package>> {
|
||||
self.get_installed_packages(true)
|
||||
}
|
||||
|
||||
fn get_explicitly_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
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}"));
|
||||
}
|
||||
|
||||
run_external_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}"));
|
||||
}
|
||||
|
||||
run_external_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}"));
|
||||
|
||||
run_external_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<HashSet<Package>> {
|
||||
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::<HashSet<Package>>())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#[cfg(feature = "arch")]
|
||||
pub mod arch;
|
||||
#[cfg(feature = "debian")]
|
||||
pub mod debian;
|
||||
pub mod fedora;
|
||||
pub mod flatpak;
|
||||
pub mod python;
|
||||
pub mod rust;
|
||||
pub mod rustup;
|
||||
pub mod void;
|
||||
@@ -0,0 +1,118 @@
|
||||
use std::collections::HashSet;
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::backend::backend_trait::{Backend, Switches, Text};
|
||||
use crate::backend::macros::impl_backend_constants;
|
||||
use crate::{Group, Package};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Python {
|
||||
pub(crate) binary: String,
|
||||
pub(crate) packages: HashSet<Package>,
|
||||
}
|
||||
|
||||
const BINARY: Text = "pip";
|
||||
const SECTION: Text = "python";
|
||||
|
||||
const SWITCHES_INFO: Switches = &["show"];
|
||||
const SWITCHES_INSTALL: Switches = &["install"];
|
||||
const SWITCHES_MAKE_DEPENDENCY: Switches = &[]; // not needed
|
||||
const SWITCHES_NOCONFIRM: Switches = &[]; // not needed
|
||||
const SWITCHES_REMOVE: Switches = &["uninstall"];
|
||||
|
||||
const SUPPORTS_AS_DEPENDENCY: bool = false;
|
||||
|
||||
macro_rules! ERROR{
|
||||
($bin:expr) => {
|
||||
panic!("Cannot use {} for package management in python. Please use a valid package manager like pip or pipx.", $bin)
|
||||
};
|
||||
}
|
||||
|
||||
impl Backend for Python {
|
||||
impl_backend_constants!();
|
||||
|
||||
fn get_binary(&self) -> Text {
|
||||
let r#box = self.binary.clone().into_boxed_str();
|
||||
Box::leak(r#box)
|
||||
}
|
||||
|
||||
fn get_all_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
let output = run_pip_command(&mut cmd, self.get_switches_runtime())?;
|
||||
self.extract_packages(output)
|
||||
}
|
||||
|
||||
fn get_explicitly_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
let output = run_pip_command(&mut cmd, self.get_switches_explicit())?;
|
||||
self.extract_packages(output)
|
||||
}
|
||||
|
||||
fn make_dependency(&self, _packages: &[Package]) -> Result<()> {
|
||||
panic!("not supported by {}", BINARY)
|
||||
}
|
||||
}
|
||||
|
||||
fn run_pip_command(cmd: &mut Command, args: &[&str]) -> Result<Value> {
|
||||
cmd.args(args);
|
||||
let output = String::from_utf8(cmd.output()?.stdout)?;
|
||||
let val: Value = serde_json::from_str(&output)?;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
impl Python {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
binary: BINARY.to_string(),
|
||||
packages: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_switches_runtime(&self) -> Switches {
|
||||
match self.get_binary() {
|
||||
"pip" => &["list", "--format", "json", "--not-required", "--user"],
|
||||
"pipx" => &["list", "--json"],
|
||||
_ => ERROR!(self.get_binary()),
|
||||
}
|
||||
}
|
||||
fn get_switches_explicit(&self) -> Switches {
|
||||
match self.get_binary() {
|
||||
"pip" => &["list", "--format", "json", "--user"],
|
||||
"pipx" => &["list", "--json"],
|
||||
_ => ERROR!(self.get_binary()),
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_packages(&self, output: Value) -> Result<HashSet<Package>> {
|
||||
match self.get_binary() {
|
||||
"pip" => extract_pacdef_packages(output),
|
||||
"pipx" => extract_pacdef_packages_pipx(output),
|
||||
_ => ERROR!(self.get_binary()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_pacdef_packages(value: Value) -> Result<HashSet<Package>> {
|
||||
let result = value
|
||||
.as_array()
|
||||
.context("getting inner json array")?
|
||||
.iter()
|
||||
.map(|node| node["name"].as_str().expect("should always be a string"))
|
||||
.map(Package::from)
|
||||
.collect();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn extract_pacdef_packages_pipx(value: Value) -> Result<HashSet<Package>> {
|
||||
let result = value["venvs"]
|
||||
.as_object()
|
||||
.context("getting inner json object")?
|
||||
.iter()
|
||||
.map(|(name, _)| Package::from(name.as_str()))
|
||||
.collect();
|
||||
Ok(result)
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
use std::collections::HashSet;
|
||||
use std::fs::read_to_string;
|
||||
use std::io::ErrorKind::NotFound;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::backend::backend_trait::{Backend, Switches, Text};
|
||||
use crate::backend::macros::impl_backend_constants;
|
||||
use crate::{Group, Package};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
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_NOCONFIRM: Switches = &[]; // not needed
|
||||
const SWITCHES_REMOVE: Switches = &["uninstall"];
|
||||
|
||||
const SUPPORTS_AS_DEPENDENCY: bool = false;
|
||||
|
||||
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 = match read_to_string(file) {
|
||||
Ok(string) => string,
|
||||
Err(err) if err.kind() == NotFound => {
|
||||
eprintln!(
|
||||
"WARNING: no crates file found for cargo. Assuming no crates installed yet."
|
||||
);
|
||||
return Ok(HashSet::new());
|
||||
}
|
||||
Err(err) => bail!(err),
|
||||
};
|
||||
|
||||
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<()> {
|
||||
panic!("not supported by {}", BINARY)
|
||||
}
|
||||
}
|
||||
|
||||
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_cargo_home().context("getting cargo home dir")?;
|
||||
result.push(".crates2.json");
|
||||
Ok(result)
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
use super::types::RustupPackage;
|
||||
|
||||
pub fn toolchain_of_component_was_already_removed(
|
||||
removed_toolchains: &[String],
|
||||
component: &RustupPackage,
|
||||
) -> bool {
|
||||
removed_toolchains.contains(&component.toolchain)
|
||||
}
|
||||
|
||||
pub fn install_components(line: &str, toolchain: &str, val: &mut Vec<String>) {
|
||||
let mut chunks = line.splitn(3, '-');
|
||||
let component = chunks.next().expect("Component name is empty!");
|
||||
match component {
|
||||
// these are the only components that have a single word name
|
||||
"cargo" | "rustfmt" | "clippy" | "miri" | "rls" | "rustc" => {
|
||||
val.push([toolchain, component].join("/"));
|
||||
}
|
||||
// all the others have two words hyphenated as component names
|
||||
_ => {
|
||||
let component = [
|
||||
component,
|
||||
chunks
|
||||
.next()
|
||||
.expect("No such component is managed by rustup"),
|
||||
]
|
||||
.join("-");
|
||||
val.push([toolchain, component.as_str()].join("/"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn group_components_by_toolchains(components: Vec<RustupPackage>) -> Vec<Vec<RustupPackage>> {
|
||||
let mut result = vec![];
|
||||
|
||||
let mut toolchains: Vec<String> = vec![];
|
||||
|
||||
for component in components {
|
||||
let index = toolchains
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, toolchain)| toolchain == &&component.toolchain)
|
||||
.map(|(idx, _)| idx)
|
||||
.unwrap_or_else(|| {
|
||||
toolchains.push(component.toolchain.clone());
|
||||
result.push(vec![]);
|
||||
toolchains.len() - 1
|
||||
});
|
||||
|
||||
result
|
||||
.get_mut(index)
|
||||
.expect(
|
||||
"either the index already existed or we just pushed the element with that index",
|
||||
)
|
||||
.push(component);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
mod helpers;
|
||||
mod types;
|
||||
|
||||
use crate::backend::backend_trait::{Backend, Switches, Text};
|
||||
use crate::backend::macros::impl_backend_constants;
|
||||
use crate::cmd::run_external_command;
|
||||
use crate::{Group, Package};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use std::collections::HashSet;
|
||||
use std::process::Command;
|
||||
|
||||
use self::helpers::{
|
||||
group_components_by_toolchains, install_components, toolchain_of_component_was_already_removed,
|
||||
};
|
||||
pub use self::types::Rustup;
|
||||
use self::types::{Repotype, RustupPackage};
|
||||
|
||||
const BINARY: Text = "rustup";
|
||||
const SECTION: Text = "rustup";
|
||||
|
||||
const SWITCHES_INSTALL: Switches = &["component", "add"];
|
||||
const SWITCHES_INFO: Switches = &["component", "list", "--installed"];
|
||||
const SWITCHES_MAKE_DEPENDENCY: Switches = &[];
|
||||
const SWITCHES_NOCONFIRM: Switches = &[];
|
||||
const SWITCHES_REMOVE: Switches = &["component", "remove"];
|
||||
|
||||
const SUPPORTS_AS_DEPENDENCY: bool = false;
|
||||
|
||||
impl Backend for Rustup {
|
||||
impl_backend_constants!();
|
||||
|
||||
fn get_all_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
let toolchains_vec = self
|
||||
.run_toolchain_command(Repotype::Toolchain.get_info_switches())
|
||||
.context("Getting installed toolchains")?;
|
||||
|
||||
let toolchains: HashSet<Package> = toolchains_vec
|
||||
.iter()
|
||||
.map(|name| ["toolchain", name].join("/").into())
|
||||
.collect();
|
||||
|
||||
let components: HashSet<Package> = self
|
||||
.run_component_command(Repotype::Component.get_info_switches(), &toolchains_vec)
|
||||
.context("Getting installed components")?
|
||||
.iter()
|
||||
.map(|name| ["component", name].join("/").into())
|
||||
.collect();
|
||||
|
||||
let mut packages = HashSet::new();
|
||||
|
||||
packages.extend(toolchains);
|
||||
packages.extend(components);
|
||||
|
||||
Ok(packages)
|
||||
}
|
||||
|
||||
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<()> {
|
||||
panic!("Not supported by {}", self.get_binary())
|
||||
}
|
||||
|
||||
fn install_packages(&self, packages: &[Package], _: bool) -> Result<()> {
|
||||
let packages = RustupPackage::from_pacdef_packages(packages)?;
|
||||
|
||||
let (toolchains, components) =
|
||||
RustupPackage::sort_packages_into_toolchains_and_components(packages);
|
||||
|
||||
self.install_toolchains(toolchains)?;
|
||||
self.install_components(components)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_packages(&self, packages: &[Package], _: bool) -> Result<()> {
|
||||
let rustup_packages = RustupPackage::from_pacdef_packages(packages)?;
|
||||
|
||||
let (toolchains, components) =
|
||||
RustupPackage::sort_packages_into_toolchains_and_components(rustup_packages);
|
||||
|
||||
let removed_toolchains = self.remove_toolchains(toolchains)?;
|
||||
|
||||
self.remove_components(components, removed_toolchains)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Rustup {
|
||||
fn run_component_command(&self, args: &[&str], toolchains: &[String]) -> Result<Vec<String>> {
|
||||
let mut val = Vec::new();
|
||||
|
||||
for toolchain in toolchains {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
cmd.args(args).arg(toolchain);
|
||||
|
||||
let output = String::from_utf8(cmd.output()?.stdout)?;
|
||||
|
||||
for component in output.lines() {
|
||||
install_components(component, toolchain, &mut val);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn run_toolchain_command(&self, args: &[&str]) -> Result<Vec<String>> {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
cmd.args(args);
|
||||
|
||||
let output = String::from_utf8(cmd.output()?.stdout)?;
|
||||
|
||||
let mut val = Vec::new();
|
||||
|
||||
for line in output.lines() {
|
||||
let toolchain = line.split('-').next();
|
||||
match toolchain {
|
||||
Some(name) => val.push(name.to_string()),
|
||||
None => bail!("Toolchain name not provided!"),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn install_toolchains(&self, toolchains: Vec<RustupPackage>) -> Result<()> {
|
||||
if toolchains.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
cmd.args(Repotype::Toolchain.get_install_switches());
|
||||
|
||||
for toolchain in toolchains {
|
||||
cmd.arg(&toolchain.toolchain);
|
||||
}
|
||||
|
||||
run_external_command(cmd).context("installing toolchains")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn install_components(&self, components: Vec<RustupPackage>) -> Result<()> {
|
||||
if components.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let components_by_toolchain = group_components_by_toolchains(components);
|
||||
|
||||
for components_for_one_toolchain in components_by_toolchain {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
cmd.args(Repotype::Component.get_install_switches());
|
||||
|
||||
let the_toolchain = &components_for_one_toolchain
|
||||
.first()
|
||||
.expect("will have at least one element")
|
||||
.toolchain;
|
||||
|
||||
cmd.arg(the_toolchain);
|
||||
|
||||
for component_package in &components_for_one_toolchain {
|
||||
let actual_component = component_package
|
||||
.component
|
||||
.as_ref()
|
||||
.expect("constructor makes sure this is Some");
|
||||
|
||||
cmd.arg(actual_component);
|
||||
}
|
||||
|
||||
run_external_command(cmd)
|
||||
.with_context(|| format!("installing [{components_for_one_toolchain:?}]"))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_toolchains(&self, toolchains: Vec<RustupPackage>) -> Result<Vec<String>> {
|
||||
let mut removed_toolchains = vec![];
|
||||
if !toolchains.is_empty() {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
cmd.args(Repotype::Toolchain.get_remove_switches());
|
||||
|
||||
for toolchain_package in &toolchains {
|
||||
let name = toolchain_package.toolchain.as_str();
|
||||
cmd.arg(name);
|
||||
removed_toolchains.push(name.to_string());
|
||||
}
|
||||
|
||||
run_external_command(cmd)
|
||||
.with_context(|| format!("removing toolchains [{toolchains:?}]"))?;
|
||||
}
|
||||
Ok(removed_toolchains)
|
||||
}
|
||||
|
||||
fn remove_components(
|
||||
&self,
|
||||
components: Vec<RustupPackage>,
|
||||
removed_toolchains: Vec<String>,
|
||||
) -> Result<()> {
|
||||
for component_package in components {
|
||||
let mut cmd = Command::new(self.get_binary());
|
||||
cmd.args(Repotype::Component.get_remove_switches());
|
||||
|
||||
if toolchain_of_component_was_already_removed(&removed_toolchains, &component_package) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cmd.arg(&component_package.toolchain);
|
||||
cmd.arg(
|
||||
component_package
|
||||
.component
|
||||
.as_ref()
|
||||
.expect("the constructor ensures this cannot be None"),
|
||||
);
|
||||
|
||||
run_external_command(cmd)
|
||||
.with_context(|| format!("removing component {component_package:?}"))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
use anyhow::{bail, Context, Result};
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::{backend::backend_trait::Switches, Package};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Rustup {
|
||||
pub(crate) packages: HashSet<Package>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Repotype {
|
||||
Toolchain,
|
||||
Component,
|
||||
}
|
||||
|
||||
/// A package as used exclusively in the rustup backend. Contrary to other packages, this does not
|
||||
/// have an (optional) repository and a name, but is either a component or a toolchain, has a
|
||||
/// toolchain version, and if it is a toolchain also a name.
|
||||
#[derive(Debug)]
|
||||
pub struct RustupPackage {
|
||||
/// Whether it is a toolchain or a component.
|
||||
pub repotype: Repotype,
|
||||
/// The name of the toolchain this belongs to (stable, nightly, a pinned version)
|
||||
pub toolchain: String,
|
||||
/// If it is a toolchain, it will not have a component name.
|
||||
/// If it is a component, this will be its name.
|
||||
pub component: Option<String>,
|
||||
}
|
||||
|
||||
impl Rustup {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
packages: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Repotype {
|
||||
fn try_from<T>(value: T) -> Result<Self>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
let value = value.as_ref();
|
||||
let result = match value {
|
||||
"toolchain" => Self::Toolchain,
|
||||
"component" => Self::Component,
|
||||
_ => bail!("{} is neither toolchain nor component", value),
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn get_install_switches(self) -> Switches {
|
||||
match self {
|
||||
Self::Toolchain => &["toolchain", "install"],
|
||||
Self::Component => &["component", "add", "--toolchain"],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_remove_switches(self) -> Switches {
|
||||
match self {
|
||||
Self::Toolchain => &["toolchain", "uninstall"],
|
||||
Self::Component => &["component", "remove", "--toolchain"],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_info_switches(self) -> Switches {
|
||||
match self {
|
||||
Self::Toolchain => &["toolchain", "list"],
|
||||
Self::Component => &["component", "list", "--installed", "--toolchain"],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RustupPackage {
|
||||
/// Creates a new [`RustupPackage`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if
|
||||
/// - repotype is Toolchain and component is Some, or
|
||||
/// - repotype is Component and component is None.
|
||||
fn new(repotype: Repotype, toolchain: String, component: Option<String>) -> Self {
|
||||
match repotype {
|
||||
Repotype::Toolchain => assert!(component.is_none()),
|
||||
Repotype::Component => assert!(component.is_some()),
|
||||
};
|
||||
|
||||
Self {
|
||||
repotype,
|
||||
toolchain,
|
||||
component,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sort_packages_into_toolchains_and_components(
|
||||
packages: Vec<Self>,
|
||||
) -> (Vec<Self>, Vec<Self>) {
|
||||
let mut toolchains = vec![];
|
||||
let mut components = vec![];
|
||||
|
||||
for package in packages {
|
||||
match package.repotype {
|
||||
Repotype::Toolchain => toolchains.push(package),
|
||||
Repotype::Component => components.push(package),
|
||||
}
|
||||
}
|
||||
|
||||
(toolchains, components)
|
||||
}
|
||||
|
||||
pub fn from_pacdef_packages(packages: &[Package]) -> Result<Vec<Self>> {
|
||||
let mut result = vec![];
|
||||
|
||||
for package in packages {
|
||||
let rustup_package = Self::try_from(package).with_context(|| {
|
||||
format!(
|
||||
"converting pacdef package {} to rustup package",
|
||||
package.name
|
||||
)
|
||||
})?;
|
||||
result.push(rustup_package);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Package> for RustupPackage {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(package: &Package) -> Result<Self> {
|
||||
let repo = package.repo.as_ref().context("getting repo from package")?;
|
||||
let repotype = Repotype::try_from(repo).context("getting repotype")?;
|
||||
|
||||
let (toolchain, component) = match repotype {
|
||||
Repotype::Toolchain => (package.name.to_string(), None),
|
||||
Repotype::Component => {
|
||||
let (toolchain, component) = package
|
||||
.name
|
||||
.split_once('/')
|
||||
.context("splitting package into toolchain and component")?;
|
||||
(toolchain.to_string(), Some(component.into()))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self::new(repotype, toolchain, component))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
use std::collections::HashSet;
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::Result;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::backend::backend_trait::{Backend, Switches, Text};
|
||||
use crate::backend::macros::impl_backend_constants;
|
||||
use crate::backend::root::build_base_command_with_privileges;
|
||||
use crate::cmd::run_external_command;
|
||||
use crate::{Group, Package};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Void {
|
||||
pub(crate) packages: HashSet<Package>,
|
||||
}
|
||||
|
||||
const BINARY: Text = "xbps-install";
|
||||
const INSTALL_BINARY: Text = "xbps-install";
|
||||
const REMOVE_BINARY: Text = "xbps-remove";
|
||||
const QUERY_BINARY: Text = "xbps-query";
|
||||
const PKGDB_BINARY: Text = "xbps-pkgdb";
|
||||
const SECTION: Text = "void";
|
||||
|
||||
const SWITCHES_INFO: Switches = &[];
|
||||
const SWITCHES_INSTALL: Switches = &["-S"];
|
||||
const SWITCHES_MAKE_DEPENDENCY: Switches = &["-m", "auto"];
|
||||
const SWITCHES_NOCONFIRM: Switches = &["-y"];
|
||||
const SWITCHES_REMOVE: Switches = &["-R"];
|
||||
|
||||
const SUPPORTS_AS_DEPENDENCY: bool = true;
|
||||
|
||||
impl Backend for Void {
|
||||
impl_backend_constants!();
|
||||
|
||||
fn get_all_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
// Removes the package status and description from output
|
||||
let re_str_1 = r"^ii |^uu |^hr |^\?\? | .*";
|
||||
// Removes the package version from output
|
||||
let re_str_2 = r"-[^-]*$";
|
||||
let re1 = Regex::new(re_str_1)?;
|
||||
let re2 = Regex::new(re_str_2)?;
|
||||
let mut cmd = Command::new(QUERY_BINARY);
|
||||
cmd.args(["-l"]);
|
||||
let output = String::from_utf8(cmd.output()?.stdout)?;
|
||||
|
||||
let packages = output
|
||||
.lines()
|
||||
.map(|line| {
|
||||
let result = re1.replace_all(line, "");
|
||||
let result = re2.replace_all(&result, "");
|
||||
result.to_string().into()
|
||||
})
|
||||
.collect();
|
||||
Ok(packages)
|
||||
}
|
||||
|
||||
fn get_explicitly_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||
// Removes the package version from output
|
||||
let re_str = r"-[^-]*$";
|
||||
let re = Regex::new(re_str)?;
|
||||
let mut cmd = Command::new(QUERY_BINARY);
|
||||
cmd.args(["-m"]);
|
||||
let output = String::from_utf8(cmd.output()?.stdout)?;
|
||||
|
||||
let packages = output
|
||||
.lines()
|
||||
.map(|line| {
|
||||
let result = re.replace_all(line, "").to_string();
|
||||
result.into()
|
||||
})
|
||||
.collect();
|
||||
Ok(packages)
|
||||
}
|
||||
|
||||
/// Install the specified packages.
|
||||
fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> {
|
||||
let mut cmd = build_base_command_with_privileges(INSTALL_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)
|
||||
}
|
||||
|
||||
fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result<()> {
|
||||
let mut cmd = build_base_command_with_privileges(REMOVE_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)
|
||||
}
|
||||
|
||||
fn make_dependency(&self, packages: &[Package]) -> Result<()> {
|
||||
let mut cmd = build_base_command_with_privileges(PKGDB_BINARY);
|
||||
cmd.args(self.get_switches_make_dependency());
|
||||
|
||||
for p in packages {
|
||||
cmd.arg(format!("{p}"));
|
||||
}
|
||||
|
||||
run_external_command(cmd)
|
||||
}
|
||||
|
||||
/// Show information from package manager for package.
|
||||
fn show_package_info(&self, package: &Package) -> Result<()> {
|
||||
let mut cmd = Command::new(QUERY_BINARY);
|
||||
cmd.args(self.get_switches_info());
|
||||
cmd.arg(format!("{package}"));
|
||||
|
||||
run_external_command(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl Void {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
packages: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user