#!/usr/bin/env python3 # file: scripts/audit_rust_trait_import_blocks.py # version: 1 """Audit placement and contiguity of module-level Rust trait-import blocks.""" from __future__ import annotations import argparse import pathlib import re import sys from _rust_audit_common import line_depths, mask_source, selected_files _DECLARATION = re.compile(r"^(?:pub(?:\(crate\))?\s+)?(?:async\s+|const\s+|unsafe\s+)*(?:const|static|type|struct|enum|trait|union|fn|impl)\b") _USE = re.compile(r"^use\s+") def audit_file(path: pathlib.Path) -> list[str]: """Require ordinary module-level uses before declarations and in one compact block.""" text = path.read_text(encoding="utf-8") lines = text.splitlines() masked, _ = mask_source(text) depths = line_depths(masked) seen_declaration = False uses: list[int] = [] errors: list[str] = [] for number, line in enumerate(lines, 1): if depths[number - 1] != 0: continue stripped = line.strip() if _DECLARATION.match(stripped): seen_declaration = True if _USE.match(stripped): uses.append(number) if seen_declaration: errors.append(f"{path}:{number}: module-level trait import must precede Rust declarations") for previous, current in zip(uses, uses[1:]): between = lines[previous:current - 1] if any(not candidate.strip() for candidate in between): errors.append(f"{path}:{current}: module-level trait import block contains an empty line") return errors def main() -> int: """Run the read-only trait-import-block audit on explicitly selected Rust files.""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("paths", nargs="+", help="Rust files or directories to inspect") args = parser.parse_args() files, errors = selected_files(args.paths) if errors or not files: for error in errors or ["no Rust files selected"]: print(f"Rust trait-import-block 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 trait-import-block audit: cannot read {path}: {error}", file=sys.stderr) return 2 for error in errors: print(error, file=sys.stderr) if errors: print(f"Rust trait-import-block audit: {len(errors)} error(s) across {len(files)} file(s)", file=sys.stderr) return 1 print(f"Rust trait-import-block audit: clean ({len(files)} file(s))") return 0 if __name__ == "__main__": raise SystemExit(main())