92 lines
2.4 KiB
Python
92 lines
2.4 KiB
Python
#!/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())
|