Add new Package examples to main
Print debug line with module arguments when run starts Make apt package manager non-interactive Move PackageList to dedicated module and make more impls utils::Cmd now can take environment variables
This commit is contained in:
parent
f26f1c1bf2
commit
1245db813b
17
src/main.rs
17
src/main.rs
|
@ -44,12 +44,29 @@ fn main() -> Result<(), PlaybookError> {
|
||||||
.build();
|
.build();
|
||||||
playbook.run(cmd)?;
|
playbook.run(cmd)?;
|
||||||
|
|
||||||
|
let pkg = Package::new()
|
||||||
|
.name("hello")
|
||||||
|
.state(PackageState::Present)
|
||||||
|
.build();
|
||||||
|
playbook.run(pkg)?;
|
||||||
|
|
||||||
let pkg = Package::new()
|
let pkg = Package::new()
|
||||||
.name("hello")
|
.name("hello")
|
||||||
.state(PackageState::Absent)
|
.state(PackageState::Absent)
|
||||||
.build();
|
.build();
|
||||||
playbook.run(pkg)?;
|
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();
|
playbook.print_json_pretty();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -79,6 +79,7 @@ impl PlaybookRun {
|
||||||
|
|
||||||
let module_name = cmd.module_name().to_string();
|
let module_name = cmd.module_name().to_string();
|
||||||
let args = cmd.serialize_args();
|
let args = cmd.serialize_args();
|
||||||
|
println!("Running module {} with args:{:#?}\n\n", &module_name, &args);
|
||||||
|
|
||||||
let res = cmd.run();
|
let res = cmd.run();
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,10 @@ use crate::utils::cmd::Cmd;
|
||||||
|
|
||||||
use super::{PackageError, PackageList, SpecificPackageManager};
|
use super::{PackageError, PackageList, SpecificPackageManager};
|
||||||
|
|
||||||
|
pub fn apt() -> Cmd {
|
||||||
|
Cmd::new("apt").env("DEBIAN_FRONTEND", "noninteractive")
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DebianPackageManager;
|
pub struct DebianPackageManager;
|
||||||
|
|
||||||
|
@ -11,7 +15,7 @@ impl SpecificPackageManager for DebianPackageManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self) -> Result<(), PackageError> {
|
fn update(&self) -> Result<(), PackageError> {
|
||||||
let res = Cmd::new("apt").arg("update").run()?;
|
let res = apt().arg("update").run()?;
|
||||||
|
|
||||||
if ! res.success {
|
if ! res.success {
|
||||||
return Err(PackageError::CmdFail(res));
|
return Err(PackageError::CmdFail(res));
|
||||||
|
@ -20,7 +24,7 @@ impl SpecificPackageManager for DebianPackageManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install(&self, list: PackageList) -> Result<(), PackageError> {
|
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 {
|
if ! res.success {
|
||||||
return Err(PackageError::CmdFail(res));
|
return Err(PackageError::CmdFail(res));
|
||||||
|
@ -29,7 +33,7 @@ impl SpecificPackageManager for DebianPackageManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&self, list: PackageList) -> Result<(), PackageError> {
|
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 {
|
if ! res.success {
|
||||||
return Err(PackageError::CmdFail(res));
|
return Err(PackageError::CmdFail(res));
|
||||||
|
|
|
@ -1,110 +1,80 @@
|
||||||
use serde::Serialize;
|
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)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
pub struct PackageArgs {
|
pub struct PackageList {
|
||||||
name: PackageList,
|
pub list: Vec<String>,
|
||||||
state: PackageState,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
impl PackageList {
|
||||||
pub enum PackageState {
|
pub fn list(&self) -> & [ String ] {
|
||||||
Present,
|
&self.list
|
||||||
Absent,
|
|
||||||
Latest,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PackageModule<Manager> {
|
|
||||||
manager: Manager,
|
|
||||||
args: PackageArgs,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Manager> Module<PackageArgs, (), PackageError> for PackageModule<Manager> {
|
|
||||||
fn clone_args(&self) -> PackageArgs {
|
|
||||||
self.args.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn module_name() -> &'static str {
|
|
||||||
"package"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Module<PackageArgs, (), PackageError> for PackageModule<Box<dyn SpecificPackageManager>> {
|
pub fn add<T: IntoPackageList>(self, add: T) -> PackageList {
|
||||||
fn run(&self) -> Result<(), PackageError> {
|
let Self { mut list } = self;
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let extend_with = add.into_package_list();
|
||||||
|
list.extend(extend_with.list);
|
||||||
|
|
||||||
impl Module<PackageArgs, (), PackageError> for PackageModule<NoManager> {
|
PackageList {
|
||||||
fn with_facts(self, facts: &Facts) -> PackageModule<Box<dyn SpecificPackageManager>> {
|
list
|
||||||
let Self { args, .. } = self;
|
|
||||||
|
|
||||||
let manager = match facts.os.family() {
|
|
||||||
OsFamily::Debian => Box::new(DebianPackageManager),
|
|
||||||
OsFamily::Archlinux => Box::new(ArchlinuxPackageManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
PackageModule {
|
|
||||||
args,
|
|
||||||
manager,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
impl IntoPackageList for &str {
|
||||||
pub fn new() -> PackageArgsBuilder {
|
fn into_package_list(self) -> PackageList {
|
||||||
PackageArgsBuilder::new()
|
PackageList {
|
||||||
}
|
list: vec!(self.to_string()),
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
impl IntoPackageList for String {
|
||||||
pub enum PackageError {
|
fn into_package_list(self) -> PackageList {
|
||||||
IoError(String),
|
PackageList {
|
||||||
CmdFail(CmdOutput),
|
list: vec!(self),
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
impl<const C: usize> IntoPackageList for & [ &str; C ] {
|
||||||
fn name(&self) -> &'static str;
|
fn into_package_list(self) -> PackageList {
|
||||||
fn update(&self) -> Result<(), PackageError>;
|
PackageList {
|
||||||
fn install(&self, list: PackageList) -> Result<(), PackageError>;
|
list: self.iter().map(|x| x.to_string()).collect(),
|
||||||
fn remove(&self, list: PackageList) -> Result<(), PackageError>;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const C: usize> 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<String> {
|
||||||
|
fn into_package_list(self) -> PackageList {
|
||||||
|
PackageList {
|
||||||
|
list: self,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@ use crate::utils::cmd::CmdOutput;
|
||||||
|
|
||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
|
|
||||||
|
pub mod list;
|
||||||
|
pub use list::{PackageList, IntoPackageList};
|
||||||
|
|
||||||
pub mod pacman;
|
pub mod pacman;
|
||||||
use pacman::ArchlinuxPackageManager;
|
use pacman::ArchlinuxPackageManager;
|
||||||
pub mod apt;
|
pub mod apt;
|
||||||
|
@ -99,59 +102,4 @@ pub trait SpecificPackageManager: std::fmt::Debug {
|
||||||
fn update(&self) -> Result<(), PackageError>;
|
fn update(&self) -> Result<(), PackageError>;
|
||||||
fn install(&self, list: PackageList) -> Result<(), PackageError>;
|
fn install(&self, list: PackageList) -> Result<(), PackageError>;
|
||||||
fn remove(&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
|
|
||||||
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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -30,7 +30,7 @@ impl SpecificPackageManager for ArchlinuxPackageManager {
|
||||||
Ok(())
|
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()?;
|
let res = Cmd::new("pacman").arg("-R").args(list.list()).run()?;
|
||||||
|
|
||||||
if ! res.success {
|
if ! res.success {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use duct::cmd as duct_cmd;
|
use duct::cmd as duct_cmd;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
|
@ -8,6 +9,7 @@ pub struct Cmd {
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
stdin: Option<String>,
|
stdin: Option<String>,
|
||||||
chdir: Option<PathBuf>,
|
chdir: Option<PathBuf>,
|
||||||
|
env: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cmd {
|
impl Cmd {
|
||||||
|
@ -17,11 +19,12 @@ impl Cmd {
|
||||||
args: Vec::new(),
|
args: Vec::new(),
|
||||||
stdin: None,
|
stdin: None,
|
||||||
chdir: None,
|
chdir: None,
|
||||||
|
env: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arg<T: AsRef<str>>(self, arg: T) -> Cmd {
|
pub fn arg<T: AsRef<str>>(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());
|
args.push(arg.as_ref().to_string());
|
||||||
|
|
||||||
|
@ -30,12 +33,13 @@ impl Cmd {
|
||||||
args,
|
args,
|
||||||
stdin,
|
stdin,
|
||||||
chdir,
|
chdir,
|
||||||
|
env,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn args<T: AsRef<str>>(self, new_args: & [ T ]) -> Cmd {
|
pub fn args<T: AsRef<str>>(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 {
|
for arg in new_args {
|
||||||
args.push(arg.as_ref().to_string());
|
args.push(arg.as_ref().to_string());
|
||||||
|
@ -46,10 +50,12 @@ impl Cmd {
|
||||||
args,
|
args,
|
||||||
stdin,
|
stdin,
|
||||||
chdir,
|
chdir,
|
||||||
} }
|
env,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stdin<T: AsRef<str>>(self, input: T) -> Cmd {
|
pub fn stdin<T: AsRef<str>>(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 {
|
stdin = if let Some(mut s) = stdin {
|
||||||
s.push_str(input.as_ref());
|
s.push_str(input.as_ref());
|
||||||
|
@ -63,25 +69,47 @@ impl Cmd {
|
||||||
args,
|
args,
|
||||||
stdin,
|
stdin,
|
||||||
chdir,
|
chdir,
|
||||||
|
env,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chdir<T: AsRef<str>>(self, dir: T) -> Cmd {
|
pub fn chdir<T: AsRef<str>>(self, dir: T) -> Cmd {
|
||||||
let Self { program, args, stdin, .. } = self;
|
let Self { program, args, stdin, env, .. } = self;
|
||||||
|
|
||||||
Cmd {
|
Cmd {
|
||||||
program,
|
program,
|
||||||
args,
|
args,
|
||||||
stdin,
|
stdin,
|
||||||
chdir: Some(PathBuf::from(dir.as_ref())),
|
chdir: Some(PathBuf::from(dir.as_ref())),
|
||||||
|
env,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn env<K: AsRef<str>, V: AsRef<str>>(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<CmdOutput, std::io::Error> {
|
pub fn run(self) -> Result<CmdOutput, std::io::Error> {
|
||||||
let Self { program, args, stdin, chdir, .. } = self;
|
let Self { program, args, stdin, chdir, env, .. } = self;
|
||||||
|
|
||||||
let mut cmd = duct_cmd(&program, &args);
|
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 {
|
if let Some(stdin) = &stdin {
|
||||||
cmd = cmd.stdin_bytes(stdin.as_bytes());
|
cmd = cmd.stdin_bytes(stdin.as_bytes());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user