213 lines
No EOL
5.6 KiB
Rust
213 lines
No EOL
5.6 KiB
Rust
use serde::{Serialize, Deserialize, Deserializer};
|
|
|
|
use crate::{Module, ModuleSetup};
|
|
use crate::facts::{Facts, os::OsFamily};
|
|
use crate::utils::cmd::CmdOutput;
|
|
|
|
use std::boxed::Box;
|
|
|
|
pub mod list;
|
|
pub use list::{PackageList, IntoPackageList};
|
|
|
|
pub mod pacman;
|
|
use pacman::PacmanManager;
|
|
pub mod apt;
|
|
use apt::AptManager;
|
|
|
|
pub mod builder;
|
|
pub use builder::{PackageArgsBuilder, NoManager};
|
|
|
|
pub type PackageStatus = ();
|
|
|
|
#[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,
|
|
Absent,
|
|
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,
|
|
args: PackageArgs,
|
|
}
|
|
|
|
impl Module<PackageArgs, (), PackageError> for PackageModule<Box<dyn PackageManager>> {
|
|
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<PackageModule<Box<dyn PackageManager>>, PackageArgs, (), PackageError> for PackageModule<NoManager> {
|
|
fn with_facts(self, facts: &Facts) -> PackageModule<Box<dyn PackageManager>> {
|
|
let Self { args, .. } = self;
|
|
|
|
let manager: Box<dyn PackageManager> = match facts.os.family() {
|
|
OsFamily::Debian => Box::new(AptManager),
|
|
OsFamily::Archlinux => Box::new(PacmanManager),
|
|
};
|
|
|
|
PackageModule {
|
|
args,
|
|
manager,
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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> {
|
|
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 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>;
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn ensure_present() {
|
|
let pkg = PackageModule::new()
|
|
.name("hello")
|
|
.state(PackageState::Present)
|
|
.with_apt()
|
|
.build();
|
|
|
|
assert_eq!(
|
|
pkg.serialize_args(),
|
|
serde_json::json!({
|
|
"name": vec!("hello"),
|
|
"state": PackageState::Present,
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ensure_present_multi() {
|
|
let pkg = PackageModule::new()
|
|
.name(&[ "hello", "sl" ])
|
|
.state(PackageState::Present)
|
|
.with_apt()
|
|
.build();
|
|
|
|
assert_eq!(
|
|
pkg.serialize_args(),
|
|
serde_json::json!({
|
|
"name": vec!("hello", "sl"),
|
|
"state": PackageState::Present,
|
|
})
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn ensure_absent() {
|
|
let pkg = PackageModule::new()
|
|
.name("hello")
|
|
.state(PackageState::Absent)
|
|
.with_apt()
|
|
.build();
|
|
|
|
assert_eq!(
|
|
pkg.serialize_args(),
|
|
serde_json::json!({
|
|
"name": vec!("hello"),
|
|
"state": PackageState::Absent,
|
|
})
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn ensure_absent_multi() {
|
|
let pkg = PackageModule::new()
|
|
.name(&[ "hello", "sl" ])
|
|
.state(PackageState::Absent)
|
|
.with_apt()
|
|
.build();
|
|
|
|
assert_eq!(
|
|
pkg.serialize_args(),
|
|
serde_json::json!({
|
|
"name": vec!("hello", "sl"),
|
|
"state": PackageState::Absent,
|
|
})
|
|
)
|
|
}
|
|
} |