lean_ctx/core/patterns/
maven.rs1use regex::Regex;
2use std::sync::OnceLock;
3
4static MAVEN_DOWNLOAD_RE: OnceLock<Regex> = OnceLock::new();
5static MAVEN_PROGRESS_RE: OnceLock<Regex> = OnceLock::new();
6static GRADLE_DOWNLOAD_RE: OnceLock<Regex> = OnceLock::new();
7static GRADLE_PROGRESS_RE: OnceLock<Regex> = OnceLock::new();
8static TESTS_RUN_RE: OnceLock<Regex> = OnceLock::new();
9
10fn maven_download_re() -> &'static Regex {
11 MAVEN_DOWNLOAD_RE
12 .get_or_init(|| Regex::new(r"(?i)\[INFO\]\s+(Downloading|Downloaded)\s+").unwrap())
13}
14
15fn maven_progress_re() -> &'static Regex {
16 MAVEN_PROGRESS_RE.get_or_init(|| Regex::new(r"\[INFO\].*kB\s+\|").unwrap())
17}
18
19fn gradle_download_re() -> &'static Regex {
20 GRADLE_DOWNLOAD_RE
21 .get_or_init(|| Regex::new(r"(?i)^(Downloading|Download)\s+https?://").unwrap())
22}
23
24fn gradle_progress_re() -> &'static Regex {
25 GRADLE_PROGRESS_RE.get_or_init(|| Regex::new(r"^[<>=\s]+$|^[0-9]+%\s+EXECUTING").unwrap())
26}
27
28fn tests_run_re() -> &'static Regex {
29 TESTS_RUN_RE.get_or_init(|| Regex::new(r"Tests run:\s*\d+").unwrap())
30}
31
32fn is_maven_noise(line: &str) -> bool {
33 let t = line.trim_start();
34 if maven_download_re().is_match(t) {
35 return true;
36 }
37 if maven_progress_re().is_match(t) {
38 return true;
39 }
40 if t.contains("Progress (") && t.contains("):") && t.contains('%') {
41 return true;
42 }
43 false
44}
45
46fn is_gradle_noise(line: &str) -> bool {
47 let t = line.trim();
48 if gradle_download_re().is_match(t) {
49 return true;
50 }
51 if gradle_progress_re().is_match(t) {
52 return true;
53 }
54 let tl = t.to_ascii_lowercase();
55 if tl.starts_with("consider enabling configuration cache")
56 || tl.contains("deprecated gradle features were used")
57 || tl.starts_with("you can use '--warning-mode")
58 {
59 return true;
60 }
61 false
62}
63
64fn is_maven_or_gradle_command(command: &str) -> bool {
65 let c = command.trim();
66 let cl = c.to_ascii_lowercase();
67 cl.starts_with("mvn ")
68 || cl.starts_with("./mvnw ")
69 || cl.starts_with("mvnw ")
70 || cl.starts_with("gradle ")
71 || cl.starts_with("./gradlew ")
72 || cl.starts_with("gradlew ")
73}
74
75fn is_gradle_command(command: &str) -> bool {
76 let cl = command.trim().to_ascii_lowercase();
77 cl.starts_with("gradle ") || cl.starts_with("./gradlew ") || cl.starts_with("gradlew ")
78}
79
80pub fn compress(command: &str, output: &str) -> Option<String> {
81 if !is_maven_or_gradle_command(command) {
82 return None;
83 }
84 if is_gradle_command(command) {
85 Some(compress_gradle(output))
86 } else {
87 Some(compress_maven(output))
88 }
89}
90
91fn compress_maven(output: &str) -> String {
92 let mut kept = Vec::new();
93
94 for line in output.lines() {
95 let t = line.trim_end();
96 if t.trim().is_empty() {
97 continue;
98 }
99 if is_maven_noise(t) {
100 continue;
101 }
102
103 let tl = t.to_ascii_lowercase();
104 if tl.contains("[error]")
105 || tl.contains("[fatal]")
106 || tl.contains("build failure")
107 || tl.contains("build success")
108 || tl.contains("failure!")
109 || tl.contains("tests run:")
110 || tl.contains("failures:")
111 || tl.contains("errors:")
112 || tl.contains("skipped:")
113 || tests_run_re().is_match(t)
114 {
115 kept.push(t.trim().to_string());
116 continue;
117 }
118
119 if tl.contains("[warning]") {
120 kept.push(t.trim().to_string());
121 }
122 }
123
124 if kept.is_empty() {
125 "mvn (no build/test lines kept)".to_string()
126 } else {
127 kept.join("\n")
128 }
129}
130
131fn compress_gradle(output: &str) -> String {
132 let mut kept = Vec::new();
133 let mut task_lines = Vec::new();
134
135 for line in output.lines() {
136 let t = line.trim_end();
137 if t.trim().is_empty() {
138 continue;
139 }
140 if is_gradle_noise(t) {
141 continue;
142 }
143
144 let tl = t.to_ascii_lowercase();
145 if tl.starts_with("> task ") {
146 if tl.contains("failed")
147 || tl.contains("failure")
148 || tl.contains("skipped")
149 || tl.contains("no-source")
150 {
151 task_lines.push(t.trim().to_string());
152 }
153 continue;
154 }
155
156 if tl.contains("actionable tasks:") {
157 kept.push(t.trim().to_string());
158 continue;
159 }
160
161 if tl.contains("build successful")
162 || tl.contains("build failed")
163 || tl.starts_with("failure:")
164 || tl.contains("what went wrong:")
165 || tl.contains("execution failed for task")
166 || tl.contains("error:")
167 || tl.contains("exception")
168 || tl.contains("tests completed:")
169 || (tl.contains("test ") && (tl.contains("failed") || tl.contains("passed")))
170 || tl.contains("there were failing tests")
171 {
172 kept.push(t.trim().to_string());
173 }
174 }
175
176 if !task_lines.is_empty() {
177 kept.push("tasks:\n".to_string() + &task_lines.join("\n"));
178 }
179
180 if kept.is_empty() {
181 "gradle (no summary kept)".to_string()
182 } else {
183 kept.join("\n")
184 }
185}