blue-lang-cli 0.0.70

The blue command line: run, fmt, ast, erase, check.
//! `blue replay TRACE`: run a crashed process again from its trace.
//!
//! The trace (`blue_lang_proc::trace`) holds the process's program and its
//! tape. The replay runs on a fresh interpreter in which every process word
//! and host name reads the tape, so it touches nothing outside itself and sees
//! the clock exactly as the crashed run did.

use std::path::{Path, PathBuf};
use std::process::ExitCode;

use blue_lang_proc::{replay, Trace};

/// Where `blue run` writes crash traces: `BLUE_TRACE_DIR`, or a `blue-traces`
/// directory under the system's temporary directory. `BLUE_TRACE_DIR=` (empty)
/// keeps traces in memory only.
pub fn trace_dir() -> Option<PathBuf> {
    match std::env::var_os("BLUE_TRACE_DIR") {
        Some(d) if d.is_empty() => None,
        Some(d) => Some(PathBuf::from(d)),
        None => Some(std::env::temp_dir().join("blue-traces")),
    }
}

/// Replay `path`; success when the replay ends the way the trace recorded.
pub fn run(path: &Path) -> Result<ExitCode, String> {
    let text = std::fs::read_to_string(path).map_err(|e| format!("{}: {e}", path.display()))?;
    let trace = Trace::parse(&text).map_err(|e| format!("{}: {e}", path.display()))?;
    println!(
        "process {}{} incarnation {}: {} tape entries{}",
        trace.pid,
        trace
            .definition
            .as_deref()
            .map(|d| format!(" ({d})"))
            .unwrap_or_default(),
        trace.incarnation,
        trace.tape.len(),
        if trace.truncated { ", truncated" } else { "" },
    );
    println!("recorded: {} {}", trace.reason_kind, trace.reason_text);
    let r = replay(&trace, blue_lang_runtime::interpreter_hostless());
    println!(
        "replayed: {} {} ({} of {} tape entries)",
        r.kind, r.text, r.consumed, r.tape_len
    );
    if let Some(d) = &r.diverged {
        println!("diverged: {d}");
    }
    if trace.truncated {
        println!("not replayable: the tape outgrew its bound, and a replay needs all of it");
    }
    if r.reproduced {
        println!("reproduced");
        Ok(ExitCode::SUCCESS)
    } else {
        println!("not reproduced");
        Ok(ExitCode::FAILURE)
    }
}