aboutsummaryrefslogtreecommitdiff
path: root/scripts/expand_codes.py
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-07-23 09:52:25 +0800
committer魏曹先生 <1992414357@qq.com>2026-07-23 09:52:25 +0800
commit194bf5a03c78340284efd1d37ff888251c4c42d7 (patch)
treedd1358690a88eaa21064f8515ec73fb4863895a6 /scripts/expand_codes.py
parent56d283991a54a77975532e5e084db216f5c187e1 (diff)
21
Diffstat (limited to 'scripts/expand_codes.py')
-rw-r--r--scripts/expand_codes.py152
1 files changed, 134 insertions, 18 deletions
diff --git a/scripts/expand_codes.py b/scripts/expand_codes.py
index e9ca7bc..28eed4f 100644
--- a/scripts/expand_codes.py
+++ b/scripts/expand_codes.py
@@ -1,18 +1,134 @@
-# 0. 检查安装 cargo-expand 并快速失败
-# 1. 检查并创建目录
-#
-# .temp/Cargo.toml 内容只有一个简单的[package]和一个空[workspace](这很关键),还有相对依赖(../)的might_be_async
-#
-# 然后读取 doc/usage 下所有结尾不是_expand.rs的.rs文件,执行循环,过程如下:
-#
-# 1. 将内容写入.temp/src/lib.rs,并加入内容
-#
-# const EXPAND_BEGIN: () = ();
-# 源码
-# const EXPAND_END: () = ();
-#
-# 2. 执行 cargo expand,将const EXPAND_BEGIN: () = (); ... const EXPAND_END: () = ();的内容截取
-#
-# 3. trim 字符串,写入 doc/usage/源文件名_expand.rs
-
-# 这玩意加入 make test 中
+"""
+Generate `*_expand.rs` files in `doc/usage/` by running `cargo expand` on
+each usage example.
+
+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 = "const EXPAND_BEGIN: () = ();"
+EXPAND_END = "const EXPAND_END: () = ();"
+
+
+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/ skeleton with Cargo.toml that depends on might_be_async."""
+ if TEMP.exists():
+ shutil.rmtree(TEMP)
+
+ (TEMP / "src").mkdir(parents=True)
+
+ # Cargo.toml with an empty [workspace] to isolate from any parent workspace
+ (TEMP / "Cargo.toml").write_text("""\
+[package]
+name = "expander"
+version = "0.0.0"
+edition = "2024"
+
+[workspace]
+
+[dependencies]
+might_be_async = { path = ".." }
+""")
+
+
+def write_lib_rs(source: str) -> None:
+ """Wrap `source` with EXPAND markers and write to .temp/src/lib.rs."""
+ (TEMP / "src" / "lib.rs").write_text(f"""\
+#![allow(unused_imports, dead_code)]
+
+{EXPAND_BEGIN}
+{source}
+{EXPAND_END}
+""")
+
+
+def run_expand() -> str:
+ """Run `cargo expand` in .temp/ and return the stdout."""
+ result = subprocess.run(
+ ["cargo", "expand"],
+ cwd=TEMP,
+ capture_output=True,
+ text=True,
+ check=True,
+ )
+ return result.stdout
+
+
+def extract_body(expanded: str) -> str:
+ """Extract the text between the EXPAND_BEGIN and EXPAND_END markers."""
+ # Remove shebang (#![...]) lines at the top so the markers are easier to find
+ lines = expanded.splitlines()
+ cleaned = "\n".join(line for line in lines if not line.startswith("#!"))
+
+ # Find the marker lines
+ 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)
+
+ # Extract text between the two markers
+ start = begin_idx + len(EXPAND_BEGIN)
+ body = cleaned[start:end_idx]
+ return body.strip()
+
+
+def write_expand(name: str, body: str) -> None:
+ """Write the expanded body to doc/usage/{name}_expand.rs."""
+ dest = USAGE / f"{name}_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"))
+
+ 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()
+ write_lib_rs(code)
+ expanded = run_expand()
+ body = extract_body(expanded)
+ write_expand(stem, body)
+
+ print("\nDone.")
+
+
+if __name__ == "__main__":
+ main()