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}