Skip to main content

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))]
53// TODO: #[cfg_attr(feature = "clap", derive(clap::Args))]
54pub struct RunnerOptions {
55    /// Additional arguments appended after definitions, including an optional program file.
56    ///
57    /// Each string is one literal argument, without shell expansion. To express
58    /// ordered or repeated `--define` arguments that the map cannot represent,
59    /// put them here and leave those keys out of [`define`](Self::define).
60    /// The program's documented duplicate-definition policy then applies.
61    /// See [`crate::programs`] for option and operand ordering.
62    #[builder(field)]
63    pub other: Vec<String>,
64
65    /// Named runtime values passed as `--define=VAR=VAL` (`-D` in the CLI).
66    ///
67    /// The runner emits one argument per entry in [`BTreeMap`] key order. The
68    /// program splits the argument value at its first `=`: names must be
69    /// nonempty and cannot contain `=`, while values may be empty or contain
70    /// additional `=` characters. These constraints are not checked here.
71    ///
72    /// Replacing a key in this map replaces its previous value before execution;
73    /// it does not send duplicate definitions to the program. Definitions are
74    /// runtime arguments, not environment-variable assignments or shell source.
75    #[builder(field)]
76    pub define: BTreeMap<String, String>,
77}
78
79impl<S: runner_options_builder::State> RunnerOptionsBuilder<S> {
80    /// Appends one literal argument to [`RunnerOptions::other`], preserving order.
81    pub fn other(mut self, flag: impl Into<String>) -> Self {
82        self.other.push(flag.into());
83        self
84    }
85
86    /// Appends a present argument to [`RunnerOptions::other`]; `None` adds nothing.
87    pub fn maybe_other(mut self, flag: Option<impl Into<String>>) -> Self {
88        if let Some(flag) = flag {
89            self.other.push(flag.into());
90        }
91        self
92    }
93
94    /// Inserts a runtime definition, replacing any earlier value for the same key.
95    ///
96    /// Calls for different keys accumulate in [`RunnerOptions::define`], which
97    /// is emitted in key order. Neither the name nor the value is validated.
98    pub fn define(mut self, key: impl Into<String>, val: impl Into<String>) -> Self {
99        self.define.insert(key.into(), val.into());
100        self
101    }
102}