44 lines
1.5 KiB
Python
Executable File
44 lines
1.5 KiB
Python
Executable File
"""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()
|
|
)
|