""" 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()