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};

/// `/echo` — produce text output.
///
/// `/echo(hello world)` — primary arg as output.
/// `/echo.text(hello world)` — `.text()` method as output.
/// `/echo` (with piped input) — pass-through.
pub struct Echo;

impl SlashCommand for Echo {
    fn name(&self) -> &str {
        "echo"
    }

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

    fn execute(
        &self,
        primary: Option<&str>,
        args: &[Arg],
        input: Option<&PipeValue>,
    ) -> Result<CommandOutput, ExecutionError> {
        // Primary arg takes precedence.
        if let Some(text) = primary {
            let mut out = text.to_string();
            out.push('\n');
            return Ok(CommandOutput {
                stdout: Some(out.into_bytes()),
                stderr: None,
                success: true,
            });
        }

        // Collect all .text() values.
        let texts: Vec<&str> = args
            .iter()
            .filter(|a| a.name == "text")
            .filter_map(|a| a.value.as_deref())
            .collect();

        if !texts.is_empty() {
            let mut out = texts.join(" ");
            out.push('\n');
            return Ok(CommandOutput {
                stdout: Some(out.into_bytes()),
                stderr: None,
                success: true,
            });
        }

        // Pass-through piped input.
        let output = match input {
            Some(PipeValue::Bytes(b)) => Some(b.clone()),
            Some(PipeValue::Context(ctx)) => Some(ctx.to_json().into_bytes()),
            None => None,
        };

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