#!/usr/bin/env python3 """Report a Pydantic module's declarations as JSON on stdout. Takes one argv JSON object of `module` and `derives`; returns `enums` as name plus variant identifiers, and `structs` as rendered Rust source. """ import importlib import inspect import json import sys from enum import Enum from pathlib import Path from pydantic import BaseModel from pydantic_to_rust import ( emit_rust_struct, to_rust_variant_ident, walk_pydantic_model, ) def _declared_in(module, obj) -> bool: return getattr(obj, "__module__", None) == module.__name__ def discover_models(module) -> list[type[BaseModel]]: """BaseModel subclasses declared here. An imported one is another module's to emit.""" return [ obj for _, obj in inspect.getmembers(module, inspect.isclass) if issubclass(obj, BaseModel) and obj is not BaseModel and _declared_in(module, obj) ] def discover_enums(module) -> list[type[Enum]]: """Enum subclasses declared here. Only variant identifiers are reported; the caller renders the enum body.""" return [ obj for _, obj in inspect.getmembers(module, inspect.isclass) if issubclass(obj, Enum) and obj is not Enum and _declared_in(module, obj) ] def main() -> int: if len(sys.argv) < 2: sys.stderr.write("run_pydantic_to_rust.py: missing JSON payload argument\n") return 2 payload = json.loads(sys.argv[1]) module_name: str = payload["module"] derives = tuple(payload["derives"]) sys.path.insert(0, str(Path.cwd())) module = importlib.import_module(module_name) enums = discover_enums(module) models = discover_models(module) if not enums and not models: sys.stderr.write( f"run_pydantic_to_rust.py: no Enum or BaseModel subclasses " f"declared in {module_name!r}\n" ) return 3 json.dump( { "enums": [ { "name": enum_class.__name__, "variants": [to_rust_variant_ident(m.name) for m in enum_class], } for enum_class in enums ], "structs": [ emit_rust_struct(walk_pydantic_model(model), derives=derives) for model in models ], }, sys.stdout, ) return 0 if __name__ == "__main__": sys.exit(main())