Skip to content

Commit e43f889

Browse files
committed
feat: add cargo-fmt and clippy fixers
Adds Rust toolchain fixers: cargo fmt for formatting and clippy --fix for auto-fixable lint warnings.
1 parent 9669861 commit e43f889

File tree

4 files changed

+174
-34
lines changed

4 files changed

+174
-34
lines changed

dist/index.js

Lines changed: 88 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30015,11 +30015,64 @@ async function runEngine(ctx, opts) {
3001530015
}
3001630016
}
3001730017

30018-
// src/fixers/eslint.ts
30018+
// src/fixers/cargo-fmt.ts
3001930019
import { exec as exec2 } from "node:child_process";
3002030020
import { promisify as promisify2 } from "node:util";
3002130021
var run2 = promisify2(exec2);
30022-
var DETECT_PATTERNS = [
30022+
var DETECT_PATTERNS = [/cargo fmt/i, /rustfmt/i, /Diff in \/.*\.rs/i, /should run `cargo fmt`/i];
30023+
var cargoFmtFixer = {
30024+
name: "cargo-fmt",
30025+
detect(log) {
30026+
return DETECT_PATTERNS.some((p) => p.test(log));
30027+
},
30028+
async fix(cwd) {
30029+
try {
30030+
await run2("cargo fmt --all", { cwd });
30031+
} catch {}
30032+
const { stdout: status } = await run2("git diff --name-only", { cwd });
30033+
const files = status.trim().split(`
30034+
`).filter(Boolean);
30035+
return {
30036+
summary: files.length > 0 ? `Formatted ${files.length} file(s) with cargo fmt` : "cargo fmt ran but all files were already formatted",
30037+
filesChanged: files.length
30038+
};
30039+
}
30040+
};
30041+
30042+
// src/fixers/clippy.ts
30043+
import { exec as exec3 } from "node:child_process";
30044+
import { promisify as promisify3 } from "node:util";
30045+
var run3 = promisify3(exec3);
30046+
var DETECT_PATTERNS2 = [
30047+
/cargo clippy/i,
30048+
/clippy::[\w_]+/,
30049+
/warning:.*\(clippy\)/i,
30050+
/error\[E\d+\]/
30051+
];
30052+
var clippyFixer = {
30053+
name: "clippy",
30054+
detect(log) {
30055+
return DETECT_PATTERNS2.some((p) => p.test(log));
30056+
},
30057+
async fix(cwd) {
30058+
try {
30059+
await run3("cargo clippy --fix --allow-dirty --allow-staged -- -W clippy::all", { cwd });
30060+
} catch {}
30061+
const { stdout: status } = await run3("git diff --name-only", { cwd });
30062+
const files = status.trim().split(`
30063+
`).filter(Boolean);
30064+
return {
30065+
summary: files.length > 0 ? `Auto-fixed clippy warnings in ${files.length} file(s)` : "clippy ran but produced no auto-fixable changes",
30066+
filesChanged: files.length
30067+
};
30068+
}
30069+
};
30070+
30071+
// src/fixers/eslint.ts
30072+
import { exec as exec4 } from "node:child_process";
30073+
import { promisify as promisify4 } from "node:util";
30074+
var run4 = promisify4(exec4);
30075+
var DETECT_PATTERNS3 = [
3002330076
/eslint.*error/i,
3002430077
/\d+ errors?.*found/i,
3002530078
/Linting error/i,
@@ -30029,13 +30082,13 @@ var DETECT_PATTERNS = [
3002930082
var eslintFixer = {
3003030083
name: "eslint",
3003130084
detect(log) {
30032-
return DETECT_PATTERNS.some((p) => p.test(log));
30085+
return DETECT_PATTERNS3.some((p) => p.test(log));
3003330086
},
3003430087
async fix(cwd) {
3003530088
try {
30036-
await run2("npx eslint --fix .", { cwd });
30089+
await run4("npx eslint --fix .", { cwd });
3003730090
} catch {}
30038-
const { stdout: status } = await run2("git diff --name-only", { cwd });
30091+
const { stdout: status } = await run4("git diff --name-only", { cwd });
3003930092
const files = status.trim().split(`
3004030093
`).filter(Boolean);
3004130094
return {
@@ -30046,12 +30099,12 @@ var eslintFixer = {
3004630099
};
3004730100

3004830101
// src/fixers/lockfile.ts
30049-
import { exec as exec3 } from "node:child_process";
30050-
import { promisify as promisify3 } from "node:util";
30102+
import { exec as exec5 } from "node:child_process";
30103+
import { promisify as promisify5 } from "node:util";
3005130104
import { access } from "node:fs/promises";
3005230105
import { join as join2 } from "node:path";
30053-
var run3 = promisify3(exec3);
30054-
var DETECT_PATTERNS2 = [
30106+
var run5 = promisify5(exec5);
30107+
var DETECT_PATTERNS4 = [
3005530108
/lockfile.*out of date/i,
3005630109
/frozen-lockfile/i,
3005730110
/ERR_PNPM_OUTDATED_LOCKFILE/i,
@@ -30084,14 +30137,14 @@ async function detectPackageManager(cwd) {
3008430137
var lockfileFixer = {
3008530138
name: "lockfile",
3008630139
detect(log) {
30087-
return DETECT_PATTERNS2.some((p) => p.test(log));
30140+
return DETECT_PATTERNS4.some((p) => p.test(log));
3008830141
},
3008930142
async fix(cwd) {
3009030143
const pm = await detectPackageManager(cwd);
3009130144
try {
30092-
await run3(`${pm} install`, { cwd });
30145+
await run5(`${pm} install`, { cwd });
3009330146
} catch {}
30094-
const { stdout: status } = await run3("git diff --name-only", { cwd });
30147+
const { stdout: status } = await run5("git diff --name-only", { cwd });
3009530148
const files = status.trim().split(`
3009630149
`).filter(Boolean);
3009730150
return {
@@ -30102,20 +30155,20 @@ var lockfileFixer = {
3010230155
};
3010330156

3010430157
// src/fixers/oxfmt.ts
30105-
import { exec as exec4 } from "node:child_process";
30106-
import { promisify as promisify4 } from "node:util";
30107-
var run4 = promisify4(exec4);
30108-
var DETECT_PATTERNS3 = [/oxfmt/i, /oxc.*format/i];
30158+
import { exec as exec6 } from "node:child_process";
30159+
import { promisify as promisify6 } from "node:util";
30160+
var run6 = promisify6(exec6);
30161+
var DETECT_PATTERNS5 = [/oxfmt/i, /oxc.*format/i];
3010930162
var oxfmtFixer = {
3011030163
name: "oxfmt",
3011130164
detect(log) {
30112-
return DETECT_PATTERNS3.some((p) => p.test(log));
30165+
return DETECT_PATTERNS5.some((p) => p.test(log));
3011330166
},
3011430167
async fix(cwd) {
3011530168
try {
30116-
await run4("npx oxfmt --write .", { cwd });
30169+
await run6("npx oxfmt --write .", { cwd });
3011730170
} catch {}
30118-
const { stdout: status } = await run4("git diff --name-only", { cwd });
30171+
const { stdout: status } = await run6("git diff --name-only", { cwd });
3011930172
const files = status.trim().split(`
3012030173
`).filter(Boolean);
3012130174
return {
@@ -30126,20 +30179,20 @@ var oxfmtFixer = {
3012630179
};
3012730180

3012830181
// src/fixers/oxlint.ts
30129-
import { exec as exec5 } from "node:child_process";
30130-
import { promisify as promisify5 } from "node:util";
30131-
var run5 = promisify5(exec5);
30132-
var DETECT_PATTERNS4 = [/oxlint/i, /oxc.*lint/i, /Found \d+ errors?.*oxlint/i];
30182+
import { exec as exec7 } from "node:child_process";
30183+
import { promisify as promisify7 } from "node:util";
30184+
var run7 = promisify7(exec7);
30185+
var DETECT_PATTERNS6 = [/oxlint/i, /oxc.*lint/i, /Found \d+ errors?.*oxlint/i];
3013330186
var oxlintFixer = {
3013430187
name: "oxlint",
3013530188
detect(log) {
30136-
return DETECT_PATTERNS4.some((p) => p.test(log));
30189+
return DETECT_PATTERNS6.some((p) => p.test(log));
3013730190
},
3013830191
async fix(cwd) {
3013930192
try {
30140-
await run5("npx oxlint --fix .", { cwd });
30193+
await run7("npx oxlint --fix .", { cwd });
3014130194
} catch {}
30142-
const { stdout: status } = await run5("git diff --name-only", { cwd });
30195+
const { stdout: status } = await run7("git diff --name-only", { cwd });
3014330196
const files = status.trim().split(`
3014430197
`).filter(Boolean);
3014530198
return {
@@ -30150,10 +30203,10 @@ var oxlintFixer = {
3015030203
};
3015130204

3015230205
// src/fixers/prettier.ts
30153-
import { exec as exec6 } from "node:child_process";
30154-
import { promisify as promisify6 } from "node:util";
30155-
var run6 = promisify6(exec6);
30156-
var DETECT_PATTERNS5 = [
30206+
import { exec as exec8 } from "node:child_process";
30207+
import { promisify as promisify8 } from "node:util";
30208+
var run8 = promisify8(exec8);
30209+
var DETECT_PATTERNS7 = [
3015730210
/prettier.*--check/i,
3015830211
/Code style issues found/i,
3015930212
/\[warn\].*isn't formatted/i,
@@ -30163,13 +30216,13 @@ var DETECT_PATTERNS5 = [
3016330216
var prettierFixer = {
3016430217
name: "prettier",
3016530218
detect(log) {
30166-
return DETECT_PATTERNS5.some((p) => p.test(log));
30219+
return DETECT_PATTERNS7.some((p) => p.test(log));
3016730220
},
3016830221
async fix(cwd) {
3016930222
try {
30170-
await run6("npx prettier --write .", { cwd });
30223+
await run8("npx prettier --write .", { cwd });
3017130224
} catch {}
30172-
const { stdout: status } = await run6("git diff --name-only", { cwd });
30225+
const { stdout: status } = await run8("git diff --name-only", { cwd });
3017330226
const files = status.trim().split(`
3017430227
`).filter(Boolean);
3017530228
return {
@@ -30181,6 +30234,8 @@ var prettierFixer = {
3018130234

3018230235
// src/fixers/index.ts
3018330236
var builtinFixers = [
30237+
cargoFmtFixer,
30238+
clippyFixer,
3018430239
eslintFixer,
3018530240
lockfileFixer,
3018630241
oxfmtFixer,

src/fixers/cargo-fmt.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { exec } from "node:child_process";
2+
import { promisify } from "node:util";
3+
import type { Fixer, FixResult } from "../core/types.js";
4+
5+
const run = promisify(exec);
6+
7+
const DETECT_PATTERNS = [/cargo fmt/i, /rustfmt/i, /Diff in \/.*\.rs/i, /should run `cargo fmt`/i];
8+
9+
export const cargoFmtFixer: Fixer = {
10+
name: "cargo-fmt",
11+
12+
detect(log: string): boolean {
13+
return DETECT_PATTERNS.some((p) => p.test(log));
14+
},
15+
16+
async fix(cwd: string): Promise<FixResult> {
17+
try {
18+
await run("cargo fmt --all", { cwd });
19+
} catch {
20+
// cargo fmt can fail if rustfmt is not installed
21+
}
22+
23+
const { stdout: status } = await run("git diff --name-only", { cwd });
24+
const files = status.trim().split("\n").filter(Boolean);
25+
26+
return {
27+
summary:
28+
files.length > 0
29+
? `Formatted ${files.length} file(s) with cargo fmt`
30+
: "cargo fmt ran but all files were already formatted",
31+
filesChanged: files.length,
32+
};
33+
},
34+
};

src/fixers/clippy.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { exec } from "node:child_process";
2+
import { promisify } from "node:util";
3+
import type { Fixer, FixResult } from "../core/types.js";
4+
5+
const run = promisify(exec);
6+
7+
const DETECT_PATTERNS = [
8+
/cargo clippy/i,
9+
/clippy::[\w_]+/,
10+
/warning:.*\(clippy\)/i,
11+
/error\[E\d+\]/,
12+
];
13+
14+
export const clippyFixer: Fixer = {
15+
name: "clippy",
16+
17+
detect(log: string): boolean {
18+
return DETECT_PATTERNS.some((p) => p.test(log));
19+
},
20+
21+
async fix(cwd: string): Promise<FixResult> {
22+
try {
23+
await run("cargo clippy --fix --allow-dirty --allow-staged -- -W clippy::all", { cwd });
24+
} catch {
25+
// clippy --fix exits non-zero if unfixable warnings remain
26+
}
27+
28+
const { stdout: status } = await run("git diff --name-only", { cwd });
29+
const files = status.trim().split("\n").filter(Boolean);
30+
31+
return {
32+
summary:
33+
files.length > 0
34+
? `Auto-fixed clippy warnings in ${files.length} file(s)`
35+
: "clippy ran but produced no auto-fixable changes",
36+
filesChanged: files.length,
37+
};
38+
},
39+
};

src/fixers/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
import type { Fixer } from "../core/types.js";
2+
import { cargoFmtFixer } from "./cargo-fmt.js";
3+
import { clippyFixer } from "./clippy.js";
24
import { eslintFixer } from "./eslint.js";
35
import { lockfileFixer } from "./lockfile.js";
46
import { oxfmtFixer } from "./oxfmt.js";
57
import { oxlintFixer } from "./oxlint.js";
68
import { prettierFixer } from "./prettier.js";
79

810
export const builtinFixers: Fixer[] = [
11+
cargoFmtFixer,
12+
clippyFixer,
913
eslintFixer,
1014
lockfileFixer,
1115
oxfmtFixer,
1216
oxlintFixer,
1317
prettierFixer,
1418
];
1519

16-
export { eslintFixer, lockfileFixer, oxfmtFixer, oxlintFixer, prettierFixer };
20+
export {
21+
cargoFmtFixer,
22+
clippyFixer,
23+
eslintFixer,
24+
lockfileFixer,
25+
oxfmtFixer,
26+
oxlintFixer,
27+
prettierFixer,
28+
};

0 commit comments

Comments
 (0)