Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
kykosic committed Aug 18, 2022
0 parents commit cf06f9d
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.DS_Store
__pycache__/
*.pyc

24 changes: 24 additions & 0 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
- id: cargo-fmt
name: cargo fmt
description: Format rust files with cargo fmt
entry: pre_commit_rust fmt
language: python
types: [rust]
minimum_pre_commit_version: 2.9.2
require_serial: true
- id: cargo-check
name: cargo check
description: Run cargo check for compilation errors
entry: pre_commit_rust check
language: python
types: [rust]
minimum_pre_commit_version: 2.9.2
require_serial: true
- id: cargo-clippy
name: cargo clippy
description: Run cargo clippy lint tool
entry: pre_commit_rust clippy
language: python
types: [rust]
minimum_pre_commit_version: 2.9.2
require_serial: true
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# pre-commit-rust

[pre-commit](https://pre-commit.com/) hooks for running `cargo fmt`, `cargo check`, and `cargo clippy`.

Unlike other existing hooks, these are designed to also work in a repo that contains multiple disconnected and arbitrarily nested rust projects that don't belong to the same workspace.
78 changes: 78 additions & 0 deletions pre_commit_rust.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
import argparse
import glob
import subprocess
from functools import lru_cache
from pathlib import Path

ACTIONS = ("fmt", "check", "clippy")


def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
"action",
choices=ACTIONS,
)
parser.add_argument(
"files",
nargs="*",
type=Path,
)
args = parser.parse_args()

run_dirs = get_run_dirs(args.files)
if not run_dirs:
return 0

failed = sum(run_action(args.action, d) for d in run_dirs)
return int(failed > 0)


def get_run_dirs(changed_files: list[Path]) -> set[Path]:
root_dirs = find_cargo_root_dirs()
run_dirs: set[Path] = set()
for path in changed_files:
if not is_rust_file(path):
continue
roots = [d for d in root_dirs if path.is_relative_to(d)]
if not roots:
continue
root = max(roots, key=path_len)
run_dirs.add(root)
return run_dirs


def find_cargo_root_dirs() -> list[Path]:
return [Path(p).parent for p in glob.glob("**/Cargo.toml", recursive=True)]


def is_rust_file(path: Path) -> bool:
if path.suffix == ".rs":
return True
elif path.name in ["Cargo.toml", "Cargo.lock"]:
return True
return False


@lru_cache
def path_len(path: Path) -> int:
return len(path.parts)


def run_action(action: str, directory: Path) -> int:
if action == "fmt":
cmd = "cargo fmt --"
elif action == "check":
cmd = "cargo check"
elif action == "clippy":
cmd = "cargo clippy -- -D warnings"
else:
raise ValueError(f"Invalid action {action!r}, expected one of: {ACTIONS}")

proc = subprocess.run(f"{cmd}", cwd=directory, shell=True)
return proc.returncode


if __name__ == "__main__":
exit(main())
8 changes: 8 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from setuptools import setup

setup(
name="pre_commit_rust",
version="0.1.0",
py_modules=["pre_commit_rust"],
entry_points={"console_scripts": ["pre_commit_rust=pre_commit_rust:main"]},
)

0 comments on commit cf06f9d

Please sign in to comment.