Skip to main content

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}