use forge::signal::compactor;
use once_cell::sync::Lazy;
use regex::Regex;
static ANALYZED_RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?m)^INFO: Analyzed target[^\n]+\n?").unwrap());
static FOUND_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^INFO: Found \d+[^\n]+\n?").unwrap());
static ACTION_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\[\d+ / \d+\] [^\n]+\n?").unwrap());
pub fn compress_build(raw: &str, exit_code: i32) -> String {
let cleaned = compactor::normalise(raw);
let s = ANALYZED_RE.replace_all(&cleaned, "");
let s = FOUND_RE.replace_all(&s, "");
if exit_code == 0 {
let s = ACTION_RE.replace_all(&s, "");
return compactor::collapse_blanks(&s);
}
let s = ACTION_RE.replace_all(&s, "");
s.lines()
.filter(|l| {
let t = l.trim();
!t.is_empty()
&& (t.contains("ERROR")
|| t.contains("error")
|| t.contains("FAILED")
|| t.starts_with("Target")
|| t.starts_with("Build did NOT complete"))
})
.collect::<Vec<_>>()
.join("\n")
}
pub fn compress_test(raw: &str, exit_code: i32) -> String {
let cleaned = compactor::normalise(raw);
let s = ANALYZED_RE.replace_all(&cleaned, "");
let s = ACTION_RE.replace_all(&s, "");
if exit_code == 0 {
let kept: Vec<&str> = s
.lines()
.filter(|l| {
l.contains("PASSED") || l.contains("FAILED") || l.contains("Build completed")
})
.collect();
if !kept.is_empty() {
return kept.join("\n");
}
}
compactor::collapse_blanks(&s)
}
pub fn compress_bazel(subcmd: &str, raw: &str, exit_code: i32) -> String {
match subcmd.trim() {
"build" | "run" => compress_build(raw, exit_code),
"test" => compress_test(raw, exit_code),
"query" => {
let cleaned = compactor::normalise(raw);
let lines: Vec<&str> = cleaned.lines().collect();
if lines.len() > 50 {
return format!(
"{}\n... [{} more targets] ...",
lines[..50].join("\n"),
lines.len() - 50
);
}
cleaned
}
_ => compactor::normalise(raw),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_success_strips_progress() {
let raw = "INFO: Analyzed target //foo:bar (12 packages loaded)\n[1 / 42] Compiling src/foo.cc\n[42 / 42] Linking foo\nTarget //foo:bar up-to-date: bazel-bin/foo/bar\nINFO: Build completed successfully.\n";
let out = compress_build(raw, 0);
assert!(!out.contains("Analyzed target"), "{out}");
assert!(!out.contains("[1 / 42]"), "{out}");
assert!(out.contains("Build completed"), "{out}");
}
#[test]
fn test_keeps_pass_fail() {
let raw = "INFO: Analyzed target //test:mytest\n[1 / 2] Compiling test/mytest.cc\n//test:mytest PASSED in 0.3s\nINFO: Build completed successfully.\n";
let out = compress_test(raw, 0);
assert!(out.contains("PASSED"), "{out}");
}
}