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