Skip to main content

lean_ctx_patterns/
mix.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("deps.get") || cmd.contains("deps.compile") {
13        return Some(compress_deps(trimmed));
14    }
15    if cmd.contains("compile") || cmd.contains("build") {
16        return Some(compress_compile(trimmed));
17    }
18    if cmd.contains("format") || cmd.contains("fmt") {
19        return Some(compress_format(trimmed));
20    }
21    if cmd.contains("credo") || cmd.contains("dialyzer") {
22        return Some(compress_lint(trimmed));
23    }
24
25    Some(compact_lines(trimmed, 15))
26}
27
28fn compress_test(output: &str) -> String {
29    let summary = output
30        .lines()
31        .rev()
32        .find(|l| l.contains("test") && (l.contains("passed") || l.contains("failure")));
33
34    if let Some(s) = summary {
35        let mut result = format!("mix test: {}", s.trim());
36        let failures: Vec<&str> = output
37            .lines()
38            .filter(|l| {
39                l.trim().starts_with("1)")
40                    || l.trim().starts_with("2)")
41                    || l.trim().starts_with("3)")
42            })
43            .collect();
44        for f in failures.iter().take(5) {
45            result.push_str(&format!("\n  {}", f.trim()));
46        }
47        return result;
48    }
49    compact_lines(output, 10)
50}
51
52fn compress_deps(output: &str) -> String {
53    let mut resolved = 0u32;
54    let mut compiled = 0u32;
55
56    for line in output.lines() {
57        if line.contains("Resolving") || line.contains("resolving") {
58            resolved += 1;
59        }
60        if line.contains("Compiling") || line.contains("compiling") {
61            compiled += 1;
62        }
63    }
64
65    if resolved == 0 && compiled == 0 {
66        return compact_lines(output, 5);
67    }
68    format!("deps: {resolved} resolved, {compiled} compiled")
69}
70
71fn compress_compile(output: &str) -> String {
72    let mut compiled = 0u32;
73    let mut warnings = 0u32;
74    let mut errors = Vec::new();
75
76    for line in output.lines() {
77        let trimmed = line.trim();
78        if trimmed.starts_with("Compiling") || trimmed.starts_with("Compiled") {
79            compiled += 1;
80        }
81        if trimmed.contains("warning:") {
82            warnings += 1;
83        }
84        if trimmed.contains("error") && trimmed.contains("**") {
85            errors.push(trimmed.to_string());
86        }
87    }
88
89    if !errors.is_empty() {
90        let mut result = format!("{} errors", errors.len());
91        for e in errors.iter().take(10) {
92            result.push_str(&format!("\n  {e}"));
93        }
94        return result;
95    }
96
97    let mut result = format!("{compiled} compiled");
98    if warnings > 0 {
99        result.push_str(&format!(", {warnings} warnings"));
100    }
101    result
102}
103
104fn compress_format(output: &str) -> String {
105    let files: Vec<&str> = output.lines().filter(|l| !l.trim().is_empty()).collect();
106    if files.is_empty() {
107        return "ok (formatted)".to_string();
108    }
109    format!("{} files", files.len())
110}
111
112fn compress_lint(output: &str) -> String {
113    let issues: Vec<&str> = output
114        .lines()
115        .filter(|l| {
116            let t = l.trim();
117            t.contains("┃") || t.starts_with("warning:") || t.starts_with("error:")
118        })
119        .collect();
120
121    if issues.is_empty() {
122        if output.contains("no issues") || output.contains("Analysis finished") {
123            return "clean".to_string();
124        }
125        return compact_lines(output, 10);
126    }
127    format!(
128        "{} issues:\n{}",
129        issues.len(),
130        issues
131            .iter()
132            .take(10)
133            .map(|i| format!("  {}", i.trim()))
134            .collect::<Vec<_>>()
135            .join("\n")
136    )
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}