Skip to main content

asimov_runner/programs/
runner.rs

1// This is free and unencumbered software released into the public domain.
2
3//! Language runtime execution with named definitions and program input.
4
5use crate::{Executor, ExecutorError, Input, Output};
6use alloc::{boxed::Box, format, vec::Vec};
7use async_trait::async_trait;
8use derive_more::Debug;
9use std::{ffi::OsStr, io::Cursor, process::Stdio};
10
11pub use asimov_patterns::RunnerOptions;
12
13/// Raw stdout bytes captured from a successful [`Runner`], or an execution error.
14///
15/// The cursor is positioned at zero and is empty when stdout is not captured.
16/// The pattern specifies a text execution result; this wrapper returns its raw
17/// bytes without decoding or enforcing an encoding.
18pub type RunnerResult = std::result::Result<Cursor<Vec<u8>>, ExecutorError>; // TODO
19
20/// An external [runner] that executes program text in a language runtime.
21///
22/// The pattern consumes text conforming to the runtime's grammar and produces
23/// the execution result as text. This wrapper transports input and output as
24/// bytes; the external program parses and executes the input.
25///
26/// Each definition is passed as a `--define=<key>=<value>` argument; its meaning
27/// is determined by the runtime. Input and output use the buffering and
28/// stream-handling behavior described in [`crate::programs`]. For direct
29/// control over command configuration, use [`Executor`] instead.
30///
31/// [runner]: https://asimov-specs.github.io/program-patterns/#runner
32#[allow(unused)]
33#[derive(Debug)]
34pub struct Runner {
35    executor: Executor,
36    options: RunnerOptions,
37    input: Input,
38    output: Output,
39}
40
41impl Runner {
42    /// Configures a runner without starting it.
43    ///
44    /// Adds one `--define=<key>=<value>` argument per entry in `options.define`,
45    /// in `BTreeMap` key order, followed by `options.other`. Duplicate keys have
46    /// already been collapsed by the map; ordered or repeated definitions can
47    /// instead be supplied through `options.other`. Names must be nonempty and
48    /// contain no `=`; values may be empty or contain `=`. This constructor does
49    /// not validate them. The input and output values select stdin and stdout;
50    /// stderr is captured for failure diagnostics.
51    pub fn new(
52        program: impl AsRef<OsStr>,
53        input: Input,
54        output: Output,
55        options: RunnerOptions,
56    ) -> Self {
57        let mut executor = Executor::new(program);
58        executor
59            .command()
60            .args(
61                &options
62                    .define
63                    .iter()
64                    .map(|(k, v)| format!("--define={}={}", k, v))
65                    .collect::<Vec<_>>(),
66            )
67            .args(&options.other)
68            .stdin(input.as_stdio())
69            .stdout(output.as_stdio())
70            .stderr(Stdio::piped());
71
72        Self {
73            executor,
74            options,
75            input,
76            output,
77        }
78    }
79
80    /// Sends the remaining input to a new child and returns captured stdout bytes.
81    ///
82    /// # Errors
83    ///
84    /// Returns an [`ExecutorError`] if spawning, copying input, or waiting fails,
85    /// or if the runner exits unsuccessfully.
86    pub async fn execute(&mut self) -> RunnerResult {
87        let stdout = self
88            .executor
89            .execute_with_io(&mut self.input, &mut self.output)
90            .await?;
91        Ok(stdout)
92    }
93}
94
95impl asimov_patterns::Runner<Cursor<Vec<u8>>> for Runner {}
96
97#[async_trait]
98impl asimov_patterns::Execute<Cursor<Vec<u8>>> for Runner {
99    type Error = ExecutorError;
100
101    async fn execute(&mut self) -> RunnerResult {
102        self.execute().await
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    //use super::*;
109    //use asimov_patterns::Execute;
110
111    #[tokio::test]
112    async fn test_execute() {
113        // TODO
114    }
115}