Documentation
use std::{process::Command, rc::Rc};

use crate::{
    functions_definitions::{Arguments, Example, FunctionDefinitions},
    json_value::JsonValue,
    processor::Context,
    selection::Get,
};

pub fn get() -> FunctionDefinitions {
    FunctionDefinitions::new("trigger", 1, usize::MAX, |vec| {
        struct Impl(Vec<Rc<dyn Get>>);
        impl Get for Impl {
            fn get(&self, context: &Context) -> Option<JsonValue> {
                let Some(JsonValue::String(command)) = self.0.apply(context, 0) else {
                    return None;
                };
                let mut command = Command::new(command);
                for arg in self.0.iter().skip(1) {
                    let Some(JsonValue::String(arg)) = arg.get(context) else {
                        return None;
                    };
                    command.arg(arg);
                }
                let Ok(child) = command.spawn() else {
                    return None;
                };
                Some(f64::from(child.id()).into())
            }
        }
        Rc::new(Impl(vec))
    })
    .add_description_line("Trigger an external process and return it's process ID.")
    .add_description_line("If all the arguments are strings run a process with that list.")
    .add_description_line("The result is the process ID:")
    .add_example(
        Example::new()
            .add_argument("\"echo\"")
            .add_argument("\"hello\"")
            .add_argument("\"world\"")
            .validate_output(|output| {
                if cfg!(windows) {
                    output.is_none()
                } else {
                    matches!(output, Some(JsonValue::Number(_)))
                }
            }),
    )
    .add_example(Example::new().add_argument("\"no such exec\""))
    .add_example(
        Example::new()
            .add_argument("23")
            .explain("23 is not a string"),
    )
    .add_example(
        Example::new()
            .add_argument("\"echo\"")
            .add_argument("23")
            .explain("23 is not a string"),
    )
}