crates/mimir-driver/src/bin/mimir-capture.rs
//! `mimir-capture <stdout-file> -- <program> <args…>` — run a tool, copying its
//! stdout BYTE FOR BYTE to `<stdout-file>` while it also streams to this
//! process's stdout; stderr passes through; the exit code is the tool's.
//!
//! Why: on the CONTAINER road (nornir `code_room::container`) a tool's stdout
//! comes back through the engine's log API, which frames it — a line longer
//! than a frame (`cargo metadata`'s one-line JSON) arrives cut in pieces. The
//! file is what the consumer parses; the log stream only shows progress. No
//! shell: the tool is started with its argv.
use std::io::{Read, Write};
use std::process::{Command, ExitCode, Stdio};
fn main() -> ExitCode {
let args: Vec<String> = std::env::args().skip(1).collect();
let (Some(file), Some(sep)) = (args.first(), args.get(1)) else {
eprintln!("usage: mimir-capture <stdout-file> -- <program> <args…>");
return ExitCode::from(2);
};
if sep != "--" || args.len() < 3 {
eprintln!("usage: mimir-capture <stdout-file> -- <program> <args…>");
return ExitCode::from(2);
}
let mut out = match std::fs::File::create(file) {
Ok(f) => f,
Err(e) => {
eprintln!("mimir-capture: cannot create {file}: {e}");
return ExitCode::from(2);
}
};
let mut child = match Command::new(&args[2]).args(&args[3..]).stdout(Stdio::piped()).stderr(Stdio::inherit()).spawn() {
Ok(c) => c,
Err(e) => {
eprintln!("mimir-capture: cannot start {}: {e}", args[2]);
return ExitCode::from(127);
}
};
let mut pipe = child.stdout.take().expect("piped");
let mut buf = [0u8; 64 * 1024];
let stdout = std::io::stdout();
loop {
match pipe.read(&mut buf) {
Ok(0) => break,
Ok(n) => {
if out.write_all(&buf[..n]).is_err() {
eprintln!("mimir-capture: writing {file} failed");
}
let mut s = stdout.lock();
let _ = s.write_all(&buf[..n]);
let _ = s.flush();
}
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
Err(e) => {
eprintln!("mimir-capture: reading the tool's stdout failed: {e}");
break;
}
}
}
let _ = out.flush();
match child.wait() {
Ok(st) => match st.code() {
Some(c) => ExitCode::from(c.clamp(0, 255) as u8),
None => ExitCode::from(128 + 9),
},
Err(e) => {
eprintln!("mimir-capture: wait failed: {e}");
ExitCode::from(2)
}
}
}