pub mod curl;
pub mod env;
pub mod jq;
pub mod mise;
pub mod sysinfo;
pub mod systemd;
pub mod wget;
use forge::signal::compactor;
use once_cell::sync::Lazy;
use regex::Regex;
static FIND_PERM_RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?m)^find: [^\n]*: Permission denied\n?").unwrap());
pub fn compress_ls(raw: &str, max_lines: usize) -> String {
let cleaned = compactor::normalise(raw);
let lines: Vec<&str> = cleaned.lines().collect();
if lines.len() <= max_lines {
return cleaned;
}
format!(
"{}\n... [{} more entries]",
lines[..max_lines].join("\n"),
lines.len() - max_lines
)
}
pub fn compress_find(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let s = FIND_PERM_RE.replace_all(&cleaned, "");
let lines: Vec<&str> = s.lines().filter(|l| !l.trim().is_empty()).collect();
if lines.len() <= 100 {
return lines.join("\n");
}
format!(
"{}\n... [{} more paths]",
lines[..100].join("\n"),
lines.len() - 100
)
}
pub fn compress_grep(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let lines: Vec<&str> = cleaned.lines().filter(|l| !l.trim().is_empty()).collect();
if lines.len() <= 80 {
return lines.join("\n");
}
format!(
"{}\n... [{} more matches]",
lines[..80].join("\n"),
lines.len() - 80
)
}
pub fn log_dedup(raw: &str) -> String {
let lines: Vec<&str> = raw.lines().collect();
let mut out: Vec<String> = Vec::new();
let mut prev: &str = "";
let mut repeat_count: usize = 0;
for line in &lines {
if *line == prev && !line.trim().is_empty() {
repeat_count += 1;
} else {
if repeat_count > 0 {
out.push(format!(" [repeated ×{}]", repeat_count + 1));
repeat_count = 0;
}
out.push(line.to_string());
prev = line;
}
}
if repeat_count > 0 {
out.push(format!(" [repeated ×{}]", repeat_count + 1));
}
if out.len() > 200 {
let skipped = out.len() - 200;
let tail = out[out.len() - 200..].to_vec();
return format!(
"... [{} earlier lines omitted] ...\n{}",
skipped,
tail.join("\n")
);
}
out.join("\n")
}
pub fn compress_env(raw: &str) -> String {
env::compress_env(raw)
}
pub fn compress_jq_output(raw: &str) -> String {
jq::compress_jq(raw)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ls_truncates_large_output() {
let raw = (0..300)
.map(|i| format!("file{i}.txt"))
.collect::<Vec<_>>()
.join("\n");
let out = compress_ls(&raw, 200);
assert!(out.contains("more entries"), "{out}");
}
#[test]
fn find_strips_permission_denied() {
let raw = "find: /root: Permission denied\n./src/main.rs\n./src/lib.rs\n";
let out = compress_find(raw);
assert!(!out.contains("Permission denied"), "{out}");
assert!(out.contains("main.rs"), "{out}");
}
#[test]
fn log_dedup_collapses_repeated_lines() {
let raw = "line A\nline B\nline B\nline B\nline C\n";
let out = log_dedup(raw);
assert!(out.contains("repeated"), "{out}");
assert!(!out.contains("line B\nline B"), "{out}");
}
#[test]
fn log_dedup_tails_very_long_logs() {
let raw = (0..300)
.map(|i| format!("log line {i}"))
.collect::<Vec<_>>()
.join("\n");
let out = log_dedup(&raw);
assert!(out.contains("earlier lines omitted"), "{out}");
}
}