bctx-weave 0.1.11

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

// Gradle task execution lines like: "> Task :subproject:taskName"
static TASK_LINE_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^> Task :[^\n]+\n?").unwrap());
// Maven "[INFO] Building project..." lines
static MAVEN_INFO_RE: Lazy<Regex> = Lazy::new(|| {
    Regex::new(r"(?m)^\[INFO\] (Building |--- |Scanning for |Using base |Compiling)[^\n]+\n?")
        .unwrap()
});
static MAVEN_DOWNLOAD_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^(Downloading|Downloaded): https?://[^\n]+\n?").unwrap());
static GRADLE_DOWNLOAD_RE: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(?m)^Download https?://[^\n]+\n?").unwrap());

// ── gradle ────────────────────────────────────────────────────────────────────

pub fn compress_gradle(raw: &str, exit_code: i32) -> String {
    let cleaned = compactor::normalise(raw);
    let s = GRADLE_DOWNLOAD_RE.replace_all(&cleaned, "");
    if exit_code == 0 {
        // On success: keep task lines only if they contain "SKIPPED"/"UP-TO-DATE"/
        // plus the "BUILD SUCCESSFUL" summary
        let kept: Vec<&str> = s
            .lines()
            .filter(|l| {
                l.starts_with("BUILD")
                    || l.starts_with("FAILURE")
                    || (l.starts_with("> Task") && (l.contains("FAILED") || l.contains("SKIPPED")))
                    || l.contains("tests were run")
                    || l.contains("test") && l.contains("passed")
            })
            .collect();
        if kept.is_empty() {
            // Fallback: keep last 5 lines
            let lines: Vec<&str> = s.lines().filter(|l| !l.trim().is_empty()).collect();
            let start = lines.len().saturating_sub(5);
            return lines[start..].join("\n");
        }
        return kept.join("\n");
    }
    // On failure: strip passing task lines, keep FAILED tasks + errors
    let s = TASK_LINE_RE.replace_all(&s, "");
    // Keep error lines
    let kept: Vec<&str> = s
        .lines()
        .filter(|l| {
            !l.trim().is_empty()
                && (l.contains("error")
                    || l.contains("Error")
                    || l.contains("FAILED")
                    || l.contains("FAILURE")
                    || l.starts_with("e:")
                    || l.contains("Exception"))
        })
        .collect();
    if kept.is_empty() {
        return compactor::collapse_blanks(&s);
    }
    kept.join("\n")
}

// ── maven ─────────────────────────────────────────────────────────────────────

pub fn compress_maven(raw: &str, exit_code: i32) -> String {
    let cleaned = compactor::normalise(raw);
    let s = MAVEN_DOWNLOAD_RE.replace_all(&cleaned, "");
    let s = MAVEN_INFO_RE.replace_all(&s, "");
    if exit_code == 0 {
        // Keep BUILD SUCCESS line and test summary
        let kept: Vec<&str> = s
            .lines()
            .filter(|l| {
                l.contains("BUILD SUCCESS")
                    || l.contains("BUILD FAILURE")
                    || l.contains("Tests run")
                    || l.contains("ERROR")
                    || l.starts_with("[WARNING]")
            })
            .collect();
        if kept.is_empty() {
            let lines: Vec<&str> = s.lines().filter(|l| !l.trim().is_empty()).collect();
            let start = lines.len().saturating_sub(5);
            return lines[start..].join("\n");
        }
        return kept.join("\n");
    }
    // On failure: keep ERROR/FAILURE/[ERROR] lines
    let kept: Vec<&str> = s
        .lines()
        .filter(|l| {
            !l.trim().is_empty()
                && (l.contains("[ERROR]")
                    || l.contains("BUILD FAILURE")
                    || l.contains("Tests run") && l.contains("FAILURE"))
        })
        .collect();
    if kept.is_empty() {
        return compactor::collapse_blanks(&s);
    }
    kept.join("\n")
}

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

pub fn compress_jvm_build(prog: &str, raw: &str, exit_code: i32) -> String {
    match prog {
        "gradle" | "gradlew" | "./gradlew" => compress_gradle(raw, exit_code),
        "mvn" | "mvnw" | "./mvnw" => compress_maven(raw, exit_code),
        _ => compress_gradle(raw, exit_code),
    }
}

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

    #[test]
    fn gradle_success_keeps_build_successful() {
        let raw =
            "> Task :compileJava\n> Task :test\nBUILD SUCCESSFUL in 12s\n4 actionable tasks\n";
        let out = compress_gradle(raw, 0);
        assert!(out.contains("BUILD SUCCESSFUL"), "{out}");
    }

    #[test]
    fn gradle_strips_downloads() {
        let raw = "Download https://jcenter.bintray.com/com/google/guava/guava/30.0/guava-30.0.jar\n> Task :compileJava\nBUILD SUCCESSFUL in 5s\n";
        let out = compress_gradle(raw, 0);
        assert!(!out.contains("Download https://"), "{out}");
    }

    #[test]
    fn maven_strips_info_and_downloads() {
        let raw = "Downloading: https://repo.maven.apache.org/maven2/foo.jar\n[INFO] Building myproject 1.0.0\n[INFO] BUILD SUCCESS\n";
        let out = compress_maven(raw, 0);
        assert!(!out.contains("Downloading:"), "{out}");
        assert!(out.contains("BUILD SUCCESS"), "{out}");
    }

    #[test]
    fn gradle_failure_keeps_errors() {
        let raw = "> Task :compileJava FAILED\nerror: incompatible types: String cannot be converted to int\nBUILD FAILED\n";
        let out = compress_gradle(raw, 1);
        assert!(out.contains("FAILED") || out.contains("error"), "{out}");
    }
}