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}; 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, args: PackageArgs, } impl Module for PackageModule> { 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!() } } } } impl ModuleSetup>, PackageArgs, (), PackageError> for PackageModule { fn with_facts(self, facts: &Facts) -> PackageModule> { let Self { args, .. } = self; let manager: Box = match facts.os.family() { OsFamily::Debian => Box::new(DebianPackageManager), OsFamily::Archlinux => Box::new(ArchlinuxPackageManager), }; PackageModule { args, manager, } } } impl PackageModule { pub fn new() -> PackageArgsBuilder { PackageArgsBuilder::new() } } #[derive(Clone, Debug, Serialize)] pub enum PackageError { IoError(String), CmdFail(CmdOutput), } impl From 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, } impl PackageList { pub fn list(&self) -> & [ String ] { &self.list } pub fn add(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 pub trait IntoPackageList { fn into_package_list(self) -> PackageList; } impl IntoPackageList for &str { fn into_package_list(self) -> PackageList { PackageList { list: vec!(self.to_string()), } } } 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(), } } }