use forge::signal::compactor;
use once_cell::sync::Lazy;
use regex::Regex;
static INSTALL_PROGRESS_RE: Lazy<Regex> = Lazy::new(||
Regex::new(r"(?m)^\[[\d/]+\] (Resolving|Fetching|Linking|Building)[^\n]*\n?").unwrap());
static WARN_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^warn: [^\n]+\n?").unwrap());
pub fn compress_install(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let s = INSTALL_PROGRESS_RE.replace_all(&cleaned, "");
let s = WARN_RE.replace_all(&s, "");
let kept: Vec<&str> = s
.lines()
.filter(|l| {
let t = l.trim();
t.is_empty()
|| t.contains("packages installed")
|| t.contains("Done")
|| t.contains("error")
|| t.starts_with("error")
|| t.contains("blocked")
})
.collect();
let result = compactor::collapse_blanks(&kept.join("\n"));
if result.trim().is_empty() {
compactor::collapse_blanks(&s)
} else {
result
}
}
pub fn compress_test(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(" pass")
|| l.contains(" fail")
|| l.contains("Tests:")
|| l.contains("Duration:")
})
.collect();
if !kept.is_empty() {
return kept.join("\n");
}
}
cleaned
.lines()
.filter(|l| !l.trim_start().starts_with('✓'))
.collect::<Vec<_>>()
.join("\n")
}
pub fn compress_bun(subcmd: &str, raw: &str, exit_code: i32) -> String {
let sub = subcmd.trim();
match sub {
"install" | "add" | "remove" | "update" => compress_install(raw),
"test" => compress_test(raw, exit_code),
"run" | "x" => compactor::normalise(raw),
_ => compactor::normalise(raw),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn install_strips_step_lines() {
let raw = "[1/5] Resolving packages...\n[2/5] Fetching packages...\n[3/5] Linking packages...\n47 packages installed [1.23s]\n";
let out = compress_install(raw);
assert!(!out.contains("Resolving packages"), "{out}");
assert!(out.contains("packages installed"), "{out}");
}
#[test]
fn test_success_keeps_summary() {
let raw = " ✓ foo test (12ms)\n ✓ bar test (8ms)\n Tests: 2 pass\n Duration: 0.02s\n";
let out = compress_test(raw, 0);
assert!(out.contains("pass"), "{out}");
assert!(!out.contains("✓ foo"), "{out}");
}
#[test]
fn test_failure_keeps_failing() {
let raw =
" ✓ foo test\n ✗ bar test\n AssertionError: expected 1 to be 2\n Tests: 1 fail\n";
let out = compress_test(raw, 1);
assert!(out.contains("✗ bar"), "{out}");
assert!(!out.contains("✓ foo"), "{out}");
}
}