142 lines
3.6 KiB
Rust
Raw Normal View History

2023-04-19 21:49:30 +02:00
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
2023-10-05 17:58:41 +02:00
#[derive(Clone, Debug, Serialize, Deserialize)]
2023-04-19 21:49:30 +02:00
pub struct CommandArgs {
#[serde(flatten)]
pub args: Cmd,
pub condition: Option<SomeCondition>,
}
2023-10-05 17:58:41 +02:00
impl ModuleSetup<CommandModule, CommandArgs, CommandStatus, CommandError> for CommandArgs {
fn with_facts(self, _facts: &Facts) -> CommandModule {
CommandModule {
cmd: self.args,
condition: self.condition,
}
}
}
#[derive(Debug)]
2023-04-19 21:49:30 +02:00
pub struct CommandModule {
cmd: Cmd,
condition: Option<SomeCondition>,
}
impl CommandModule {
pub fn new() -> CommandBuilder<NoCmd, NoCondition> {
CommandBuilder::new()
}
}
impl ModuleSetup<CommandModule, CommandArgs, CommandStatus, CommandError> for CommandModule {
fn with_facts(self, _facts: &Facts) -> CommandModule {
self
2023-04-19 21:49:30 +02:00
}
}
impl Module<CommandArgs, CommandStatus, CommandError> 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<CommandStatus, CommandError> {
// 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,
}
}
2023-04-21 17:25:31 +02:00
pub fn stdout(&self) -> Option<String> {
match self {
Self::Done(ret) => Some(ret.stdout.to_string()),
Self::Skipped(_c) => None,
}
}
pub fn stderr(&self) -> Option<String> {
match self {
Self::Done(ret) => Some(ret.stdout.to_string()),
Self::Skipped(_c) => None,
}
}
2023-04-19 21:49:30 +02:00
}
/// Return of a Command that was effectively run
#[derive(Clone, Debug, Serialize)]
pub struct CommandReturn {
pub success: bool,
pub stdin: Option<String>,
pub dir: Option<PathBuf>,
pub stdout: String,
pub stderr: String,
}
#[derive(Debug, Serialize)]
pub struct CommandSkipped(SomeCondition);
#[derive(Clone, Debug, Serialize)]
pub struct CommandError(String);
impl From<std::io::Error> for CommandError {
fn from(e: std::io::Error) -> CommandError {
CommandError(e.to_string())
}
}