use forge::signal::compactor;
use once_cell::sync::Lazy;
use regex::Regex;
static TABLE_SEPARATOR_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^[-─]+\n?").unwrap());
static RUN_META_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(
r"\b(ubuntu-latest|ubuntu-22\.04|ubuntu-20\.04|macos-latest|macos-14|windows-latest)\b",
)
.unwrap()
});
static JSON_BLANK_RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r#"(?m)^\s+"[a-z_]+": null,?\n?"#).unwrap());
pub fn compress_pr_list(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let s = TABLE_SEPARATOR_RE.replace_all(&cleaned, "");
let kept: Vec<&str> = s
.lines()
.filter(|l| !l.trim().is_empty())
.take(30)
.collect();
kept.join("\n")
}
pub fn compress_pr_view(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let lines: Vec<&str> = cleaned.lines().collect();
if lines.len() > 60 {
let head = &lines[..20];
let tail = &lines[lines.len() - 10..];
let omitted = lines.len() - 30;
return format!(
"{}\n... [{} more lines omitted] ...\n{}",
head.join("\n"),
omitted,
tail.join("\n")
);
}
cleaned
}
pub fn compress_pr_checks(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
cleaned
.lines()
.filter(|l| !l.trim().is_empty())
.collect::<Vec<_>>()
.join("\n")
}
pub fn compress_issue_list(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let s = TABLE_SEPARATOR_RE.replace_all(&cleaned, "");
let kept: Vec<&str> = s
.lines()
.filter(|l| !l.trim().is_empty())
.take(30)
.collect();
kept.join("\n")
}
pub fn compress_run_list(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let s = RUN_META_RE.replace_all(&cleaned, "");
let kept: Vec<&str> = s
.lines()
.filter(|l| !l.trim().is_empty())
.take(20)
.collect();
kept.join("\n")
}
pub fn compress_run_view(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let lines: Vec<&str> = cleaned.lines().collect();
if lines.len() > 50 {
let omitted = lines.len() - 50;
return format!(
"{}\n... [{} more log lines] ...",
lines[..50].join("\n"),
omitted
);
}
cleaned
}
pub fn compress_api(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let s = JSON_BLANK_RE.replace_all(&cleaned, "");
let lines: Vec<&str> = s.lines().collect();
if lines.len() > 60 {
return format!(
"{}\n... [{} more lines — use --jq to filter] ...",
lines[..60].join("\n"),
lines.len() - 60
);
}
s.into_owned()
}
pub fn compress_repo(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let lines: Vec<&str> = cleaned.lines().collect();
if lines.len() > 30 {
return lines[..30].join("\n");
}
cleaned
}
pub fn compress_gh(subcmd: &str, raw: &str) -> String {
let sub = subcmd.trim();
if sub.starts_with("pr list") || sub == "pr" {
return compress_pr_list(raw);
}
if sub.starts_with("pr view") {
return compress_pr_view(raw);
}
if sub.starts_with("pr checks") {
return compress_pr_checks(raw);
}
if sub.starts_with("issue list") || sub == "issue" {
return compress_issue_list(raw);
}
if sub.starts_with("run list") {
return compress_run_list(raw);
}
if sub.starts_with("run view") || sub.starts_with("run watch") {
return compress_run_view(raw);
}
if sub.starts_with("api") {
return compress_api(raw);
}
if sub.starts_with("repo") {
return compress_repo(raw);
}
let cleaned = compactor::normalise(raw);
let lines: Vec<&str> = cleaned.lines().filter(|l| !l.trim().is_empty()).collect();
if lines.len() > 50 {
return format!(
"{}\n... [{} more lines] ...",
lines[..50].join("\n"),
lines.len() - 50
);
}
cleaned
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pr_list_keeps_entries() {
let raw =
"#42 Fix the bug OPEN user1\n#41 Update readme MERGED user2\n";
let out = compress_pr_list(raw);
assert!(out.contains("#42"), "{out}");
assert!(out.contains("#41"), "{out}");
}
#[test]
fn pr_list_truncates_at_30() {
let raw = (0..40)
.map(|i| format!("#{i} PR {i} OPEN user\n"))
.collect::<String>();
let out = compress_pr_list(raw.as_str());
assert!(!out.contains("#39"), "{out}");
}
#[test]
fn run_list_strips_runner_os() {
let raw = "completed success CI main ubuntu-latest 2m\n";
let out = compress_run_list(raw);
assert!(!out.contains("ubuntu-latest"), "{out}");
}
#[test]
fn api_truncates_large_response() {
let raw = (0..80)
.map(|i| format!(" \"field{i}\": \"value{i}\",\n"))
.collect::<String>();
let out = compress_api(&raw);
assert!(out.contains("more lines"), "{out}");
}
}