import re
import sys
from pathlib import Path
SRC = Path(__file__).parent / "src"
LIB_RS = SRC / "lib.rs"
SKIP_SUFFIXES = ("_BACKUP_", "_BASE_", "_LOCAL_", "_REMOTE_")
def is_backup(path: Path) -> bool:
return any(part in path.stem for part in SKIP_SUFFIXES)
def collect_pub_structs(src_dir: Path) -> dict[str, list[tuple[Path, int]]]:
pattern = re.compile(r"^pub (?:struct|enum) (\w+)")
result: dict[str, list[tuple[Path, int]]] = {}
for path in sorted(src_dir.glob("*.rs")):
if path.name == "lib.rs" or is_backup(path):
continue
for lineno, line in enumerate(path.read_text().splitlines(), 1):
m = pattern.match(line)
if m:
name = m.group(1)
result.setdefault(name, []).append((path, lineno))
return result
def collect_pub_use_names(lib_rs: Path) -> set[str]:
ident_pattern = re.compile(r"\b([A-Z][A-Za-z0-9_]*)\b")
names: set[str] = set()
text = lib_rs.read_text()
for block in re.findall(r"\bpub\s+use\s+[^;]+;", text, re.DOTALL):
names.update(ident_pattern.findall(block))
return names
def main() -> int:
structs = collect_pub_structs(SRC)
exported = collect_pub_use_names(LIB_RS)
missing: list[tuple[str, Path, int]] = []
for name, locations in sorted(structs.items()):
if name not in exported:
for path, lineno in locations:
missing.append((name, path, lineno))
if not missing:
print(f"OK: all {len(structs)} pub structs/enums are re-exported in lib.rs")
return 0
print(f"MISSING from lib.rs pub use ({len(missing)} occurrence(s)):\n")
for name, path, lineno in missing:
rel = path.relative_to(SRC.parent.parent)
print(f" {name:40s} {rel}:{lineno}")
return 1
if __name__ == "__main__":
sys.exit(main())