use forge::signal::compactor;
use once_cell::sync::Lazy;
use regex::Regex;
static DOWNLOAD_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"(?m)^go: (downloading|extracting|finding module|found) [^\n]+\n?").unwrap()
});
static MODULE_CACHE_RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?m)^go: (added|upgraded|downgraded) [^\n]+\n?").unwrap());
static TEST_PASS_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^--- PASS: [^\n]+\n?").unwrap());
static TEST_SKIP_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^--- SKIP: [^\n]+\n?").unwrap());
static LINK_VERBOSE_RE: Lazy<Regex> = Lazy::new(||
Regex::new(r"(?m)^[#/][^\n]+(\.a|\.o|\.so)[^\n]*\n?").unwrap());
static VET_PROGRESS_RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?m)^# [a-zA-Z0-9/_\-\.]+\n?").unwrap());
pub fn compress_build(raw: &str, exit_code: i32) -> String {
let cleaned = compactor::normalise(raw);
let s = DOWNLOAD_RE.replace_all(&cleaned, "");
if exit_code == 0 {
let s = LINK_VERBOSE_RE.replace_all(&s, "");
return compactor::collapse_blanks(&s);
}
let errors: Vec<&str> = s.lines().filter(|l| !l.is_empty()).collect();
errors.join("\n")
}
pub fn compress_test(raw: &str, exit_code: i32) -> String {
let cleaned = compactor::normalise(raw);
let s = DOWNLOAD_RE.replace_all(&cleaned, "");
if exit_code == 0 {
let summary: Vec<&str> = s
.lines()
.filter(|l| {
l.starts_with("ok ")
|| l.starts_with("FAIL")
|| l.starts_with("?")
|| l.contains("coverage:")
})
.collect();
return summary.join("\n");
}
let s = TEST_PASS_RE.replace_all(&s, "");
let s = TEST_SKIP_RE.replace_all(&s, "");
compactor::collapse_blanks(&s)
}
pub fn compress_mod(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let s = DOWNLOAD_RE.replace_all(&cleaned, "");
compactor::collapse_blanks(&s)
}
pub fn compress_vet(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let s = VET_PROGRESS_RE.replace_all(&cleaned, "");
compactor::collapse_blanks(&s)
}
pub fn compress_generate(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
cleaned
.lines()
.filter(|l| !l.starts_with("//go:generate"))
.collect::<Vec<_>>()
.join("\n")
}
pub fn compress_go(subcmd: &str, raw: &str, exit_code: i32) -> String {
let sub = subcmd.trim();
match sub {
"build" | "run" | "install" => compress_build(raw, exit_code),
"test" => compress_test(raw, exit_code),
"mod" => compress_mod(raw),
"vet" => compress_vet(raw),
"generate" => compress_generate(raw),
_ => {
let cleaned = compactor::normalise(raw);
let s = DOWNLOAD_RE.replace_all(&cleaned, "");
let s = MODULE_CACHE_RE.replace_all(&s, "");
compactor::collapse_blanks(&s)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_strips_download_lines() {
let raw =
"go: downloading github.com/foo/bar v1.2.3\ngo: extracting github.com/foo/bar v1.2.3\n";
let out = compress_build(raw, 0);
assert!(!out.contains("downloading"), "{out}");
}
#[test]
fn test_success_keeps_ok_lines() {
let raw = "--- PASS: TestFoo (0.01s)\n--- PASS: TestBar (0.02s)\nok github.com/foo/pkg 0.034s\n";
let out = compress_test(raw, 0);
assert!(out.contains("ok"), "{out}");
assert!(!out.contains("PASS: TestFoo"), "{out}");
}
#[test]
fn test_failure_keeps_fail_lines() {
let raw = "--- PASS: TestFoo (0.01s)\n--- FAIL: TestBar (0.02s)\n\tmain_test.go:42: expected 1, got 2\nFAIL\n";
let out = compress_test(raw, 1);
assert!(out.contains("FAIL"), "{out}");
assert!(!out.contains("PASS: TestFoo"), "{out}");
}
#[test]
fn mod_strips_downloading() {
let raw =
"go: downloading golang.org/x/sys v0.0.0-20230101\ngo: added golang.org/x/sys v0.0.0\n";
let out = compress_mod(raw);
assert!(!out.contains("downloading"), "{out}");
assert!(out.contains("added"), "{out}");
}
#[test]
fn dispatcher_routes_test() {
let raw = "ok github.com/example/pkg 0.01s\n";
let out = compress_go("test", raw, 0);
assert!(out.contains("ok"));
}
}