#![allow(clippy::result_large_err)]

use serde::{Deserialize, Serialize};

use crate::{
    action::{ActionError, Context},
    action_impl::{spawn, ActionImpl},
};

/// Execute a snippet of shell script.
///
/// The snippet is executed using `bash`, with `set -xeuo pipefail` to make
/// it fail if any command fails, or an unset variable is used, and to trace
/// execution to allow easier debugging.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Shell {
    shell: String,
}

impl Shell {
    /// Create a new `Shell` action.
    pub fn new<S: Into<String>>(shell: S) -> Self {
        let shell = shell.into();
        Self { shell }
    }

    /// Shell snippet to run.
    pub fn shell(&self) -> &str {
        &self.shell
    }
}

impl ActionImpl for Shell {
    fn execute(&self, context: &mut Context) -> Result<(), ActionError> {
        let snippet = bash_snippet(&self.shell);
        spawn(context, &["bash", "-c", &snippet])?;
        Ok(())
    }
}

/// Construct a full snippet to give to Bash.
pub fn bash_snippet(snippet: &str) -> String {
    format!("set -xeuo pipefail\n{snippet}\n")
}
