asimov_patterns/programs/runner.rs
1// This is free and unencumbered software released into the public domain.
2
3//! Language-runtime execution: the runner marker trait and named definitions.
4
5use crate::Execute;
6use alloc::{collections::btree_map::BTreeMap, string::String, vec::Vec};
7use bon::Builder;
8
9/// A language runtime that executes program text and emits its result as text.
10///
11/// The program defines its language and version, input grammar and encoding,
12/// how definitions are exposed to code, and how results are represented.
13/// Executing code can have external side effects; the pattern does not imply
14/// sandboxing, determinism, or rollback after failure.
15///
16/// # Command-line contract
17///
18/// `PROGRAM [OPTIONS] [INPUT-FILE]`
19///
20/// The program file defaults to `-` (stdin); the execution result goes to
21/// stdout. [`RunnerOptions::define`] supplies runtime variables through the
22/// repeatable `--define=VAR=VAL` option. No standard input/output-format
23/// options or output-file operand are defined for this pattern.
24///
25/// `T` is the implementation's result representation. This trait identifies the
26/// language-runtime role, not generic process launching. See [`crate::programs`]
27/// for links to concrete execution behavior and the [runner specification][spec].
28///
29/// [spec]: https://asimov-specs.github.io/program-patterns/#runner
30pub trait Runner<T>: Execute<T> {}
31
32/// Runtime variable definitions and additional arguments for a [`Runner`].
33///
34/// `Default` creates an empty definition map and argument list. The map holds
35/// at most one value per name and is forwarded in key order, not insertion
36/// order. Values are stored without validating the selected runtime's naming
37/// or value rules.
38///
39/// # Examples
40///
41/// ```rust
42/// use asimov_patterns::RunnerOptions;
43///
44/// let options = RunnerOptions::builder()
45/// .define("mode", "preview")
46/// .define("expression", "a=b")
47/// .build();
48///
49/// assert_eq!(options.define.get("expression").map(String::as_str), Some("a=b"));
50/// ```
51#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Builder)]
52#[builder(derive(Debug), on(String, into))]
53pub struct RunnerOptions {
54 /// Additional arguments appended after definitions, including an optional program file.
55 ///
56 /// Each string is one literal argument, without shell expansion. To express
57 /// ordered or repeated `--define` arguments that the map cannot represent,
58 /// put them here and leave those keys out of [`define`](Self::define).
59 /// The program's documented duplicate-definition policy then applies.
60 /// See [`crate::programs`] for option and operand ordering.
61 #[builder(field)]
62 pub other: Vec<String>,
63
64 /// Named runtime values passed as `--define=VAR=VAL` (`-D` in the CLI).
65 ///
66 /// The runner emits one argument per entry in [`BTreeMap`] key order. The
67 /// program splits the argument value at its first `=`: names must be
68 /// nonempty and cannot contain `=`, while values may be empty or contain
69 /// additional `=` characters. These constraints are not checked here.
70 ///
71 /// Replacing a key in this map replaces its previous value before execution;
72 /// it does not send duplicate definitions to the program. Definitions are
73 /// runtime arguments, not environment-variable assignments or shell source.
74 #[builder(field)]
75 pub define: BTreeMap<String, String>,
76}
77
78impl<S: runner_options_builder::State> RunnerOptionsBuilder<S> {
79 /// Appends one literal argument to [`RunnerOptions::other`], preserving order.
80 pub fn other(mut self, flag: impl Into<String>) -> Self {
81 self.other.push(flag.into());
82 self
83 }
84
85 /// Appends a present argument to [`RunnerOptions::other`]; `None` adds nothing.
86 pub fn maybe_other(mut self, flag: Option<impl Into<String>>) -> Self {
87 if let Some(flag) = flag {
88 self.other.push(flag.into());
89 }
90 self
91 }
92
93 /// Inserts a runtime definition, replacing any earlier value for the same key.
94 ///
95 /// Calls for different keys accumulate in [`RunnerOptions::define`], which
96 /// is emitted in key order. Neither the name nor the value is validated.
97 pub fn define(mut self, key: impl Into<String>, val: impl Into<String>) -> Self {
98 self.define.insert(key.into(), val.into());
99 self
100 }
101}