mxsh 0.2.0

Embeddable POSIX-style shell parser and runtime
Documentation
use mxsh::ShellBuilder;
use mxsh::embed::{StdioConfig, VariableAttributes};
use mxsh::runtime::testing::{InMemoryRuntime, StringStdioOut};

fn main() {
    let stdout = StringStdioOut::new();
    let mut shell = ShellBuilder::new()
        .env("PATH", "", VariableAttributes::EXPORT)
        .stdio(StdioConfig {
            stdout: stdout.fd(),
            ..StdioConfig::default()
        })
        .register_command_not_found_handler(|context, argv| {
            let formatted = argv
                .iter()
                .enumerate()
                .map(|(i, arg)| format!("argv[{i}] = {arg:?}"))
                .collect::<Vec<_>>()
                .join("\n");
            let _ = context.write_stdout_line(&formatted);
            0
        })
        .build(InMemoryRuntime::new())
        .expect("shell should build");

    let result = shell.run("nonexistent_command foo bar baz");
    assert_eq!(result.status, 0);
    let output = stdout.collect();
    println!("{output}");
    assert_eq!(
        output,
        "argv[0] = \"nonexistent_command\"\n\
         argv[1] = \"foo\"\n\
         argv[2] = \"bar\"\n\
         argv[3] = \"baz\"\n"
    );
}