diff --git a/src/main.rs b/src/main.rs index 2732bd9..9ff466b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,12 +44,29 @@ fn main() -> Result<(), PlaybookError> { .build(); playbook.run(cmd)?; + let pkg = Package::new() + .name("hello") + .state(PackageState::Present) + .build(); + playbook.run(pkg)?; + let pkg = Package::new() .name("hello") .state(PackageState::Absent) .build(); playbook.run(pkg)?; + let pkg = Package::new() + .name(vec!("hello", "sl")) + .state(PackageState::Present) + .build(); + playbook.run(pkg)?; + + let pkg = Package::new() + .name(&[ "hello", "sl" ]) + .state(PackageState::Absent) + .build(); + playbook.run(pkg)?; playbook.print_json_pretty(); Ok(()) diff --git a/src/modules/mod.rs b/src/modules/mod.rs index 9d68e5c..68834ce 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -79,6 +79,7 @@ impl PlaybookRun { let module_name = cmd.module_name().to_string(); let args = cmd.serialize_args(); + println!("Running module {} with args:{:#?}\n\n", &module_name, &args); let res = cmd.run(); diff --git a/src/modules/package/apt.rs b/src/modules/package/apt.rs index 4a476c9..cecf295 100644 --- a/src/modules/package/apt.rs +++ b/src/modules/package/apt.rs @@ -2,6 +2,10 @@ use crate::utils::cmd::Cmd; use super::{PackageError, PackageList, SpecificPackageManager}; +pub fn apt() -> Cmd { + Cmd::new("apt").env("DEBIAN_FRONTEND", "noninteractive") +} + #[derive(Debug)] pub struct DebianPackageManager; @@ -11,7 +15,7 @@ impl SpecificPackageManager for DebianPackageManager { } fn update(&self) -> Result<(), PackageError> { - let res = Cmd::new("apt").arg("update").run()?; + let res = apt().arg("update").run()?; if ! res.success { return Err(PackageError::CmdFail(res)); @@ -20,7 +24,7 @@ impl SpecificPackageManager for DebianPackageManager { } fn install(&self, list: PackageList) -> Result<(), PackageError> { - let res = Cmd::new("apt").arg("install").args(list.list()).run()?; + let res = apt().arg("install").args(list.list()).run()?; if ! res.success { return Err(PackageError::CmdFail(res)); @@ -29,7 +33,7 @@ impl SpecificPackageManager for DebianPackageManager { } fn remove(&self, list: PackageList) -> Result<(), PackageError> { - let res = Cmd::new("apt").arg("remove").args(list.list()).run()?; + let res = apt().arg("remove").args(list.list()).run()?; if ! res.success { return Err(PackageError::CmdFail(res)); diff --git a/src/modules/package/list.rs b/src/modules/package/list.rs index beeef0c..d2e2176 100644 --- a/src/modules/package/list.rs +++ b/src/modules/package/list.rs @@ -1,110 +1,80 @@ use serde::Serialize; -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; -use builder::{NoPackage, PackageArgsBuilder}; - #[derive(Clone, Debug, Serialize)] -pub struct PackageArgs { - name: PackageList, - state: PackageState, +pub struct PackageList { + pub list: Vec, } -#[derive(Clone, Debug, Serialize)] -pub enum PackageState { - Present, - Absent, - Latest, -} - -#[derive(Debug)] -pub struct PackageModule { - manager: Manager, - args: PackageArgs, -} - -impl Module for PackageModule { - fn clone_args(&self) -> PackageArgs { - self.args.clone() +impl PackageList { + pub fn list(&self) -> & [ String ] { + &self.list } - - fn module_name() -> &'static str { - "package" - } -} -impl Module for PackageModule> { - fn run(&self) -> Result<(), PackageError> { - Ok(()) - } -} + pub fn add(self, add: T) -> PackageList { + let Self { mut list } = self; + let extend_with = add.into_package_list(); + list.extend(extend_with.list); -impl Module for PackageModule { - fn with_facts(self, facts: &Facts) -> PackageModule> { - let Self { args, .. } = self; - - let manager = match facts.os.family() { - OsFamily::Debian => Box::new(DebianPackageManager), - OsFamily::Archlinux => Box::new(ArchlinuxPackageManager), - }; - - PackageModule { - args, - manager, + 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 PackageModule { - pub fn new() -> PackageArgsBuilder { - PackageArgsBuilder::new() - } - - pub fn from_args_with_facts(args: PackageArgs, facts: &Facts) -> PackageModule { - let manager = match facts.os.family() { - OsFamily::Debian => PackageModule(Box::new(DebianPackageManager)), - OsFamily::Archlinux => PackageModule(Box::new(ArchlinuxPackageManager)), - }; - PackageModule { - manager, - args, - } - } - - pub fn from_args_with(args: PackageArgs, manager: SpecificPackageManager) -> PackageManager { - PackageModule { - manager: Box::new(manager), - args, +impl IntoPackageList for &str { + fn into_package_list(self) -> PackageList { + PackageList { + list: vec!(self.to_string()), } } } -#[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()) +impl IntoPackageList for String { + fn into_package_list(self) -> PackageList { + PackageList { + list: vec!(self), + } } } -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>; +impl IntoPackageList for & [ &str; C ] { + fn into_package_list(self) -> PackageList { + PackageList { + list: self.iter().map(|x| x.to_string()).collect(), + } + } +} + +impl IntoPackageList for & [ String; C ] { + fn into_package_list(self) -> PackageList { + PackageList { + list: self.to_vec(), + } + } +} + +impl IntoPackageList for Vec<&str> { + fn into_package_list(self) -> PackageList { + PackageList { + list: self.iter().map(|x| x.to_string()).collect(), + } + } +} + +impl IntoPackageList for Vec { + fn into_package_list(self) -> PackageList { + PackageList { + list: self, + } + } } diff --git a/src/modules/package/mod.rs b/src/modules/package/mod.rs index 5b769d5..b121454 100644 --- a/src/modules/package/mod.rs +++ b/src/modules/package/mod.rs @@ -6,6 +6,9 @@ use crate::utils::cmd::CmdOutput; use std::boxed::Box; +pub mod list; +pub use list::{PackageList, IntoPackageList}; + pub mod pacman; use pacman::ArchlinuxPackageManager; pub mod apt; @@ -99,59 +102,4 @@ pub trait SpecificPackageManager: std::fmt::Debug { 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(), - } - } -} +} \ No newline at end of file diff --git a/src/modules/package/pacman.rs b/src/modules/package/pacman.rs index 85651d2..cce89a0 100644 --- a/src/modules/package/pacman.rs +++ b/src/modules/package/pacman.rs @@ -30,7 +30,7 @@ impl SpecificPackageManager for ArchlinuxPackageManager { Ok(()) } - fn remove(&self, _list: PackageList) -> Result<(), PackageError> { + fn remove(&self, list: PackageList) -> Result<(), PackageError> { let res = Cmd::new("pacman").arg("-R").args(list.list()).run()?; if ! res.success { diff --git a/src/utils/cmd.rs b/src/utils/cmd.rs index 08da934..a38dc89 100644 --- a/src/utils/cmd.rs +++ b/src/utils/cmd.rs @@ -1,5 +1,6 @@ use duct::cmd as duct_cmd; +use std::collections::HashMap; use std::path::PathBuf; #[derive(Clone, Debug, Serialize)] @@ -8,6 +9,7 @@ pub struct Cmd { args: Vec, stdin: Option, chdir: Option, + env: HashMap, } impl Cmd { @@ -17,11 +19,12 @@ impl Cmd { args: Vec::new(), stdin: None, chdir: None, + env: HashMap::new(), } } pub fn arg>(self, arg: T) -> Cmd { - let Self { program, mut args, stdin, chdir, .. } = self; + let Self { program, mut args, stdin, chdir, env, .. } = self; args.push(arg.as_ref().to_string()); @@ -30,12 +33,13 @@ impl Cmd { args, stdin, chdir, + env, } } pub fn args>(self, new_args: & [ T ]) -> Cmd { - let Self { program, mut args, stdin, chdir, .. } = self; + let Self { program, mut args, stdin, chdir, env, .. } = self; for arg in new_args { args.push(arg.as_ref().to_string()); @@ -46,10 +50,12 @@ impl Cmd { args, stdin, chdir, - } } + env, + } + } pub fn stdin>(self, input: T) -> Cmd { - let Self { program, args, mut stdin, chdir, .. } = self; + let Self { program, args, mut stdin, chdir, env, .. } = self; stdin = if let Some(mut s) = stdin { s.push_str(input.as_ref()); @@ -63,25 +69,47 @@ impl Cmd { args, stdin, chdir, + env, } } pub fn chdir>(self, dir: T) -> Cmd { - let Self { program, args, stdin, .. } = self; + let Self { program, args, stdin, env, .. } = self; Cmd { program, args, stdin, chdir: Some(PathBuf::from(dir.as_ref())), + env, + } + } + + pub fn env, V: AsRef>(self, key: K, value: V) -> Cmd { + let Self { program, args, stdin, chdir, mut env, .. } = self; + + env.insert(key.as_ref().to_string(), value.as_ref().to_string()); + + Cmd { + program, + args, + stdin, + chdir, + env, } } pub fn run(self) -> Result { - let Self { program, args, stdin, chdir, .. } = self; + let Self { program, args, stdin, chdir, env, .. } = self; let mut cmd = duct_cmd(&program, &args); + if ! env.is_empty() { + for (key, value) in env { + cmd = cmd.env(key, value); + } + } + if let Some(stdin) = &stdin { cmd = cmd.stdin_bytes(stdin.as_bytes()); }