Skip to main content

lean_ctx_patterns/
bazel.rs

1// SPDX-License-Identifier: Apache-2.0
2
3pub fn compress(cmd: &str, output: &str) -> Option<String> {
4    let trimmed = output.trim();
5    if trimmed.is_empty() {
6        return Some("ok".to_string());
7    }
8
9    if cmd.contains("test") {
10        return Some(compress_test(trimmed));
11    }
12    if cmd.contains("build") {
13        return Some(compress_build(trimmed));
14    }
15    if cmd.contains("query") {
16        return Some(compress_query(trimmed));
17    }
18
19    Some(compact_lines(trimmed, 15))
20}
21
22fn compress_test(output: &str) -> String {
23    let mut passed = 0u32;
24    let mut failed = 0u32;
25    let mut failures = Vec::new();
26
27    for line in output.lines() {
28        let trimmed = line.trim();
29        if trimmed.contains("PASSED") {
30            passed += 1;
31        }
32        if trimmed.contains("FAILED") {
33            failed += 1;
34            failures.push(trimmed.to_string());
35        }
36    }
37
38    let summary = output
39        .lines()
40        .find(|l| l.contains("executed") || l.contains("test(s)"));
41
42    if passed == 0 && failed == 0 {
43        if let Some(s) = summary {
44            return format!("bazel test: {}", s.trim());
45        }
46        return compact_lines(output, 10);
47    }
48
49    let mut result = format!("bazel test: {passed} passed");
50    if failed > 0 {
51        result.push_str(&format!(", {failed} failed"));
52    }
53    for f in failures.iter().take(5) {
54        result.push_str(&format!("\n  {f}"));
55    }
56    result
57}
58
59fn compress_build(output: &str) -> String {
60    let mut targets = 0u32;
61    let mut errors = Vec::new();
62
63    for line in output.lines() {
64        let trimmed = line.trim();
65        if (trimmed.contains("up-to-date") || trimmed.contains("Build completed"))
66            && let Some(n) = trimmed
67                .split_whitespace()
68                .find_map(|w| w.parse::<u32>().ok())
69        {
70            targets = n;
71        }
72        if trimmed.starts_with("ERROR:") || trimmed.starts_with("error:") {
73            errors.push(trimmed.to_string());
74        }
75    }
76
77    if !errors.is_empty() {
78        let mut result = format!("{} errors:", errors.len());
79        for e in errors.iter().take(10) {
80            result.push_str(&format!("\n  {e}"));
81        }
82        return result;
83    }
84
85    let info_line = output
86        .lines()
87        .rev()
88        .find(|l| l.contains("INFO: Build completed") || l.contains("up-to-date"));
89    if let Some(info) = info_line {
90        return info.trim().to_string();
91    }
92
93    format!("ok ({targets} targets)")
94}
95
96fn compress_query(output: &str) -> String {
97    let targets: Vec<&str> = output.lines().filter(|l| !l.trim().is_empty()).collect();
98    if targets.len() <= 20 {
99        return targets.join("\n");
100    }
101    format!(
102        "{} targets:\n{}\n... ({} more)",
103        targets.len(),
104        targets[..15].join("\n"),
105        targets.len() - 15
106    )
107}
108
109fn compact_lines(text: &str, max: usize) -> String {
110    let lines: Vec<&str> = text.lines().filter(|l| !l.trim().is_empty()).collect();
111    if lines.len() <= max {
112        return lines.join("\n");
113    }
114    format!(
115        "{}\n{}",
116        lines[..max].join("\n"),
117        super::elision_marker(lines.len() - max)
118    )
119}