Skip to main content

lean_ctx/core/patterns/
cmake.rs

1pub fn compress(cmd: &str, output: &str) -> Option<String> {
2    let trimmed = output.trim();
3    if trimmed.is_empty() {
4        return Some("ok".to_string());
5    }
6
7    if cmd.contains("--build") || cmd.contains("make") {
8        return Some(compress_build(trimmed));
9    }
10    if cmd.contains("ctest") || cmd.contains("test") {
11        return Some(compress_test(trimmed));
12    }
13
14    Some(compress_configure(trimmed))
15}
16
17fn compress_configure(output: &str) -> String {
18    let mut found_generators = Vec::new();
19    let mut found_compilers = Vec::new();
20    let mut warnings = 0u32;
21    let mut errors = Vec::new();
22
23    for line in output.lines() {
24        let trimmed = line.trim();
25        if trimmed.starts_with("-- The") && trimmed.contains("compiler") {
26            found_compilers.push(trimmed.to_string());
27        }
28        if trimmed.starts_with("-- Generating")
29            || (trimmed.starts_with("--") && trimmed.contains("generator"))
30        {
31            found_generators.push(trimmed.to_string());
32        }
33        if trimmed.contains("CMake Warning") || trimmed.starts_with("WARNING:") {
34            warnings += 1;
35        }
36        if trimmed.contains("CMake Error") || trimmed.starts_with("ERROR:") {
37            errors.push(trimmed.to_string());
38        }
39    }
40
41    if !errors.is_empty() {
42        let mut result = format!("{} errors:", errors.len());
43        for e in errors.iter().take(10) {
44            result.push_str(&format!("\n  {e}"));
45        }
46        return result;
47    }
48
49    let success =
50        output.contains("Configuring done") || output.contains("Build files have been written");
51    let mut result = if success {
52        "CMake configured ok".to_string()
53    } else {
54        "CMake configure:".to_string()
55    };
56    if warnings > 0 {
57        result.push_str(&format!(" ({warnings} warnings)"));
58    }
59    if !found_compilers.is_empty() {
60        result.push_str(&format!("\n  compilers: {}", found_compilers.len()));
61    }
62    result
63}
64
65fn compress_build(output: &str) -> String {
66    let lines: Vec<&str> = output.lines().collect();
67    let errors: Vec<&&str> = lines
68        .iter()
69        .filter(|l| l.contains("error:") || l.contains("Error:"))
70        .collect();
71    let warnings: Vec<&&str> = lines
72        .iter()
73        .filter(|l| l.contains("warning:") || l.contains("Warning:"))
74        .collect();
75
76    let progress_lines: Vec<&&str> = lines
77        .iter()
78        .filter(|l| l.trim().starts_with('[') && l.contains(']'))
79        .collect();
80
81    if !errors.is_empty() {
82        let mut result = format!("{} errors", errors.len());
83        if !warnings.is_empty() {
84            result.push_str(&format!(", {} warnings", warnings.len()));
85        }
86        for e in errors.iter().take(10) {
87            result.push_str(&format!("\n  {}", e.trim()));
88        }
89        return result;
90    }
91
92    let mut result = format!("Build ok ({} steps", progress_lines.len().max(lines.len()));
93    if !warnings.is_empty() {
94        result.push_str(&format!(", {} warnings", warnings.len()));
95    }
96    result.push(')');
97    result
98}
99
100fn compress_test(output: &str) -> String {
101    let mut passed = 0u32;
102    let mut failed = 0u32;
103    let mut failures = Vec::new();
104
105    for line in output.lines() {
106        let trimmed = line.trim();
107        if trimmed.contains("Passed") {
108            for word in trimmed.split_whitespace() {
109                if let Ok(n) = word.parse::<u32>() {
110                    passed = n;
111                    break;
112                }
113            }
114        }
115        if trimmed.contains("Failed") && !trimmed.starts_with("The following") {
116            for word in trimmed.split_whitespace() {
117                if let Ok(n) = word.parse::<u32>() {
118                    failed = n;
119                    break;
120                }
121            }
122        }
123        if trimmed.starts_with("Failed")
124            || (trimmed.contains("***Failed") || trimmed.contains("***Exception"))
125        {
126            failures.push(trimmed.to_string());
127        }
128    }
129
130    let summary_line = output
131        .lines()
132        .rev()
133        .find(|l| l.contains("tests passed") || l.contains("% tests passed"));
134    if let Some(summary) = summary_line {
135        let mut result = format!("ctest: {}", summary.trim());
136        for f in failures.iter().take(5) {
137            result.push_str(&format!("\n  {f}"));
138        }
139        return result;
140    }
141
142    if passed == 0 && failed == 0 {
143        return compact_lines(output, 10);
144    }
145
146    let mut result = format!("ctest: {passed} passed");
147    if failed > 0 {
148        result.push_str(&format!(", {failed} failed"));
149    }
150    for f in failures.iter().take(5) {
151        result.push_str(&format!("\n  {f}"));
152    }
153    result
154}
155
156fn compact_lines(text: &str, max: usize) -> String {
157    let lines: Vec<&str> = text.lines().filter(|l| !l.trim().is_empty()).collect();
158    if lines.len() <= max {
159        return lines.join("\n");
160    }
161    format!(
162        "{}\n... ({} more lines)",
163        lines[..max].join("\n"),
164        lines.len() - max
165    )
166}