rustclamp 1.0.0

A minimal synchronous entrypoint for Clamp applications.
#!/usr/bin/env python3
"""Generate local Cargo coordination without changing package manifests."""

import argparse
from pathlib import Path
import shutil
import subprocess
import tempfile

REPOS = ("rustclamp", "core", "kernel", "runtime", "http", "postgres", "messaging", "worker", "scheduler")
MANIFEST = '''# Generated by rustclamp/tools/workspace.py; local coordination only.
[workspace]
resolver = "3"
members = ["rustclamp", "core", "kernel", "runtime", "http", "postgres", "messaging", "worker", "scheduler"]
exclude = ["experiments", "playground"]

# rustclamp depends on core through git (#86); the coordinated checkout uses the member.
[patch."https://github.com/rustclamp/core"]
rustclamp-core = { path = "core" }

[profile.release]
opt-level = 3
debug = false
strip = "none"
lto = false
codegen-units = 16
panic = "unwind"
'''
# Earlier generated manifests this script may replace in place.
_WITHOUT_PATCH = MANIFEST.replace(
    '\n# rustclamp depends on core through git (#86); the coordinated checkout uses the member.\n'
    '[patch."https://github.com/rustclamp/core"]\nrustclamp-core = { path = "core" }\n',
    '',
)
PREVIOUS_MANIFESTS = (
    _WITHOUT_PATCH,
    _WITHOUT_PATCH.replace(
        'members = ["rustclamp", "core", "kernel", "runtime", "http", "postgres", "messaging", "worker", "scheduler"]',
        'members = ["rustclamp", "core", "kernel", "runtime", "http", "postgres", "messaging", "worker"]',
    ),
)


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[2])
    args = parser.parse_args()
    root = args.root.resolve()
    for repo in REPOS:
        if not (root / repo / "Cargo.toml").is_file():
            parser.error(f"missing sibling package: {root / repo}")
    outputs = {
        root / "Cargo.toml": MANIFEST,
        root / "rust-toolchain.toml": (root / "rustclamp/rust-toolchain.toml").read_text(),
    }
    for path, content in outputs.items():
        if path.exists() and path.read_text() != content and not (
            path == root / "Cargo.toml" and path.read_text() in PREVIOUS_MANIFESTS
        ):
            parser.error(f"refusing to replace differing file: {path}")
    for path, content in outputs.items():
        path.write_text(content)
    (root / "workspace").mkdir(exist_ok=True)
    for repo in REPOS:
        lock = root / repo / "Cargo.lock"
        if not lock.exists():
            # Standalone lockfiles remain owned by their individual repositories.
            with tempfile.TemporaryDirectory(prefix="clamp-lock-") as tmp:
                temp_root = Path(tmp)
                for sibling in REPOS:
                    shutil.copytree(
                        root / sibling,
                        temp_root / sibling,
                        ignore=shutil.ignore_patterns(".git", "target", "Cargo.lock"),
                    )
                dest = temp_root / repo
                subprocess.run(["cargo", "generate-lockfile", "--offline"], cwd=dest, check=True)
                shutil.copyfile(dest / "Cargo.lock", lock)
    print(f"Coordination manifest: {root / 'Cargo.toml'}")


if __name__ == "__main__":
    main()