diff --git a/configforge/commands/generate.py b/configforge/commands/generate.py new file mode 100644 index 0000000..8ebb8fd --- /dev/null +++ b/configforge/commands/generate.py @@ -0,0 +1,94 @@ +"""Generate command for ConfigForge.""" + +from pathlib import Path +from typing import Optional + +import click + +from configforge.exceptions import ( + FileOperationError, + InvalidConfigFormatError, + SchemaGenerationError, +) +from configforge.formatters import JSONHandler, TypeScriptHandler, YAMLHandler +from configforge.utils.file_parser import detect_format + + +@click.command("generate") +@click.argument("input_file", type=click.Path(exists=True)) +@click.option( + "--output", + "-o", + type=click.Path(), + help="Output file path (default: stdout)", +) +@click.option( + "--interface-name", + "-n", + default="Config", + help="Name for the generated TypeScript interface", +) +@click.option( + "--from-data", + is_flag=True, + default=False, + help="Generate TypeScript interface from sample data instead of schema", +) +def generate( + input_file: str, + output: Optional[str], + interface_name: str, + from_data: bool, +) -> None: + """Generate TypeScript interfaces from JSON Schema or config data.""" + try: + format_type = detect_format(input_file) + + if format_type not in ("json", "yaml", "yml"): + raise InvalidConfigFormatError( + f"Unsupported format: {format_type}. Use JSON or YAML." + ) + + if from_data: + data = _read_data(input_file, format_type) + ts_output = TypeScriptHandler.generate_from_data(data, interface_name) + else: + schema = _read_schema(input_file, format_type) + ts_output = TypeScriptHandler.generate_from_schema(schema, interface_name) + + if output: + _write_output(output, ts_output) + click.echo(f"Generated TypeScript interface -> {output}") + else: + click.echo(ts_output) + + except (InvalidConfigFormatError, FileOperationError, SchemaGenerationError) as e: + click.echo(f"Error: {e.message}", err=True) + click.get_current_context().exit(1) + + +def _read_data(filepath: str, format_type: str) -> dict: + """Read configuration data from file.""" + if format_type == "json": + return JSONHandler.read(filepath) + else: + return YAMLHandler.read(filepath) + + +def _read_schema(filepath: str, format_type: str) -> dict: + """Read JSON Schema from file.""" + if format_type == "json": + return JSONHandler.read(filepath) + else: + return YAMLHandler.read(filepath) + + +def _write_output(filepath: str, content: str) -> None: + """Write content to output file.""" + try: + with open(filepath, "w", encoding="utf-8") as f: + f.write(content) + except PermissionError: + raise FileOperationError(f"Permission denied: {filepath}", filepath=filepath) + except OSError as e: + raise FileOperationError(f"Failed to write {filepath}: {str(e)}", filepath=filepath)