This commit is contained in:
selfhoster selfhoster 2023-10-05 17:58:41 +02:00
commit 89ef66a694
19 changed files with 461 additions and 64 deletions

View file

@ -1,6 +1,8 @@
use rayon::prelude::*;
use crate::utils::cmd::{Cmd, CmdOutput};
use super::{PackageError, PackageList, PackageManager};
use super::{PackageError, PackageList, PackageManager, IntoPackageList};
/// This structure is mostly used as an indirection for testing purposes.
/// But you can use it to build your own apt commands.
@ -40,6 +42,14 @@ impl AptCmd {
#[derive(Debug)]
pub struct AptManager;
impl AptManager {
fn is_installed_list(&self, list: &PackageList) -> Result<bool, PackageError> {
let res = Cmd::new("dpkg").arg("-s").args(list.list()).run()?;
Ok(res.success)
}
}
impl PackageManager for AptManager {
fn name(&self) -> &'static str {
"apt"
@ -54,7 +64,26 @@ impl PackageManager for AptManager {
Ok(())
}
fn is_installed(&self, pkg: &str) -> Result<bool, PackageError> {
let res = Cmd::new("dpkg").arg("-s").arg(pkg).run()?;
Ok(res.success)
}
fn install(&self, list: PackageList) -> Result<(), PackageError> {
// Filter out already installed packages
// let list: Vec<String> = list.list().par_iter().filter_map(|pkg| {
// if ! self.is_installed(&pkg).unwrap() {
// Some(pkg.to_string())
// } else {
// None
// }
// }).collect();
if self.is_installed_list(&list)? {
return Ok(());
}
//let res = AptCmd::install(list.into_package_list()).run()?;
let res = AptCmd::install(list).run()?;
if ! res.success {

View file

@ -1,8 +1,11 @@
use serde::Serialize;
use serde::{Serialize, Deserialize};
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
use crate::utils::serde::string_or_seq_string;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct PackageList {
#[serde(deserialize_with = "string_or_seq_string")]
pub list: Vec<String>,
}

View file

@ -1,4 +1,4 @@
use serde::Serialize;
use serde::{Serialize, Deserialize, Deserializer};
use crate::{Module, ModuleSetup};
use crate::facts::{Facts, os::OsFamily};
@ -19,12 +19,25 @@ pub use builder::{PackageArgsBuilder, NoManager};
pub type PackageStatus = ();
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PackageArgs {
name: PackageList,
state: PackageState,
}
impl ModuleSetup<PackageModule<Box<dyn PackageManager>>, PackageArgs, (), PackageError> for PackageArgs {
fn with_facts(self, facts: &Facts) -> PackageModule<Box<dyn PackageManager>> {
let manager: Box<dyn PackageManager> = match facts.os.family() {
OsFamily::Debian => Box::new(AptManager),
OsFamily::Archlinux => Box::new(PacmanManager),
};
PackageModule {
args: self,
manager,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub enum PackageState {
Present,
@ -32,6 +45,21 @@ pub enum PackageState {
Latest,
}
impl<'de> Deserialize<'de> for PackageState {
fn deserialize<D>(de: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let variant = String::deserialize(de)?;
Ok(match variant.as_str() {
"present" => PackageState::Present,
"absent" => PackageState::Absent,
"latest" => PackageState::Latest,
_other => unimplemented!(),
})
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct PackageModule<Manager> {
manager: Manager,
@ -78,6 +106,12 @@ impl ModuleSetup<PackageModule<Box<dyn PackageManager>>, PackageArgs, (), Packag
}
}
// Stupid impl just for tests
impl ModuleSetup<PackageModule<Box<dyn PackageManager>>, PackageArgs, (), PackageError> for PackageModule<Box<dyn PackageManager>> {
fn with_facts(self, _facts: &Facts) -> PackageModule<Box<dyn PackageManager>> {
self
}
}
impl PackageModule<NoManager> {
pub fn new() -> PackageArgsBuilder<builder::NoPackage, builder::NoState, builder::NoManager> {
@ -100,6 +134,7 @@ impl From<std::io::Error> for PackageError {
pub trait PackageManager: std::fmt::Debug {
fn name(&self) -> &'static str;
fn update(&self) -> Result<(), PackageError>;
fn is_installed(&self, pkg: &str) -> Result<bool, PackageError>;
fn install(&self, list: PackageList) -> Result<(), PackageError>;
fn remove(&self, list: PackageList) -> Result<(), PackageError>;
}

View file

@ -20,6 +20,12 @@ impl PacmanCmd {
cmd
}
pub fn is_installed(pkg: &str) -> PacmanCmd {
let mut cmd = Self::new();
cmd.0 = cmd.0.arg("-Qi").arg(pkg);
cmd
}
pub fn install(list: PackageList) -> PacmanCmd {
let mut cmd = Self::new();
cmd.0 = cmd.0.arg("-S").args(list.list());
@ -56,6 +62,12 @@ impl PackageManager for PacmanManager {
Ok(())
}
fn is_installed(&self, pkg: &str) -> Result<bool, PackageError> {
let res = PacmanCmd::is_installed(pkg).run()?;
Ok(res.success)
}
fn install(&self, list: PackageList) -> Result<(), PackageError> {
let res = PacmanCmd::install(list).run()?;