lean_ctx/core/patterns/
cmake.rs1pub 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}