cargo-nw 0.1.1

Cargo subcommand for building NW application deployment packages (redistributables) for Windows, MacOS and Linux.
use crate::prelude::*;
use async_std::path::*;
use std::str::FromStr;
use uuid::Uuid;

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ScriptKind {
    Bash,
    Sh,
    Zsh,
    Bat,
    Cmd,
    Ps1,
    Other(String),
}

impl ToString for ScriptKind {
    fn to_string(&self) -> String {
        match self {
            ScriptKind::Bash => "bash",
            ScriptKind::Sh => "sh",
            ScriptKind::Zsh => "zsh",
            ScriptKind::Bat => "bat",
            ScriptKind::Cmd => "cmd",
            ScriptKind::Ps1 => "ps1",
            ScriptKind::Other(s) => s.as_str(),
        }
        .to_string()
    }
}

impl FromStr for ScriptKind {
    type Err = ();
    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        match s {
            "bash" => Ok(ScriptKind::Bash),
            "sh" => Ok(ScriptKind::Sh),
            "zsh" => Ok(ScriptKind::Zsh),
            "bat" => Ok(ScriptKind::Bat),
            "cmd" => Ok(ScriptKind::Cmd),
            "ps1" => Ok(ScriptKind::Ps1),
            _ => Ok(ScriptKind::Other(s.to_string())),
        }
    }
}

impl ScriptKind {
    pub fn interpreter(&self) -> Option<Vec<String>> {
        match self {
            ScriptKind::Bash => Some(vec!["bash"]),
            ScriptKind::Sh => Some(vec!["sh"]),
            ScriptKind::Zsh => Some(vec!["zsh"]),
            ScriptKind::Bat => Some(vec!["cmd.exe", "/k"]),
            ScriptKind::Cmd => Some(vec!["cmd.exe", "/k"]),
            ScriptKind::Ps1 => Some(vec!["powershell.exe"]),
            ScriptKind::Other(_) => None,
        }
        .map(|v| v.iter().map(|s| s.to_string()).collect::<Vec<_>>())
    }
}

#[derive(Debug, Clone, Deserialize)]
pub struct Script {
    #[serde(rename = "type")]
    pub kind: ScriptKind,
    pub name: Option<String>,
    pub interpreter: Option<Vec<String>>,
    pub script: String,
}

impl Script {
    pub async fn execute(&self, tpl: &Tpl, cwd: &Path) -> Result<()> {
        let name = self
            .name
            .clone()
            .map(|s| format!("'{s}'"))
            .unwrap_or_else(|| "".to_string());
        log_info!("Script", "running script {name}");
        let file = format!(
            "{}.{}",
            self.name
                .clone()
                .unwrap_or_else(|| Uuid::new_v4().to_string()),
            self.kind.to_string()
        );
        let mut argv = self
            .interpreter
            .clone()
            .or_else(|| self.kind.interpreter())
            .unwrap_or_else(|| {
                panic!(
                    "unable to determine interpreter for script `{file}`; please specify explicitly"
                )
            });
        let file = cwd.join(&file);
        async_std::fs::write(&file, &self.script).await?;
        argv.push(file.to_str().unwrap().to_string());
        let proc = argv.remove(0);

        cmd(proc, argv).dir(cwd).full_env(&tpl.map).run()?;

        Ok(())
    }
}