use forge::signal::compactor;
use once_cell::sync::Lazy;
use regex::Regex;
static COMPILE_RE: Lazy<Regex> = Lazy::new(||
Regex::new(r"(?m)^(Compiling lib/|Running Gradle|Running pod install)[^\n]+\n?").unwrap());
static PUB_FETCH_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"(?m)^(Downloading|Fetching|Resolving|Precompiling) [^\n]+\.\.\.\n?").unwrap()
});
static TOOL_DOWNLOAD_RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?m)^(Downloading|Installing) Flutter (tool|SDK)[^\n]*\n?").unwrap());
pub fn compress_pub(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let s = PUB_FETCH_RE.replace_all(&cleaned, "");
let kept: Vec<&str> = s
.lines()
.filter(|l| {
let t = l.trim();
t.is_empty()
|| t.starts_with("Got dependencies")
|| t.contains("Changed")
|| t.contains("error:")
|| t.starts_with("Because")
|| t.starts_with("Note:")
|| t.starts_with("Dart SDK")
})
.collect();
if kept.is_empty() {
return compactor::collapse_blanks(&s);
}
kept.join("\n")
}
pub fn compress_build(raw: &str, exit_code: i32) -> String {
let cleaned = compactor::normalise(raw);
let s = COMPILE_RE.replace_all(&cleaned, "");
let s = TOOL_DOWNLOAD_RE.replace_all(&s, "");
if exit_code == 0 {
let kept: Vec<&str> = s
.lines()
.filter(|l| {
let t = l.trim();
t.is_empty()
|| t.starts_with("✓")
|| t.starts_with("Building")
|| t.contains("Built ")
|| t.contains("seconds")
|| t.contains("error")
|| t.contains("warning")
})
.collect();
if !kept.is_empty() {
return kept.join("\n");
}
}
s.lines()
.filter(|l| l.contains("error") || l.contains("Error") || !l.trim().is_empty())
.collect::<Vec<_>>()
.join("\n")
}
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("All tests passed")
|| l.contains("passed")
|| l.contains("+") && l.contains(":") })
.collect();
if !kept.is_empty() {
return kept.join("\n");
}
}
cleaned
.lines()
.filter(|l| {
let t = l.trim();
!t.starts_with('+') || t.contains("FAILED") || t.contains("ERROR")
})
.collect::<Vec<_>>()
.join("\n")
}
pub fn compress_flutter(subcmd: &str, raw: &str, exit_code: i32) -> String {
let sub = subcmd.trim();
match sub {
"pub" => compress_pub(raw),
"build" | "run" | "install" => compress_build(raw, exit_code),
"test" => compress_test(raw, exit_code),
"analyze" => {
let cleaned = compactor::normalise(raw);
cleaned
.lines()
.filter(|l| {
l.contains("error")
|| l.contains("warning")
|| l.contains("info")
|| l.contains("issues found")
})
.collect::<Vec<_>>()
.join("\n")
}
_ => compactor::normalise(raw),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pub_strips_fetch_lines() {
let raw = "Resolving dependencies...\nFetching foo 1.0.0...\nGot dependencies!\n";
let out = compress_pub(raw);
assert!(!out.contains("Fetching foo"), "{out}");
assert!(out.contains("Got dependencies"), "{out}");
}
#[test]
fn build_success_keeps_built_line() {
let raw = "Compiling lib/main.dart\n✓ Built build/app/outputs/flutter-apk/app-release.apk (12.4MB)\n";
let out = compress_build(raw, 0);
assert!(out.contains("Built build/"), "{out}");
assert!(!out.contains("Compiling lib/"), "{out}");
}
}