Initial upload: config-converter-cli v1.0.0
This commit is contained in:
135
configconverter/validators.py
Normal file
135
configconverter/validators.py
Normal file
@@ -0,0 +1,135 @@
|
||||
"""Validation module for config-converter-cli."""
|
||||
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from rich.console import Console
|
||||
from rich.syntax import Syntax
|
||||
from rich.text import Text
|
||||
|
||||
from configconverter.converters import Converter
|
||||
from configconverter.exceptions import ParseError, ValidationError
|
||||
|
||||
|
||||
class Validator:
|
||||
"""Validates configuration files for syntax errors."""
|
||||
|
||||
def __init__(self):
|
||||
self.converter = Converter()
|
||||
self.console = Console()
|
||||
|
||||
def validate(
|
||||
self, content: str, format: Optional[str] = None
|
||||
) -> Tuple[bool, Optional[ParseError]]:
|
||||
"""Validate content for syntax errors.
|
||||
|
||||
Args:
|
||||
content: The content to validate
|
||||
format: Optional format hint (json, yaml, toml)
|
||||
|
||||
Returns:
|
||||
Tuple of (is_valid, error)
|
||||
"""
|
||||
try:
|
||||
if format:
|
||||
detected_format = format
|
||||
else:
|
||||
detected_format = self.converter.detect_format(content)
|
||||
|
||||
data = self.converter._parse(content, detected_format)
|
||||
return True, None
|
||||
except ParseError as e:
|
||||
return False, e
|
||||
except Exception as e:
|
||||
return False, ParseError(f"Validation failed: {e}")
|
||||
|
||||
def validate_file(
|
||||
self, file_path: str, format: Optional[str] = None
|
||||
) -> Tuple[bool, Optional[ParseError]]:
|
||||
"""Validate a file for syntax errors.
|
||||
|
||||
Args:
|
||||
file_path: Path to the file
|
||||
format: Optional format hint (json, yaml, toml)
|
||||
|
||||
Returns:
|
||||
Tuple of (is_valid, error)
|
||||
"""
|
||||
try:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
return self.validate(content, format)
|
||||
except FileNotFoundError:
|
||||
return False, ParseError(f"File not found: {file_path}")
|
||||
except Exception as e:
|
||||
return False, ParseError(f"Failed to read file: {e}")
|
||||
|
||||
def format_error_message(
|
||||
self, error: ParseError, content: str
|
||||
) -> str:
|
||||
"""Format a parse error with syntax highlighting.
|
||||
|
||||
Args:
|
||||
error: The parse error
|
||||
content: The original content
|
||||
|
||||
Returns:
|
||||
Formatted error message
|
||||
"""
|
||||
lines = content.split("\n")
|
||||
|
||||
if error.line_number is not None:
|
||||
line_idx = error.line_number - 1
|
||||
if 0 <= line_idx < len(lines):
|
||||
context_lines = 2
|
||||
start = max(0, line_idx - context_lines)
|
||||
end = min(len(lines), line_idx + context_lines + 1)
|
||||
|
||||
snippet_lines = []
|
||||
for i in range(start, end):
|
||||
prefix = ">>> " if i == line_idx else " "
|
||||
snippet_lines.append(f"{prefix}{i + 1:4d} | {lines[i]}")
|
||||
|
||||
snippet = "\n".join(snippet_lines)
|
||||
return f"Parse Error at line {error.line_number}:\n{snippet}\n\n{error.message}"
|
||||
|
||||
return f"Parse Error: {error.message}"
|
||||
|
||||
def print_error(
|
||||
self, error: ParseError, content: str, file_path: Optional[str] = None
|
||||
) -> None:
|
||||
"""Print a validation error with colored output.
|
||||
|
||||
Args:
|
||||
error: The parse error
|
||||
content: The original content
|
||||
file_path: Optional file path for header
|
||||
"""
|
||||
header = f"[bold red]Error[/bold red]"
|
||||
if file_path:
|
||||
header += f" in [cyan]{file_path}[/cyan]"
|
||||
|
||||
self.console.print(header)
|
||||
|
||||
if error.line_number is not None:
|
||||
lines = content.split("\n")
|
||||
line_idx = error.line_number - 1
|
||||
|
||||
if 0 <= line_idx < len(lines):
|
||||
self.console.print(
|
||||
f"Line {error.line_number}: [red]{error.message}[/red]"
|
||||
)
|
||||
self.console.print()
|
||||
|
||||
syntax = Syntax(
|
||||
lines[line_idx],
|
||||
"python",
|
||||
line_numbers=True,
|
||||
start_line=error.line_number,
|
||||
highlight_lines={error.line_number},
|
||||
)
|
||||
self.console.print(syntax)
|
||||
else:
|
||||
self.console.print(f"[red]{error.message}[/red]")
|
||||
|
||||
|
||||
validator = Validator()
|
||||
Reference in New Issue
Block a user