bctx-weave 0.1.23

bctx-weave — FilterMesh lens pipeline, CLI interception, domain compression
Documentation
use forge::signal::compactor;
use once_cell::sync::Lazy;
use regex::Regex;

// RSpec passing example lines: "  example description (0.01234 seconds)"
static RSPEC_PASS_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\s+\.\n?").unwrap());
// RSpec progress dots block (all dots on one line)
static DOTS_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^[\.F\*]+\n?").unwrap());
// Rake verbose task headers
static RAKE_TASK_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\(in /[^\)]+\)\n?").unwrap());

// ── rspec ─────────────────────────────────────────────────────────────────────

pub fn compress_rspec(raw: &str, exit_code: i32) -> String {
    let cleaned = compactor::normalise(raw);
    if exit_code == 0 {
        // Keep only the summary line "X examples, 0 failures"
        let kept: Vec<&str> = cleaned
            .lines()
            .filter(|l| {
                l.contains("example") && (l.contains("failure") || l.contains("pending"))
                    || l.contains("Finished in")
                    || l.contains("seconds")
            })
            .collect();
        if !kept.is_empty() {
            return kept.join("\n");
        }
    }
    // On failure: strip passing dot lines, keep failure details
    let s = DOTS_RE.replace_all(&cleaned, "");
    let s = RSPEC_PASS_RE.replace_all(&s, "");
    compactor::collapse_blanks(&s)
}

// ── minitest ──────────────────────────────────────────────────────────────────

pub fn compress_minitest(raw: &str, exit_code: i32) -> String {
    let cleaned = compactor::normalise(raw);
    if exit_code == 0 {
        let kept: Vec<&str> = cleaned
            .lines()
            .filter(|l| l.contains("runs, ") || l.contains("assertions, "))
            .collect();
        if !kept.is_empty() {
            return kept.join("\n");
        }
    }
    // Failures: keep failing lines
    cleaned
        .lines()
        .filter(|l| !l.trim_start().starts_with('.'))
        .collect::<Vec<_>>()
        .join("\n")
}

// ── rake ──────────────────────────────────────────────────────────────────────

pub fn compress_rake(raw: &str) -> String {
    let cleaned = compactor::normalise(raw);
    let s = RAKE_TASK_RE.replace_all(&cleaned, "");
    compactor::collapse_blanks(&s)
}

// ── top-level dispatcher ──────────────────────────────────────────────────────

pub fn compress_ruby_test(prog: &str, raw: &str, exit_code: i32) -> String {
    match prog {
        "rspec" => compress_rspec(raw, exit_code),
        "minitest" => compress_minitest(raw, exit_code),
        "rake" => compress_rake(raw),
        _ => compress_rspec(raw, exit_code),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn rspec_success_keeps_summary() {
        let raw = "....\nFinished in 0.012 seconds\n4 examples, 0 failures\n";
        let out = compress_rspec(raw, 0);
        assert!(out.contains("examples"), "{out}");
        assert!(!out.contains("...."), "{out}");
    }

    #[test]
    fn rspec_failure_strips_dots() {
        let raw = "...F.\n\nFailures:\n  1) MyClass does foo\n     Failure/Error: expect(1).to eq(2)\n5 examples, 1 failure\n";
        let out = compress_rspec(raw, 1);
        assert!(!out.contains("...F"), "{out}");
        assert!(out.contains("Failure") || out.contains("failure"), "{out}");
    }

    #[test]
    fn rake_strips_working_dir() {
        let raw = "(in /home/user/project)\ntask1: building\ntask1: done\n";
        let out = compress_rake(raw);
        assert!(!out.contains("(in /home/"), "{out}");
        assert!(out.contains("task1"), "{out}");
    }
}