lean_ctx/core/patterns/
mod.rs1pub mod ansible;
2pub mod aws;
3pub mod bazel;
4pub mod bun;
5pub mod cargo;
6pub mod cmake;
7pub mod composer;
8pub mod curl;
9pub mod deno;
10pub mod deps_cmd;
11pub mod docker;
12pub mod dotnet;
13pub mod env_filter;
14pub mod eslint;
15pub mod find;
16pub mod flutter;
17pub mod gh;
18pub mod git;
19pub mod golang;
20pub mod grep;
21pub mod helm;
22pub mod json_schema;
23pub mod kubectl;
24pub mod log_dedup;
25pub mod ls;
26pub mod make;
27pub mod maven;
28pub mod mix;
29pub mod mypy;
30pub mod mysql;
31pub mod next_build;
32pub mod npm;
33pub mod pip;
34pub mod playwright;
35pub mod pnpm;
36pub mod poetry;
37pub mod prettier;
38pub mod prisma;
39pub mod psql;
40pub mod ruby;
41pub mod ruff;
42pub mod swift;
43pub mod systemd;
44pub mod terraform;
45pub mod test;
46pub mod typescript;
47pub mod wget;
48pub mod zig;
49
50pub fn compress_output(command: &str, output: &str) -> Option<String> {
51 let specific = try_specific_pattern(command, output);
52 if specific.is_some() {
53 return specific;
54 }
55
56 if let Some(r) = json_schema::compress(output) {
57 return Some(r);
58 }
59
60 if let Some(r) = log_dedup::compress(output) {
61 return Some(r);
62 }
63
64 if let Some(r) = test::compress(output) {
65 return Some(r);
66 }
67
68 None
69}
70
71fn try_specific_pattern(cmd: &str, output: &str) -> Option<String> {
72 let cl = cmd.to_ascii_lowercase();
73 let c = cl.as_str();
74
75 if c.starts_with("git ") {
76 return git::compress(c, output);
77 }
78 if c.starts_with("gh ") {
79 return gh::compress(c, output);
80 }
81 if c == "terraform" || c.starts_with("terraform ") {
82 return terraform::compress(c, output);
83 }
84 if c == "make" || c.starts_with("make ") {
85 return make::compress(c, output);
86 }
87 if c.starts_with("mvn ")
88 || c.starts_with("./mvnw ")
89 || c.starts_with("mvnw ")
90 || c.starts_with("gradle ")
91 || c.starts_with("./gradlew ")
92 || c.starts_with("gradlew ")
93 {
94 return maven::compress(c, output);
95 }
96 if c.starts_with("kubectl ") || c.starts_with("k ") {
97 return kubectl::compress(c, output);
98 }
99 if c.starts_with("helm ") {
100 return helm::compress(c, output);
101 }
102 if c.starts_with("pnpm ") {
103 return pnpm::compress(c, output);
104 }
105 if c.starts_with("bun ") {
106 return bun::compress(c, output);
107 }
108 if c.starts_with("deno ") {
109 return deno::compress(c, output);
110 }
111 if c.starts_with("npm ") || c.starts_with("yarn ") {
112 return npm::compress(c, output);
113 }
114 if c.starts_with("cargo ") {
115 return cargo::compress(c, output);
116 }
117 if c.starts_with("docker ") || c.starts_with("docker-compose ") {
118 return docker::compress(c, output);
119 }
120 if c.starts_with("pip ") || c.starts_with("pip3 ") || c.starts_with("python -m pip") {
121 return pip::compress(c, output);
122 }
123 if c.starts_with("mypy") || c.starts_with("python -m mypy") || c.starts_with("dmypy ") {
124 return mypy::compress(c, output);
125 }
126 if c.starts_with("pytest") || c.starts_with("python -m pytest") {
127 return test::compress(output);
128 }
129 if c.starts_with("ruff ") {
130 return ruff::compress(c, output);
131 }
132 if c.starts_with("eslint")
133 || c.starts_with("npx eslint")
134 || c.starts_with("biome ")
135 || c.starts_with("stylelint")
136 {
137 return eslint::compress(c, output);
138 }
139 if c.starts_with("prettier") || c.starts_with("npx prettier") {
140 return prettier::compress(output);
141 }
142 if c.starts_with("go ") || c.starts_with("golangci-lint") || c.starts_with("golint") {
143 return golang::compress(c, output);
144 }
145 if c.starts_with("playwright")
146 || c.starts_with("npx playwright")
147 || c.starts_with("cypress")
148 || c.starts_with("npx cypress")
149 {
150 return playwright::compress(c, output);
151 }
152 if c.starts_with("vitest") || c.starts_with("npx vitest") || c.starts_with("pnpm vitest") {
153 return test::compress(output);
154 }
155 if c.starts_with("next ")
156 || c.starts_with("npx next")
157 || c.starts_with("vite ")
158 || c.starts_with("npx vite")
159 {
160 return next_build::compress(c, output);
161 }
162 if c.starts_with("tsc") || c.contains("typescript") {
163 return typescript::compress(output);
164 }
165 if c.starts_with("rubocop")
166 || c.starts_with("bundle ")
167 || c.starts_with("rake ")
168 || c.starts_with("rails test")
169 || c.starts_with("rspec")
170 {
171 return ruby::compress(c, output);
172 }
173 if c.starts_with("grep ") || c.starts_with("rg ") {
174 return grep::compress(output);
175 }
176 if c.starts_with("find ") {
177 return find::compress(output);
178 }
179 if c.starts_with("ls ") || c == "ls" {
180 return ls::compress(output);
181 }
182 if c.starts_with("curl ") {
183 return curl::compress(output);
184 }
185 if c.starts_with("wget ") {
186 return wget::compress(output);
187 }
188 if c == "env" || c.starts_with("env ") || c.starts_with("printenv") {
189 return env_filter::compress(output);
190 }
191 if c.starts_with("dotnet ") {
192 return dotnet::compress(c, output);
193 }
194 if c.starts_with("flutter ")
195 || (c.starts_with("dart ") && (c.contains(" analyze") || c.ends_with(" analyze")))
196 {
197 return flutter::compress(c, output);
198 }
199 if c.starts_with("poetry ")
200 || c.starts_with("uv sync")
201 || (c.starts_with("uv ") && c.contains("pip install"))
202 {
203 return poetry::compress(c, output);
204 }
205 if c.starts_with("aws ") {
206 return aws::compress(c, output);
207 }
208 if c.starts_with("psql ") || c.starts_with("pg_") {
209 return psql::compress(c, output);
210 }
211 if c.starts_with("mysql ") || c.starts_with("mariadb ") {
212 return mysql::compress(c, output);
213 }
214 if c.starts_with("prisma ") || c.starts_with("npx prisma") {
215 return prisma::compress(c, output);
216 }
217 if c.starts_with("swift ") {
218 return swift::compress(c, output);
219 }
220 if c.starts_with("zig ") {
221 return zig::compress(c, output);
222 }
223 if c.starts_with("cmake ") || c.starts_with("ctest") {
224 return cmake::compress(c, output);
225 }
226 if c.starts_with("ansible") || c.starts_with("ansible-playbook") {
227 return ansible::compress(c, output);
228 }
229 if c.starts_with("composer ") {
230 return composer::compress(c, output);
231 }
232 if c.starts_with("mix ") || c.starts_with("iex ") {
233 return mix::compress(c, output);
234 }
235 if c.starts_with("bazel ") || c.starts_with("blaze ") {
236 return bazel::compress(c, output);
237 }
238 if c.starts_with("systemctl ") || c.starts_with("journalctl") {
239 return systemd::compress(c, output);
240 }
241
242 None
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn routes_git_commands() {
251 let output = "On branch main\nnothing to commit";
252 assert!(compress_output("git status", output).is_some());
253 }
254
255 #[test]
256 fn routes_cargo_commands() {
257 let output = " Compiling lean-ctx v2.1.1\n Finished `release` profile [optimized] target(s) in 30.5s";
258 assert!(compress_output("cargo build --release", output).is_some());
259 }
260
261 #[test]
262 fn routes_npm_commands() {
263 let output = "added 150 packages, and audited 151 packages in 5s\n\n25 packages are looking for funding\n run `npm fund` for details\n\nfound 0 vulnerabilities";
264 assert!(compress_output("npm install", output).is_some());
265 }
266
267 #[test]
268 fn routes_docker_commands() {
269 let output = "CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES";
270 assert!(compress_output("docker ps", output).is_some());
271 }
272
273 #[test]
274 fn routes_mypy_commands() {
275 let output = "src/main.py:10: error: Missing return [return]\nFound 1 error in 1 file (checked 3 source files)";
276 assert!(compress_output("mypy .", output).is_some());
277 assert!(compress_output("python -m mypy src/", output).is_some());
278 }
279
280 #[test]
281 fn routes_pytest_commands() {
282 let output = "===== test session starts =====\ncollected 5 items\ntest_main.py ..... [100%]\n===== 5 passed in 0.5s =====";
283 assert!(compress_output("pytest", output).is_some());
284 assert!(compress_output("python -m pytest tests/", output).is_some());
285 }
286
287 #[test]
288 fn unknown_command_returns_none() {
289 assert!(compress_output("some-unknown-tool --version", "v1.0").is_none());
290 }
291
292 #[test]
293 fn case_insensitive_routing() {
294 let output = "On branch main\nnothing to commit";
295 assert!(compress_output("Git Status", output).is_some());
296 assert!(compress_output("GIT STATUS", output).is_some());
297 }
298}