Skip to main content

sqz_engine/cmd_formatters/
mod.rs

1mod preprocess;
2mod truncate;
3mod test_output;
4mod git;
5mod cargo;
6mod npm;
7mod docker;
8mod kubectl;
9mod system;
10mod js;
11mod lint;
12mod gh;
13mod python;
14mod go;
15mod cloud;
16mod jvm;
17mod ruby;
18mod xcode;
19mod android;
20mod dotnet;
21#[cfg(test)]
22mod props;
23
24pub fn format_command(cmd: &str, output: &str) -> Option<String> {
25    let cleaned = preprocess::clean(output);
26    let input = if cleaned.len() < output.len() { &cleaned } else { output };
27
28    dispatch(cmd, input)
29}
30
31fn dispatch(cmd: &str, output: &str) -> Option<String> {
32    let mut parts: Vec<&str> = cmd.split_whitespace().collect();
33
34    // `bundle exec rspec ...` / `bin/rails ...` — unwrap the bundler prefix so
35    // the real Ruby tool is dispatched. `bundle install/update` is left intact.
36    if parts.first().map(|s| s.rsplit('/').next().unwrap_or(s)) == Some("bundle")
37        && parts.get(1) == Some(&"exec")
38    {
39        parts.drain(0..2);
40    }
41
42    let base = parts.first().map(|s| s.rsplit('/').next().unwrap_or(s)).unwrap_or("");
43
44    match base {
45        // Version control
46        "git" => git::format_git(parts.get(1).copied(), output),
47        "gh" => gh::format_gh(parts.get(1).copied(), output),
48
49        // Rust
50        "cargo" => cargo::format_cargo(parts.get(1).copied(), output),
51
52        // JavaScript/TypeScript
53        "npm" | "npx" => npm::format_npm(parts.get(1).copied(), output),
54        "pnpm" => npm::format_pnpm(parts.get(1).copied(), output),
55        "yarn" | "bun" => npm::format_npm(parts.get(1).copied(), output),
56        "tsc" => Some(js::format_tsc(output)),
57        "eslint" | "biome" => Some(lint::format_lint(output)),
58
59        // Python
60        "pytest" => Some(python::format_pytest(output)),
61        "python" | "python3" if cmd.contains("pytest") || cmd.contains("-m pytest") => {
62            Some(python::format_pytest(output))
63        }
64        "ruff" => python::format_python(cmd, parts.get(1).copied(), output),
65        "mypy" => python::format_python(cmd, None, output),
66        "pip" | "pip3" => python::format_python(cmd, parts.get(1).copied(), output),
67
68        // Go
69        "go" => go::format_go(parts.get(1).copied(), output),
70        "golangci-lint" => Some(lint::format_lint(output)),
71
72        // JVM
73        "gradle" | "gradlew" | "./gradlew" => jvm::format_gradle(parts.get(1).copied(), output),
74        "mvn" | "maven" => jvm::format_maven(parts.get(1).copied(), output),
75
76        // Apple / Android / .NET
77        "xcodebuild" => Some(xcode::format_xcodebuild(output)),
78        "adb" if parts.get(1) == Some(&"logcat") => Some(android::format_logcat(output)),
79        "logcat" => Some(android::format_logcat(output)),
80        "dotnet" => dotnet::format_dotnet(parts.get(1).copied(), output),
81        // Ruby
82        "rspec" => Some(ruby::format_rspec(output)),
83        "rubocop" => Some(ruby::format_rubocop(output)),
84        "rake" | "rails" => ruby::format_rake(parts.get(1).copied(), output),
85        "bundle" | "bundler" => ruby::format_bundle(parts.get(1).copied(), output),
86
87        // Containers / orchestration
88        "docker" | "podman" => docker::format_docker(parts.get(1).copied(), output),
89        "kubectl" => kubectl::format_kubectl(parts.get(1).copied(), output),
90
91        // Cloud CLIs
92        "aws" => cloud::format_aws(parts.get(1).copied(), output),
93        "terraform" | "tf" => cloud::format_terraform(parts.get(1).copied(), output),
94        "gcloud" => cloud::format_gcloud(output),
95
96        // System utilities
97        "ls" => Some(system::format_ls(output)),
98        "find" | "fd" => Some(system::format_find(output)),
99        "grep" | "rg" | "ag" => Some(system::format_grep(output)),
100        "tree" => Some(system::format_tree(output)),
101        "curl" | "wget" => Some(system::format_curl(output)),
102
103        _ => None,
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn test_format_command_routing() {
113        assert!(format_command("git status", "nothing to commit").is_some());
114        assert!(format_command("cargo test", "test result: ok").is_some());
115        assert!(format_command("unknown_tool", "output").is_none());
116    }
117
118    #[test]
119    fn test_ansi_stripped_before_dispatch() {
120        let output = "\x1b[32mOn branch main\x1b[0m\nnothing to commit, working tree clean\n";
121        let result = format_command("git status", output);
122        assert_eq!(result, Some("clean".to_string()));
123    }
124
125    #[test]
126    fn test_new_commands_dispatch() {
127        assert!(format_command("gh pr list", "[]").is_some());
128        assert!(format_command("ruff check", "All checks passed!").is_some());
129        assert!(format_command("go test ./...", "ok").is_some());
130        assert!(format_command("grep foo bar.txt", "bar.txt:1:foo").is_some());
131        assert!(format_command("tree", ".\n├── src\n└── Cargo.toml").is_some());
132        assert!(format_command("curl http://example.com", "response").is_some());
133    }
134
135    #[test]
136    fn test_mobile_dotnet_dispatch() {
137        assert!(format_command("xcodebuild -scheme App", "** BUILD SUCCEEDED **").is_some());
138        assert!(format_command("adb logcat -d", "08-29 10:00:01.000  1  1 E Tag: boom").is_some());
139        assert!(format_command("logcat", "08-29 10:00:01.000  1  1 E Tag: boom").is_some());
140        assert!(format_command("dotnet build", "Build succeeded.").is_some());
141        assert!(format_command("dotnet test", "Passed! - Failed: 0, Passed: 1, Total: 1").is_some());
142        assert!(format_command("adb devices", "List of devices attached").is_none());
143        assert!(format_command("dotnet run", "hello").is_none());
144    }
145
146    #[test]
147    fn test_phase4_commands_dispatch() {
148        assert!(format_command("aws s3 ls", "2024-01-01 bucket/file.txt").is_some());
149        assert!(format_command("terraform plan", "No changes. Infrastructure is up-to-date.").is_some());
150        assert!(format_command("gradle build", "BUILD SUCCESSFUL in 3s").is_some());
151        assert!(format_command("mvn compile", "[INFO] BUILD SUCCESS").is_some());
152        assert!(format_command("kubectl describe pod nginx", "Name: nginx").is_some());
153    }
154
155    #[test]
156    fn test_ruby_commands_dispatch() {
157        assert!(format_command("rspec", "1 example, 0 failures").is_some());
158        assert!(
159            format_command("rubocop", "10 files inspected, no offenses detected").is_some()
160        );
161        assert!(format_command(
162            "rake test",
163            "8 runs, 9 assertions, 0 failures, 0 errors, 0 skips"
164        )
165        .is_some());
166        assert!(format_command(
167            "bundle install",
168            "Bundle complete! 85 dependencies"
169        )
170        .is_some());
171        // `bundle exec` prefix unwraps to the real tool.
172        assert!(format_command("bundle exec rspec", "1 example, 0 failures").is_some());
173        // Non-test rake tasks fall through to generic compression.
174        assert!(format_command("rake db:migrate", "== migrating ==").is_none());
175    }
176}