from __future__ import annotations import argparse import asyncio import json from pathlib import Path from .data_store import MockDataStore from .export_introspection import write_profiles from .profiles import list_profiles, load_profile from .server import FocasMockServer def _default_root() -> Path: return Path(__file__).resolve().parents[2] def build_parser() -> argparse.ArgumentParser: root = _default_root() parser = argparse.ArgumentParser(prog="focas-mock") sub = parser.add_subparsers(dest="command", required=True) serve = sub.add_parser("serve", help="Start the mock server.") serve.add_argument("--host", default="127.0.0.1") serve.add_argument("--port", type=int, default=8193) serve.add_argument("--profile", default="fwlib30i64") serve.add_argument("--data", help="Optional JSON patch file.") sub.add_parser("list-profiles", help="List built-in profiles.") dump_profile = sub.add_parser("dump-profile", help="Print one built-in profile as JSON.") dump_profile.add_argument("profile") extract = sub.add_parser("extract-profiles", help="Regenerate JSON profiles from vendored DLLs.") extract.add_argument("--dll-dir", default=str(root / "vendor" / "fanuc-cnc-api" / "64bit")) extract.add_argument("--fwlib", default=str(root / "upstream" / "fwlib.cs")) extract.add_argument("--out-dir", default=str(root / "src" / "focas_mock" / "builtin_profiles")) return parser async def _run_server(args: argparse.Namespace) -> None: profile = load_profile(args.profile) store = MockDataStore(profile) if args.data: store.load_patch_file(args.data) server = FocasMockServer(args.host, args.port, profile, store) await server.start() print(f"focas-mock listening on {server.host}:{server.port} with profile {profile['profile_name']}") try: await server.serve_forever() finally: await server.close() def main(argv: list[str] | None = None) -> None: parser = build_parser() args = parser.parse_args(argv) if args.command == "list-profiles": for profile in list_profiles(): print(profile) return if args.command == "dump-profile": print(json.dumps(load_profile(args.profile), indent=2)) return if args.command == "extract-profiles": written = write_profiles(args.dll_dir, args.fwlib, args.out_dir) for path in written: print(path) return if args.command == "serve": asyncio.run(_run_server(args)) return if __name__ == "__main__": main()