Add commands and formatters modules
Some checks failed
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / test (3.8) (push) Has been cancelled
CI / test (3.9) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / build-package (push) Has been cancelled
Some checks failed
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / test (3.8) (push) Has been cancelled
CI / test (3.9) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / build-package (push) Has been cancelled
This commit is contained in:
120
configforge/commands/convert.py
Normal file
120
configforge/commands/convert.py
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
"""Convert command for ConfigForge."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
from configforge.exceptions import (
|
||||||
|
ConversionError,
|
||||||
|
FileOperationError,
|
||||||
|
InvalidConfigFormatError,
|
||||||
|
)
|
||||||
|
from configforge.formatters import JSONHandler, YAMLHandler, TOMLHandler, ENVHandler
|
||||||
|
from configforge.utils.file_parser import detect_format
|
||||||
|
|
||||||
|
|
||||||
|
OUTPUT_FORMAT_MAP = {
|
||||||
|
"json": ("json", JSONHandler),
|
||||||
|
"yaml": ("yaml", YAMLHandler),
|
||||||
|
"toml": ("toml", TOMLHandler),
|
||||||
|
"env": ("env", ENVHandler),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("convert")
|
||||||
|
@click.argument("input_file", type=click.Path(exists=True))
|
||||||
|
@click.option(
|
||||||
|
"--output",
|
||||||
|
"-o",
|
||||||
|
type=click.Path(),
|
||||||
|
help="Output file path (default: stdout)",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--format",
|
||||||
|
"-f",
|
||||||
|
type=click.Choice(["json", "yaml", "toml", "env"]),
|
||||||
|
help="Output format (default: auto-detect from extension)",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--indent",
|
||||||
|
is_flag=True,
|
||||||
|
default=False,
|
||||||
|
help="Use indented output for JSON",
|
||||||
|
)
|
||||||
|
def convert(
|
||||||
|
input_file: str,
|
||||||
|
output: Optional[str],
|
||||||
|
format: Optional[str],
|
||||||
|
indent: bool,
|
||||||
|
) -> None:
|
||||||
|
"""Convert a configuration file between formats."""
|
||||||
|
try:
|
||||||
|
input_format = detect_format(input_file)
|
||||||
|
|
||||||
|
if format:
|
||||||
|
output_format = format
|
||||||
|
elif output:
|
||||||
|
output_format = detect_format(output)
|
||||||
|
else:
|
||||||
|
raise ConversionError(
|
||||||
|
"Output format must be specified via --format or output file extension"
|
||||||
|
)
|
||||||
|
|
||||||
|
data = _read_input(input_file, input_format)
|
||||||
|
content = _format_output(data, output_format, indent)
|
||||||
|
|
||||||
|
if output:
|
||||||
|
_write_output(output, content)
|
||||||
|
click.echo(f"Converted {input_file} ({input_format}) -> {output} ({output_format})")
|
||||||
|
else:
|
||||||
|
click.echo(content)
|
||||||
|
|
||||||
|
except (InvalidConfigFormatError, FileOperationError, ConversionError) as e:
|
||||||
|
click.echo(f"Error: {e.message}", err=True)
|
||||||
|
click.get_current_context().exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def _read_input(filepath: str, format_type: str) -> dict:
|
||||||
|
"""Read and parse input file."""
|
||||||
|
handlers = {
|
||||||
|
"json": JSONHandler,
|
||||||
|
"yaml": YAMLHandler,
|
||||||
|
"toml": TOMLHandler,
|
||||||
|
"env": ENVHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
handler = handlers.get(format_type)
|
||||||
|
if not handler:
|
||||||
|
raise InvalidConfigFormatError(f"Unsupported format: {format_type}")
|
||||||
|
|
||||||
|
return handler.read(filepath)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_output(data: dict, format_type: str, indent: bool = False) -> str:
|
||||||
|
"""Format data to output string."""
|
||||||
|
handlers = {
|
||||||
|
"json": lambda d: JSONHandler.dumps(d, indent=2 if indent else 0),
|
||||||
|
"yaml": lambda d: YAMLHandler.dumps(d),
|
||||||
|
"toml": lambda d: TOMLHandler.dumps(d),
|
||||||
|
"env": lambda d: ENVHandler.dumps(d),
|
||||||
|
}
|
||||||
|
|
||||||
|
handler = handlers.get(format_type)
|
||||||
|
if not handler:
|
||||||
|
raise InvalidConfigFormatError(f"Unsupported output format: {format_type}")
|
||||||
|
|
||||||
|
return handler(data)
|
||||||
|
|
||||||
|
|
||||||
|
def _write_output(filepath: str, content: str) -> None:
|
||||||
|
"""Write content to output file."""
|
||||||
|
try:
|
||||||
|
os.makedirs(os.path.dirname(filepath) if os.path.dirname(filepath) else ".", exist_ok=True)
|
||||||
|
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)
|
||||||
Reference in New Issue
Block a user