"""Helpers for source-level contracts over split Rust modules. The project intentionally keeps Rust source files small and composes several modules with include!("..."). Static contracts must inspect the logical source, not only the thin facade file. """ from __future__ import annotations from pathlib import Path import re _INCLUDE = re.compile(r'include!\(\s*"([^"]+\.rs)"\s*\)\s*;?') def expanded_source(path: Path, _stack: tuple[Path, ...] = ()) -> str: path = path.resolve() if path in _stack: chain = " -> ".join(str(item) for item in (*_stack, path)) raise RuntimeError(f"cyclic Rust include!: {chain}") text = path.read_text(encoding="utf-8") def replace(match: re.Match[str]) -> str: child = (path.parent / match.group(1)).resolve() if not child.is_file(): raise FileNotFoundError(f"{path}: include target does not exist: {match.group(1)}") return expanded_source(child, (*_stack, path)) return _INCLUDE.sub(replace, text) def rust_source(root: Path, relative: str) -> str: """Read one logical Rust source file, recursively expanding include! fragments.""" return expanded_source(root / relative) def rust_tree(root: Path, relative_dir: str) -> str: """Read every Rust file beneath a module directory (including mod-declared children).""" directory = root / relative_dir return "\n".join( expanded_source(path) for path in sorted(directory.rglob("*.rs")) if path.is_file() )