0.0.2-alpha.4
This commit is contained in:
78
scripts/audit_rust_crate_root_facades.py
Normal file
78
scripts/audit_rust_crate_root_facades.py
Normal file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
# file: scripts/audit_rust_crate_root_facades.py
|
||||
# version: 1
|
||||
|
||||
"""Audit Rust library crate roots so `src/lib.rs` remains a facade."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
from _rust_audit_common import line_depths, mask_source
|
||||
|
||||
_ITEM = re.compile(
|
||||
r"^\s*(?:(?:pub(?:\([^)]*\))?)\s+)?"
|
||||
r"(?:(?:async|const|unsafe)\s+)*(?:extern\s+\"[^\"]+\"\s+)?"
|
||||
r"(?:fn|struct|enum|union|trait|impl|type|const|static|macro_rules!)\b"
|
||||
)
|
||||
|
||||
|
||||
def _selected_lib_roots(raw_paths: list[str]) -> tuple[list[pathlib.Path], list[str]]:
|
||||
roots: set[pathlib.Path] = set()
|
||||
errors: list[str] = []
|
||||
for raw_path in raw_paths:
|
||||
path = pathlib.Path(raw_path)
|
||||
if path.is_dir():
|
||||
roots.update(candidate for candidate in path.rglob("src/lib.rs") if candidate.is_file())
|
||||
elif path.is_file() and path.as_posix().endswith("src/lib.rs"):
|
||||
roots.add(path)
|
||||
else:
|
||||
errors.append(f"unsupported or missing library-root path: {path}")
|
||||
return sorted(roots), errors
|
||||
|
||||
|
||||
def _audit_file(path: pathlib.Path) -> list[str]:
|
||||
text = path.read_text(encoding="utf-8")
|
||||
masked, _ = mask_source(text)
|
||||
lines = masked.splitlines()
|
||||
depths = line_depths(masked)
|
||||
errors: list[str] = []
|
||||
for index, line in enumerate(lines):
|
||||
if depths[index] != 0:
|
||||
continue
|
||||
if _ITEM.match(line) is not None:
|
||||
errors.append(f"{path}:{index + 1}: crate root must remain a facade; move this item to a responsibility module")
|
||||
return errors
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Run the read-only Knowledge library crate-root facade audit."""
|
||||
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("paths", nargs="+", help="Crate roots or directories containing Rust library crates")
|
||||
arguments = parser.parse_args()
|
||||
files, errors = _selected_lib_roots(arguments.paths)
|
||||
if errors or not files:
|
||||
for error in errors or ["no Rust library crate roots selected"]:
|
||||
print(f"Rust crate-root facade audit: {error}", file=sys.stderr)
|
||||
return 2
|
||||
for path in files:
|
||||
try:
|
||||
errors.extend(_audit_file(path))
|
||||
except (OSError, UnicodeError) as error:
|
||||
print(f"Rust crate-root facade audit: cannot read {path}: {error}", file=sys.stderr)
|
||||
return 2
|
||||
for error in errors:
|
||||
print(error, file=sys.stderr)
|
||||
if errors:
|
||||
print(f"Rust crate-root facade audit: {len(errors)} error(s) across {len(files)} crate root(s)", file=sys.stderr)
|
||||
return 1
|
||||
print(f"Rust crate-root facade audit: clean ({len(files)} crate root(s))")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user