prax 0.1.1

a web penetration proxy hosted in neovim
Documentation
use std::process::Stdio;

use mlua::FromLua;
use tokio::io::AsyncWriteExt;

use super::interp::{Interp, Val};

pub type Func = usize;

#[derive(FromLua, Debug, Clone)]
pub enum Subst {
    Func(Func),
    System(String),
}

#[derive(thiserror::Error, Debug)]
pub enum SubstError {
    #[error("lua subst error {0}")]
    Lua(#[from] mlua::Error),

    #[error("lua subst error {0}")]
    Io(#[from] tokio::io::Error),

    #[error("failed to run command: {0}")]
    SystemFailure(i32),

    #[error("system output not utf8: {0}")]
    NonUTFOutput(#[from] std::string::FromUtf8Error),

    #[error("lua invoked and expected a string but got {0}")]
    TypeMismatch(Val),
}

impl Subst {
    pub async fn sub_num(&self, interp: &Interp, num: i64) -> Result<i64, SubstError> {
        match self {
            Subst::Func(slot) => {
                let res = interp.invoke(*slot, num.into()).await?;

                match res {
                    Val::Number(n) => Ok(n),
                    _ => Err(SubstError::TypeMismatch(res)),
                }
            }

            Subst::System(_) => todo!("implement running system command"),
        }
    }

    pub async fn subst(&self, interp: &Interp, content: String) -> Result<String, SubstError> {
        match self {
            Subst::Func(slot) => {
                let res = interp.invoke(*slot, content.into()).await?;

                match res {
                    Val::String(s) => Ok(s),
                    _ => Err(SubstError::TypeMismatch(res)),
                }
            }

            Subst::System(sh) => {
                let mut proc = tokio::process::Command::new("/bin/sh")
                    .arg("-c")
                    .arg(sh)
                    .stdin(Stdio::piped())
                    .stdout(Stdio::piped())
                    .spawn()?;

                proc.stdin
                    .take()
                    .unwrap()
                    .write_all(content.as_bytes())
                    .await?;
                let out = proc.wait_with_output().await?;

                if !out.status.success() {
                    return Err(SubstError::SystemFailure(out.status.code().unwrap_or(-1)));
                }

                let mut out = String::from_utf8(out.stdout)?;

                if out.ends_with('\n') {
                    out.pop();
                }

                Ok(out)
            }
        }
    }
}