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