mk_rs_core/shell.rs
1//! Shell abstraction for recipe execution.
2//!
3//! Defines the [`Shell`] trait, which abstracts the strategy for executing
4//! recipe scripts. The trait lives in mk-core; concrete implementations
5//! (`ShShell`, `DuckShell`, `CustomShell`) are provided by the mk-shell crate.
6//!
7//! Shell-specific quoting rules (`find_unescaped`, `quote`) enable the
8//! attribute parser to handle `=` inside quoted strings correctly.
9
10use std::collections::HashMap;
11use std::path::Path;
12
13/// Result of executing a recipe through a shell.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct ShellResult {
16 /// Exit code. 0 = success.
17 pub exit_code: i32,
18 /// Captured stdout.
19 pub stdout: String,
20 /// Captured stderr.
21 pub stderr: String,
22}
23
24pub use crate::error::ShellError;
25
26/// Abstraction for executing recipe scripts.
27/// Implementations: sh (POSIX /bin/sh), rc (Plan 9 rc), duckscript (future).
28pub trait Shell: Send + Sync {
29 /// Human-readable shell name (e.g. "sh", "rc").
30 fn name(&self) -> &str;
31
32 /// Execute a recipe script.
33 /// `recipe` — the full script text (multiline string).
34 /// `env` — environment variables to pass to the shell process.
35 /// `dir` — working directory for the recipe.
36 fn execute(
37 &self,
38 recipe: &str,
39 env: &HashMap<String, String>,
40 dir: &Path,
41 ) -> Result<ShellResult, ShellError>;
42
43 /// Find unescaped instances of a character in a string.
44 /// Used by the parser to detect assignment attributes.
45 /// E.g. find unescaped '=' to separate attr from value.
46 fn find_unescaped(&self, input: &str, ch: char) -> Vec<usize>;
47
48 /// Shell-quote a string so it's safe as a shell argument.
49 fn quote(&self, token: &str) -> String;
50}
51
52#[cfg(test)]
53mod tests {
54 // Shell trait tests are in mk-shell's ShShell tests.
55}