slash-lib 0.1.0

Executor types and high-level API for the slash-command language
Documentation
use slash_lang::parser::ast::Arg;

use crate::command::{MethodDef, SlashCommand};
use crate::executor::{CommandOutput, ExecutionError, PipeValue};

/// `/write(path)` — write piped input to a file.
///
/// Pure `std::fs::write`. Overwrites by default.
/// `/write(path).append` — append instead of overwrite.
pub struct Write;

impl SlashCommand for Write {
    fn name(&self) -> &str {
        "write"
    }

    fn methods(&self) -> &[MethodDef] {
        static METHODS: [MethodDef; 1] = [MethodDef::flag("append")];
        &METHODS
    }

    fn execute(
        &self,
        primary: Option<&str>,
        args: &[Arg],
        input: Option<&PipeValue>,
    ) -> Result<CommandOutput, ExecutionError> {
        let path = primary.ok_or_else(|| {
            ExecutionError::Runner("/write requires a path: /write(file.txt)".into())
        })?;

        let data = match input {
            Some(PipeValue::Bytes(b)) => b.as_slice(),
            Some(PipeValue::Context(ctx)) => {
                let json = ctx.to_json();
                return write_data(path, json.as_bytes(), args);
            }
            None => return Err(ExecutionError::Runner("/write: no piped input".into())),
        };

        write_data(path, data, args)
    }
}

fn write_data(path: &str, data: &[u8], args: &[Arg]) -> Result<CommandOutput, ExecutionError> {
    let append = args.iter().any(|a| a.name == "append");

    if append {
        use std::io::Write as _;
        let mut file = std::fs::OpenOptions::new()
            .create(true)
            .append(true)
            .open(path)
            .map_err(|e| ExecutionError::Runner(format!("/write({}): {}", path, e)))?;
        file.write_all(data)
            .map_err(|e| ExecutionError::Runner(format!("/write({}): {}", path, e)))?;
    } else {
        std::fs::write(path, data)
            .map_err(|e| ExecutionError::Runner(format!("/write({}): {}", path, e)))?;
    }

    Ok(CommandOutput {
        stdout: None,
        stderr: None,
        success: true,
    })
}