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