Skip to main content

lean_ctx/core/patterns/
mod.rs

1pub 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}