291 lines
8.4 KiB
Python
291 lines
8.4 KiB
Python
"""
|
|
Modelgen - Generic Model Generation Tool
|
|
|
|
Generates typed models from various sources to various formats.
|
|
|
|
Input sources:
|
|
- from-config: Configuration files (soleprint config.json style)
|
|
- from-schema: Python dataclasses in schema/ folder
|
|
- extract: Existing codebases (Django, SQLAlchemy, Prisma)
|
|
|
|
Output formats:
|
|
- pydantic: Pydantic BaseModel classes
|
|
- django: Django ORM models
|
|
- typescript: TypeScript interfaces
|
|
- protobuf: Protocol Buffer definitions
|
|
- prisma: Prisma schema
|
|
|
|
Usage:
|
|
python -m soleprint.station.tools.modelgen --help
|
|
python -m soleprint.station.tools.modelgen from-config -c config.json -o models.py
|
|
python -m soleprint.station.tools.modelgen from-schema -o models/ --targets pydantic,typescript
|
|
python -m soleprint.station.tools.modelgen extract --source /path/to/django --targets pydantic
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from .generator import GENERATORS
|
|
|
|
|
|
def cmd_from_config(args):
|
|
"""Generate models from a configuration file (soleprint config.json style)."""
|
|
from .loader import load_config
|
|
from .model_generator import ModelGenerator
|
|
|
|
config_path = Path(args.config)
|
|
if not config_path.exists():
|
|
print(f"Error: Config file not found: {config_path}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
output_path = Path(args.output)
|
|
|
|
print(f"Loading config: {config_path}")
|
|
config = load_config(config_path)
|
|
|
|
print(f"Generating {args.format} models to: {output_path}")
|
|
generator = ModelGenerator(
|
|
config=config,
|
|
output_path=output_path,
|
|
output_format=args.format,
|
|
)
|
|
result_path = generator.generate()
|
|
|
|
print(f"Models generated: {result_path}")
|
|
|
|
|
|
def cmd_from_schema(args):
|
|
"""Generate models from Python dataclasses in schema/ folder."""
|
|
from .loader import load_schema
|
|
from .writer import write_file
|
|
|
|
# Determine schema path
|
|
schema_path = Path(args.schema) if args.schema else Path.cwd() / "schema"
|
|
|
|
if not schema_path.exists():
|
|
print(f"Error: Schema folder not found: {schema_path}", file=sys.stderr)
|
|
print(
|
|
"Create a schema/ folder with Python dataclasses and an __init__.py",
|
|
file=sys.stderr,
|
|
)
|
|
print("that exports DATACLASSES and ENUMS lists.", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
print(f"Loading schema: {schema_path}")
|
|
schema = load_schema(schema_path)
|
|
|
|
print(f"Found {len(schema.models)} models, {len(schema.enums)} enums")
|
|
|
|
# Parse targets
|
|
targets = [t.strip() for t in args.targets.split(",")]
|
|
output_dir = Path(args.output)
|
|
|
|
for target in targets:
|
|
if target not in GENERATORS:
|
|
print(f"Warning: Unknown target '{target}', skipping", file=sys.stderr)
|
|
continue
|
|
|
|
generator = GENERATORS[target]()
|
|
ext = generator.file_extension()
|
|
|
|
# Determine output filename (use target name to avoid overwrites)
|
|
if len(targets) == 1 and args.output.endswith(ext):
|
|
output_file = output_dir
|
|
else:
|
|
output_file = output_dir / f"models_{target}{ext}"
|
|
|
|
print(f"Generating {target} to: {output_file}")
|
|
generator.generate(schema, output_file)
|
|
|
|
print("Done!")
|
|
|
|
|
|
def cmd_extract(args):
|
|
"""Extract models from existing codebase."""
|
|
from .loader.extract import EXTRACTORS
|
|
|
|
source_path = Path(args.source)
|
|
if not source_path.exists():
|
|
print(f"Error: Source path not found: {source_path}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
# Auto-detect or use specified framework
|
|
framework = args.framework
|
|
extractor = None
|
|
|
|
if framework == "auto":
|
|
for name, extractor_cls in EXTRACTORS.items():
|
|
ext = extractor_cls(source_path)
|
|
if ext.detect():
|
|
framework = name
|
|
extractor = ext
|
|
print(f"Detected framework: {framework}")
|
|
break
|
|
|
|
if not extractor:
|
|
print("Error: Could not auto-detect framework", file=sys.stderr)
|
|
print(f"Available frameworks: {list(EXTRACTORS.keys())}", file=sys.stderr)
|
|
sys.exit(1)
|
|
else:
|
|
if framework not in EXTRACTORS:
|
|
print(f"Error: Unknown framework: {framework}", file=sys.stderr)
|
|
print(f"Available: {list(EXTRACTORS.keys())}", file=sys.stderr)
|
|
sys.exit(1)
|
|
extractor = EXTRACTORS[framework](source_path)
|
|
|
|
print(f"Extracting from: {source_path}")
|
|
models, enums = extractor.extract()
|
|
|
|
print(f"Extracted {len(models)} models, {len(enums)} enums")
|
|
|
|
# Parse targets
|
|
targets = [t.strip() for t in args.targets.split(",")]
|
|
output_dir = Path(args.output)
|
|
|
|
for target in targets:
|
|
if target not in GENERATORS:
|
|
print(f"Warning: Unknown target '{target}', skipping", file=sys.stderr)
|
|
continue
|
|
|
|
generator = GENERATORS[target]()
|
|
ext = generator.file_extension()
|
|
|
|
# Determine output filename (use target name to avoid overwrites)
|
|
if len(targets) == 1 and args.output.endswith(ext):
|
|
output_file = output_dir
|
|
else:
|
|
output_file = output_dir / f"models_{target}{ext}"
|
|
|
|
print(f"Generating {target} to: {output_file}")
|
|
generator.generate((models, enums), output_file)
|
|
|
|
print("Done!")
|
|
|
|
|
|
def cmd_list_formats(args):
|
|
"""List available output formats."""
|
|
print("Available output formats:")
|
|
for fmt in GENERATORS.keys():
|
|
print(f" - {fmt}")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Modelgen - Generic Model Generation Tool",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
)
|
|
|
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
|
|
# Available formats for help text
|
|
formats = list(GENERATORS.keys())
|
|
formats_str = ", ".join(formats)
|
|
|
|
# from-config command
|
|
config_parser = subparsers.add_parser(
|
|
"from-config",
|
|
help="Generate models from soleprint configuration file",
|
|
)
|
|
config_parser.add_argument(
|
|
"--config",
|
|
"-c",
|
|
type=str,
|
|
required=True,
|
|
help="Path to configuration file (e.g., config.json)",
|
|
)
|
|
config_parser.add_argument(
|
|
"--output",
|
|
"-o",
|
|
type=str,
|
|
required=True,
|
|
help="Output path (file or directory)",
|
|
)
|
|
config_parser.add_argument(
|
|
"--format",
|
|
"-f",
|
|
type=str,
|
|
default="pydantic",
|
|
choices=["pydantic"], # Only pydantic for config mode
|
|
help="Output format (default: pydantic)",
|
|
)
|
|
config_parser.set_defaults(func=cmd_from_config)
|
|
|
|
# from-schema command
|
|
schema_parser = subparsers.add_parser(
|
|
"from-schema",
|
|
help="Generate models from Python dataclasses in schema/ folder",
|
|
)
|
|
schema_parser.add_argument(
|
|
"--schema",
|
|
"-s",
|
|
type=str,
|
|
default=None,
|
|
help="Path to schema folder (default: ./schema)",
|
|
)
|
|
schema_parser.add_argument(
|
|
"--output",
|
|
"-o",
|
|
type=str,
|
|
required=True,
|
|
help="Output path (file or directory)",
|
|
)
|
|
schema_parser.add_argument(
|
|
"--targets",
|
|
"-t",
|
|
type=str,
|
|
default="pydantic",
|
|
help=f"Comma-separated output targets ({formats_str})",
|
|
)
|
|
schema_parser.set_defaults(func=cmd_from_schema)
|
|
|
|
# extract command
|
|
extract_parser = subparsers.add_parser(
|
|
"extract",
|
|
help="Extract models from existing codebase",
|
|
)
|
|
extract_parser.add_argument(
|
|
"--source",
|
|
"-s",
|
|
type=str,
|
|
required=True,
|
|
help="Path to source codebase",
|
|
)
|
|
extract_parser.add_argument(
|
|
"--framework",
|
|
"-f",
|
|
type=str,
|
|
choices=["django", "sqlalchemy", "prisma", "auto"],
|
|
default="auto",
|
|
help="Source framework (default: auto-detect)",
|
|
)
|
|
extract_parser.add_argument(
|
|
"--output",
|
|
"-o",
|
|
type=str,
|
|
required=True,
|
|
help="Output path (file or directory)",
|
|
)
|
|
extract_parser.add_argument(
|
|
"--targets",
|
|
"-t",
|
|
type=str,
|
|
default="pydantic",
|
|
help=f"Comma-separated output targets ({formats_str})",
|
|
)
|
|
extract_parser.set_defaults(func=cmd_extract)
|
|
|
|
# list-formats command
|
|
formats_parser = subparsers.add_parser(
|
|
"list-formats",
|
|
help="List available output formats",
|
|
)
|
|
formats_parser.set_defaults(func=cmd_list_formats)
|
|
|
|
args = parser.parse_args()
|
|
args.func(args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|