use anyhow::Context as _;
use schemars::JsonSchema;
use serde::Deserialize;
use std::io::{
BufRead as _,
BufReader,
};
use std::thread;
use super::{
Shell,
TaskContext,
};
use crate::defaults::default_verbose;
use crate::handle_output;
use crate::schema::get_output_handler;
#[derive(Debug, Default, Deserialize, JsonSchema)]
pub struct Precondition {
pub command: String,
#[serde(default)]
pub message: Option<String>,
#[serde(default)]
pub shell: Option<Shell>,
#[serde(default)]
pub work_dir: Option<String>,
#[serde(default)]
pub verbose: Option<bool>,
}
impl Precondition {
pub fn execute(&self, context: &TaskContext) -> anyhow::Result<()> {
assert!(!self.command.is_empty());
let verbose = self.verbose(context);
let stdout = get_output_handler(verbose);
let stderr = get_output_handler(verbose);
let mut cmd = self
.shell
.as_ref()
.map(|shell| shell.proc())
.unwrap_or_else(|| context.shell().proc());
cmd.arg(self.command.clone()).stdout(stdout).stderr(stderr);
if let Some(work_dir) = self.resolved_work_dir(context) {
cmd.current_dir(work_dir);
}
for (key, value) in context.env_vars.iter() {
cmd.env(key, value);
}
let mut cmd = cmd.spawn()?;
if verbose {
handle_output!(cmd.stdout, context);
handle_output!(cmd.stderr, context);
}
let status = cmd.wait()?;
if !status.success() {
if let Some(message) = &self.message {
anyhow::bail!("Precondition failed - {}", message);
} else {
anyhow::bail!("Precondition failed - {}", self.command);
}
}
Ok(())
}
fn verbose(&self, context: &TaskContext) -> bool {
self.verbose.or(context.verbose).unwrap_or(default_verbose())
}
fn resolved_work_dir(&self, context: &TaskContext) -> Option<std::path::PathBuf> {
self
.work_dir
.as_ref()
.map(|work_dir| context.resolve_from_config(work_dir))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_precondition_1() -> anyhow::Result<()> {
{
let yaml = "
command: 'echo \"Hello, World!\"'
message: 'This is a message'
";
let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
assert_eq!(precondition.command, "echo \"Hello, World!\"");
assert_eq!(precondition.message, Some("This is a message".into()));
assert_eq!(precondition.work_dir, None);
assert_eq!(precondition.verbose, None);
Ok(())
}
}
#[test]
fn test_precondition_2() -> anyhow::Result<()> {
{
let yaml = "
command: 'echo \"Hello, World!\"'
";
let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
assert_eq!(precondition.command, "echo \"Hello, World!\"");
assert_eq!(precondition.message, None);
assert_eq!(precondition.work_dir, None);
assert_eq!(precondition.verbose, None);
Ok(())
}
}
#[test]
fn test_precondition_3() -> anyhow::Result<()> {
{
let yaml = "
command: 'echo \"Hello, World!\"'
message: null
";
let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
assert_eq!(precondition.command, "echo \"Hello, World!\"");
assert_eq!(precondition.message, None);
assert_eq!(precondition.work_dir, None);
assert_eq!(precondition.verbose, None);
Ok(())
}
}
#[test]
fn test_precondition_4() -> anyhow::Result<()> {
{
let yaml = "
command: 'echo \"Hello, World!\"'
work_dir: /tmp
";
let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
assert_eq!(precondition.command, "echo \"Hello, World!\"");
assert_eq!(precondition.message, None);
assert_eq!(precondition.work_dir, Some("/tmp".into()));
assert_eq!(precondition.verbose, None);
Ok(())
}
}
#[test]
fn test_precondition_5() -> anyhow::Result<()> {
{
let yaml = "
command: 'echo \"Hello, World!\"'
verbose: true
";
let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
assert_eq!(precondition.command, "echo \"Hello, World!\"");
assert_eq!(precondition.message, None);
assert_eq!(precondition.work_dir, None);
assert_eq!(precondition.verbose, Some(true));
Ok(())
}
}
#[test]
fn test_precondition_6() -> anyhow::Result<()> {
{
let yaml = "
command: ls -la
message: Listing directory contents
shell: bash
work_dir: /tmp
verbose: false
";
let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
assert_eq!(precondition.command, "ls -la");
assert_eq!(
precondition.message,
Some("Listing directory contents".to_string())
);
if let Some(shell) = precondition.shell {
assert_eq!(shell.cmd(), "bash".to_string());
} else {
panic!("Expected shell to be Some");
}
assert_eq!(precondition.work_dir, Some("/tmp".into()));
assert_eq!(precondition.verbose, Some(false));
Ok(())
}
}
}