158 lines
4.0 KiB
Rust
Raw Normal View History

2023-04-19 21:49:30 +02:00
use serde::Serialize;
use crate::{Module, ModuleSetup};
use crate::facts::{Facts, os::OsFamily};
use crate::utils::cmd::CmdOutput;
use std::boxed::Box;
pub mod pacman;
use pacman::ArchlinuxPackageManager;
pub mod apt;
use apt::DebianPackageManager;
pub mod builder;
pub use builder::{PackageArgsBuilder, NoManager};
2023-04-19 21:49:30 +02:00
pub type PackageStatus = ();
#[derive(Clone, Debug, Serialize)]
pub struct PackageArgs {
name: PackageList,
state: PackageState,
}
#[derive(Clone, Debug, Serialize)]
pub enum PackageState {
Present,
Absent,
Latest,
}
#[derive(Debug)]
pub struct PackageModule<Manager> {
manager: Manager,
args: PackageArgs,
}
impl Module<PackageArgs, (), PackageError> for PackageModule<Box<dyn SpecificPackageManager>> {
fn serialize_args(&self) -> serde_json::Value {
serde_json::to_value(&self.args).unwrap()
}
fn module_name(&self) -> &'static str {
"package"
}
fn run(self) -> Result<(), PackageError> {
let packages = self.args.name;
match &self.args.state {
PackageState::Present => {
self.manager.install(packages)
}, PackageState::Absent => {
self.manager.remove(packages)
}, PackageState::Latest => {
unimplemented!()
}
}
2023-04-19 21:49:30 +02:00
}
}
impl ModuleSetup<PackageModule<Box<dyn SpecificPackageManager>>, PackageArgs, (), PackageError> for PackageModule<NoManager> {
fn with_facts(self, facts: &Facts) -> PackageModule<Box<dyn SpecificPackageManager>> {
2023-04-19 21:49:30 +02:00
let Self { args, .. } = self;
let manager: Box<dyn SpecificPackageManager> = match facts.os.family() {
OsFamily::Debian => Box::new(DebianPackageManager),
OsFamily::Archlinux => Box::new(ArchlinuxPackageManager),
};
PackageModule {
2023-04-19 21:49:30 +02:00
args,
manager,
}
2023-04-19 21:49:30 +02:00
}
}
impl PackageModule<NoManager> {
pub fn new() -> PackageArgsBuilder<builder::NoPackage, builder::NoState, builder::NoManager> {
PackageArgsBuilder::new()
}
}
#[derive(Clone, Debug, Serialize)]
pub enum PackageError {
IoError(String),
CmdFail(CmdOutput),
}
impl From<std::io::Error> for PackageError {
fn from(e: std::io::Error) -> PackageError {
PackageError::IoError(e.to_string())
}
}
pub trait SpecificPackageManager: std::fmt::Debug {
fn name(&self) -> &'static str;
fn update(&self) -> Result<(), PackageError>;
fn install(&self, list: PackageList) -> Result<(), PackageError>;
fn remove(&self, list: PackageList) -> Result<(), PackageError>;
}
#[derive(Clone, Debug, Serialize)]
pub struct PackageList {
pub list: Vec<String>,
}
impl PackageList {
pub fn list(&self) -> & [ String ] {
&self.list
}
pub fn add<T: IntoPackageList>(self, add: T) -> PackageList {
let Self { mut list } = self;
let extend_with = add.into_package_list();
list.extend(extend_with.list);
PackageList {
list
}
}
}
/// Turn a stringy value or list of values into an actual package list.
/// Only implemented for &str and &str/String slices because apparently
/// there may be a future implementation that turns a slice of stringy values
/// into a stringy value, resulting in conflicting implementations.
/// https://stackoverflow.com/questions/63136970/how-do-i-work-around-the-upstream-crates-may-add-a-new-impl-of-trait-error
2023-04-19 21:49:30 +02:00
pub trait IntoPackageList {
fn into_package_list(self) -> PackageList;
}
impl IntoPackageList for &str {
2023-04-19 21:49:30 +02:00
fn into_package_list(self) -> PackageList {
PackageList {
list: vec!(self.to_string()),
2023-04-19 21:49:30 +02:00
}
}
}
impl IntoPackageList for & [ &str ] {
fn into_package_list(self) -> PackageList {
PackageList {
list: self.iter().map(|x| x.to_string()).collect(),
}
}
}
impl IntoPackageList for & [ String ] {
fn into_package_list(self) -> PackageList {
PackageList {
list: self.to_vec(),
}
}
}