Skip to main content

lean_ctx_patterns/
bun.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use crate::ansi::strip_ansi;
4
5pub fn compress(cmd: &str, output: &str) -> Option<String> {
6    let trimmed = output.trim();
7    if trimmed.is_empty() {
8        return Some("ok".to_string());
9    }
10
11    if cmd.contains("test") {
12        return Some(compress_test(trimmed));
13    }
14    if cmd.contains("install") || cmd.contains("add") || cmd.contains("remove") {
15        return Some(compress_install(trimmed));
16    }
17    if cmd.contains("build") || cmd.contains("run") {
18        return Some(compress_build(trimmed));
19    }
20
21    Some(compact_lines(trimmed, 15))
22}
23
24fn compress_test(output: &str) -> String {
25    let mut passed = 0u32;
26    let mut failed = 0u32;
27    let mut skipped = 0u32;
28    let mut failures = Vec::new();
29    let mut time = String::new();
30
31    for line in output.lines() {
32        let trimmed = line.trim();
33        let plain = strip_ansi(trimmed);
34        if plain.contains("pass") && (plain.contains("tests") || plain.contains("test")) {
35            for word in plain.split_whitespace() {
36                if let Ok(n) = word.parse::<u32>() {
37                    passed = n;
38                    break;
39                }
40            }
41        }
42        if plain.contains("fail") && !plain.starts_with("FAIL") {
43            for word in plain.split_whitespace() {
44                if let Ok(n) = word.parse::<u32>() {
45                    failed = n;
46                    break;
47                }
48            }
49        }
50        if plain.contains("skip") {
51            for word in plain.split_whitespace() {
52                if let Ok(n) = word.parse::<u32>() {
53                    skipped = n;
54                    break;
55                }
56            }
57        }
58        if plain.starts_with("FAIL") || plain.starts_with("✗") || plain.starts_with("×") {
59            failures.push(plain.clone());
60        }
61        if (plain.contains("Ran") || plain.contains("Done"))
62            && (plain.contains("ms") || plain.contains('s'))
63        {
64            time.clone_from(&plain);
65        }
66    }
67
68    if passed == 0 && failed == 0 {
69        return compact_lines(output, 10);
70    }
71
72    let mut result = format!("bun test: {passed} passed");
73    if failed > 0 {
74        result.push_str(&format!(", {failed} failed"));
75    }
76    if skipped > 0 {
77        result.push_str(&format!(", {skipped} skipped"));
78    }
79    if !time.is_empty() {
80        result.push_str(&format!(" ({time})"));
81    }
82    for f in failures.iter().take(5) {
83        result.push_str(&format!("\n  {f}"));
84    }
85    result
86}
87
88fn compress_install(output: &str) -> String {
89    let mut installed = 0u32;
90    let mut removed = 0u32;
91    let mut time = String::new();
92
93    for line in output.lines() {
94        let plain = strip_ansi(line.trim());
95        if plain.contains("installed") || plain.starts_with('+') {
96            installed += 1;
97        }
98        if plain.contains("removed") || plain.starts_with('-') {
99            removed += 1;
100        }
101        if plain.contains("done") && (plain.contains("ms") || plain.contains('s')) {
102            time.clone_from(&plain);
103        }
104    }
105
106    let mut parts = Vec::new();
107    if installed > 0 {
108        parts.push(format!("{installed} installed"));
109    }
110    if removed > 0 {
111        parts.push(format!("{removed} removed"));
112    }
113    if !time.is_empty() {
114        parts.push(time);
115    }
116
117    if parts.is_empty() {
118        return compact_lines(output, 5);
119    }
120    format!("bun: {}", parts.join(", "))
121}
122
123fn compress_build(output: &str) -> String {
124    let lines: Vec<&str> = output.lines().collect();
125    let errors: Vec<&&str> = lines
126        .iter()
127        .filter(|l| l.contains("error") || l.contains("Error"))
128        .collect();
129    if !errors.is_empty() {
130        let mut result = format!("{} errors:", errors.len());
131        for e in errors.iter().take(10) {
132            result.push_str(&format!("\n  {}", e.trim()));
133        }
134        return result;
135    }
136    compact_lines(output, 10)
137}
138
139fn compact_lines(text: &str, max: usize) -> String {
140    let lines: Vec<&str> = text.lines().filter(|l| !l.trim().is_empty()).collect();
141    if lines.len() <= max {
142        return lines.join("\n");
143    }
144    format!(
145        "{}\n{}",
146        lines[..max].join("\n"),
147        super::elision_marker(lines.len() - max)
148    )
149}