Skip to main content

lean_ctx/core/patterns/
mod.rs

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