Skip to content

Commit 67e6c15

Browse files
committed
Tooling: Add jscpd to find Rust code duplications
1 parent cb5b822 commit 67e6c15

2 files changed

Lines changed: 53 additions & 0 deletions

File tree

scripts/check/registry.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ func getCheckByName(name string) Check {
2323
return &RustTestsCheck{}
2424
case "rust-tests-linux":
2525
return &RustTestsLinuxCheck{}
26+
case "jscpd-rust":
27+
return &JscpdRustCheck{}
2628
// Desktop/Svelte checks
2729
case "prettier":
2830
return &PrettierCheck{}
@@ -89,6 +91,7 @@ func getRustChecks() []Check {
8991
&CargoAuditCheck{},
9092
&CargoDenyCheck{},
9193
&CargoUdepsCheck{},
94+
&JscpdRustCheck{},
9295
&RustTestsCheck{},
9396
&RustTestsLinuxCheck{},
9497
}

scripts/check/rust_checks.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,3 +250,53 @@ func (c *CargoUdepsCheck) Run(ctx *CheckContext) error {
250250
}
251251
return nil
252252
}
253+
254+
// JscpdRustCheck detects code duplication in Rust files.
255+
type JscpdRustCheck struct{}
256+
257+
func (c *JscpdRustCheck) Name() string {
258+
return "jscpd-rust"
259+
}
260+
261+
func (c *JscpdRustCheck) Run(ctx *CheckContext) error {
262+
rustSrcDir := filepath.Join(ctx.RootDir, "apps", "desktop", "src-tauri", "src")
263+
264+
// Check if jscpd is available via npx
265+
cmd := exec.Command("npx", "jscpd", "--version")
266+
if _, err := runCommand(cmd, true); err != nil {
267+
fmt.Printf("%sInstalling jscpd...%s ", colorYellow, colorReset)
268+
installCmd := exec.Command("npm", "install", "-g", "jscpd")
269+
if _, err := runCommand(installCmd, true); err != nil {
270+
return fmt.Errorf("failed to install jscpd: %w", err)
271+
}
272+
}
273+
274+
// Run jscpd on Rust source files
275+
// --min-lines 5: minimum 5 lines to consider as duplicate
276+
// --min-tokens 100: minimum 100 tokens (filters out small trait impls and test data)
277+
// --threshold 2: fail if >2% duplication
278+
// --ignore-pattern: ignore trait implementations (impl X for Y) which often have
279+
// similar accessor methods that can't be deduplicated
280+
cmd = exec.Command("npx", "jscpd",
281+
rustSrcDir,
282+
"--format", "rust",
283+
"--min-lines", "5",
284+
"--min-tokens", "100",
285+
"--threshold", "2",
286+
"--ignore", "**/test*.rs,**/*_test.rs",
287+
"--reporters", "console",
288+
)
289+
output, err := runCommand(cmd, true)
290+
if err != nil {
291+
// jscpd returns non-zero when threshold exceeded
292+
if strings.Contains(output, "duplicated lines") || strings.Contains(output, "threshold") {
293+
fmt.Println()
294+
fmt.Print(indentOutput(output, " "))
295+
return fmt.Errorf("code duplication exceeds threshold (2%%)")
296+
}
297+
fmt.Println()
298+
fmt.Print(indentOutput(output, " "))
299+
return fmt.Errorf("jscpd failed")
300+
}
301+
return nil
302+
}

0 commit comments

Comments
 (0)