use crate::utils::cmd::Cmd; use std::path::PathBuf; pub mod builder; pub mod condition; use builder::{CommandBuilder, NoCondition, NoCmd}; use condition::SomeCondition; use crate::{Module, ModuleSetup, Facts}; /// A copy of the argument passed to the [`CommandBuilder`], /// as returned serialized in the task run output #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CommandArgs { #[serde(flatten)] pub args: Cmd, pub condition: Option, } impl ModuleSetup for CommandArgs { fn with_facts(self, _facts: &Facts) -> CommandModule { CommandModule { cmd: self.args, condition: self.condition, } } } #[derive(Debug)] pub struct CommandModule { cmd: Cmd, condition: Option, } impl CommandModule { pub fn new() -> CommandBuilder { CommandBuilder::new() } } impl ModuleSetup for CommandModule { fn with_facts(self, _facts: &Facts) -> CommandModule { self } } impl Module for CommandModule { fn serialize_args(&self) -> serde_json::Value { serde_json::to_value(&CommandArgs { args: self.cmd.clone(), condition: self.condition.clone(), }).unwrap() } fn module_name(&self) -> &'static str { "command" } fn run(self) -> Result { // The command was built successfully, run it let Self { cmd, condition, .. } = self; // Check conditions if let Some(condition) = condition { if ! condition.should_run() { return Ok(CommandStatus::Skipped(condition)); } } let res = cmd.run()?; Ok(CommandStatus::Done( CommandReturn { success: res.success, stdin: res.stdin, dir: res.dir, stdout: res.stdout, stderr: res.stderr, } )) } } /// Command module has inner parameters to determine whether the command should run (`creates` and `removes`). Because of this, /// it returns a special [`CommandStatus`] that can be Done or Skipped. These variants are flattened into the textual/JSON output #[derive(Clone, Debug, Serialize)] #[serde(tag="status")] pub enum CommandStatus { #[serde(rename="done")] Done(CommandReturn), #[serde(rename="skip")] Skipped(SomeCondition), } impl CommandStatus { pub fn success(&self) -> bool { match self { Self::Done(ret) => ret.success, Self::Skipped(_c) => true, } } pub fn stdout(&self) -> Option { match self { Self::Done(ret) => Some(ret.stdout.to_string()), Self::Skipped(_c) => None, } } pub fn stderr(&self) -> Option { match self { Self::Done(ret) => Some(ret.stdout.to_string()), Self::Skipped(_c) => None, } } } /// Return of a Command that was effectively run #[derive(Clone, Debug, Serialize)] pub struct CommandReturn { pub success: bool, pub stdin: Option, pub dir: Option, pub stdout: String, pub stderr: String, } #[derive(Debug, Serialize)] pub struct CommandSkipped(SomeCondition); #[derive(Clone, Debug, Serialize)] pub struct CommandError(String); impl From for CommandError { fn from(e: std::io::Error) -> CommandError { CommandError(e.to_string()) } }