aboutsummaryrefslogtreecommitdiff
path: root/scripts/expand_codes.py
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-07-23 08:08:33 +0800
committer魏曹先生 <1992414357@qq.com>2026-07-23 14:01:45 +0800
commit68a652ed2f51d366bb8033497e6dfe545895410e (patch)
treed463833846248410733e196a0939c59cd67ca8a2 /scripts/expand_codes.py
feat: scaffold crate structure and implement core macros
Add the project skeleton, LICENSE files, README, Makefile, doc examples, and the initial implementation of `#[func]`, `invoke!`, and `select!` procedural macros.
Diffstat (limited to 'scripts/expand_codes.py')
-rw-r--r--scripts/expand_codes.py160
1 files changed, 160 insertions, 0 deletions
diff --git a/scripts/expand_codes.py b/scripts/expand_codes.py
new file mode 100644
index 0000000..c6b64a6
--- /dev/null
+++ b/scripts/expand_codes.py
@@ -0,0 +1,160 @@
+"""
+Generate `*_expand.rs` and `*_async_expand.rs` files in `doc/usage/` by running
+`cargo expand` on each usage example, once for sync and once for async.
+
+Requires `cargo-expand` to be installed:
+ cargo install cargo-expand
+"""
+
+import shutil
+import subprocess
+import sys
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parent.parent
+TEMP = ROOT / ".temp"
+USAGE = ROOT / "doc" / "usage"
+
+EXPAND_BEGIN = "use might_be_async::*;\nconst EXPAND_BEGIN: () = ();"
+EXPAND_END = "const EXPAND_END: () = ();"
+
+CARGO_TOML = """\
+[package]
+name = "expander"
+version = "0.0.0"
+edition = "2024"
+
+[workspace]
+
+[features]
+async = []
+
+[dependencies]
+might_be_async = { path = "../.." }
+"""
+
+
+def check_cargo_expand() -> None:
+ """Ensure cargo-expand is installed."""
+ try:
+ subprocess.run(
+ ["cargo", "expand", "--help"],
+ capture_output=True,
+ check=True,
+ )
+ except (FileNotFoundError, subprocess.CalledProcessError):
+ print("error: `cargo expand` not found. Install it with:")
+ print(" cargo install cargo-expand")
+ sys.exit(1)
+
+
+def prepare_temp() -> None:
+ """Create .temp/sync/ and .temp/async/ skeletons, and placeholder expand files."""
+ if TEMP.exists():
+ shutil.rmtree(TEMP)
+
+ for variant in ("sync", "async"):
+ (TEMP / variant / "src").mkdir(parents=True)
+ (TEMP / variant / "Cargo.toml").write_text(CARGO_TOML)
+
+ # Create placeholder _async_expand.rs files so the main crate can compile
+ # during cargo expand. They will be overwritten with real content later.
+ for name in ("func", "invoke", "select"):
+ placeholder = USAGE / f"{name}_async_expand.rs"
+ if not placeholder.exists():
+ placeholder.write_text(
+ "// placeholder — will be overwritten by expand script\n"
+ )
+
+
+def write_lib_rs(variant: str, source: str) -> None:
+ """Wrap `source` with EXPAND markers and write to .temp/{variant}/src/lib.rs."""
+ (TEMP / variant / "src" / "lib.rs").write_text(f"""\
+#![allow(unused_imports, dead_code)]
+
+{EXPAND_BEGIN}
+{source}
+{EXPAND_END}
+""")
+
+
+def run_expand(variant: str) -> str:
+ """Run `cargo expand` in .temp/{variant}/, optionally with --features async."""
+ cmd = ["cargo", "expand"]
+ if variant == "async":
+ cmd.append("--features")
+ cmd.append("async")
+
+ try:
+ result = subprocess.run(
+ cmd,
+ cwd=TEMP / variant,
+ capture_output=True,
+ text=True,
+ check=True,
+ )
+ return result.stdout
+ except subprocess.CalledProcessError as e:
+ print(f"error: `cargo expand` failed in .temp/{variant}/:", file=sys.stderr)
+ print(e.stderr, file=sys.stderr)
+ sys.exit(1)
+
+
+def extract_body(expanded: str) -> str:
+ """Extract the text between the EXPAND_BEGIN and EXPAND_END markers."""
+ lines = expanded.splitlines()
+ cleaned = "\n".join(line for line in lines if not line.startswith("#!"))
+
+ begin_idx = cleaned.find(EXPAND_BEGIN)
+ end_idx = cleaned.find(EXPAND_END)
+
+ if begin_idx == -1 or end_idx == -1:
+ print("error: could not locate EXPAND markers in output", file=sys.stderr)
+ print("=== expanded output (first 60 lines) ===")
+ print("\n".join(cleaned.splitlines()[:60]))
+ sys.exit(1)
+
+ start = begin_idx + len(EXPAND_BEGIN)
+ body = cleaned[start:end_idx]
+ return body.strip()
+
+
+def write_expand(name: str, suffix: str, body: str) -> None:
+ """Write the expanded body to doc/usage/{name}_{suffix}expand.rs."""
+ dest = USAGE / f"{name}_{suffix}expand.rs"
+ dest.write_text(body + "\n")
+ print(f" → {dest.name}")
+
+
+def main() -> None:
+ check_cargo_expand()
+ prepare_temp()
+
+ # Find all input .rs files that are NOT already expanded
+ inputs = sorted(
+ p
+ for p in USAGE.glob("*.rs")
+ if not p.name.endswith("_expand.rs") and not p.name.endswith("_async_expand.rs")
+ )
+
+ if not inputs:
+ print("No usage examples found in doc/usage/")
+ sys.exit(0)
+
+ print(f"Expanding {len(inputs)} example(s) with cargo-expand …\n")
+
+ for src in inputs:
+ stem = src.stem # "func", "invoke", "select"
+ code = src.read_text()
+
+ for variant, suffix in [("sync", ""), ("async", "async_")]:
+ write_lib_rs(variant, code)
+ expanded = run_expand(variant)
+ body = extract_body(expanded)
+ write_expand(stem, suffix, body)
+
+ print("\nDone.")
+
+
+if __name__ == "__main__":
+ main()