From af95b69646bac4cc00cd32acdd3e8f1eabd8bde0 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Sat, 31 Jan 2026 07:10:13 +0000 Subject: [PATCH] Initial upload: ConfDoc v0.1.0 - Config validation and documentation generator --- src/confdoc/validator/errors.py | 148 ++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 src/confdoc/validator/errors.py diff --git a/src/confdoc/validator/errors.py b/src/confdoc/validator/errors.py new file mode 100644 index 0000000..ae19ba5 --- /dev/null +++ b/src/confdoc/validator/errors.py @@ -0,0 +1,148 @@ +from typing import Any, Dict, List, Optional +from enum import Enum + + +class ErrorSeverity(Enum): + """Classification of validation error severity.""" + CRITICAL = "critical" + ERROR = "error" + WARNING = "warning" + INFO = "info" + + +class ValidationError: + """Represents a validation error with location and severity.""" + + def __init__( + self, + message: str, + path: Optional[str] = None, + line: Optional[int] = None, + column: Optional[int] = None, + severity: ErrorSeverity = ErrorSeverity.ERROR, + suggestion: Optional[str] = None, + validator: Optional[str] = None, + ): + self.message = message + self.path = path + self.line = line + self.column = column + self.severity = severity + self.suggestion = suggestion + self.validator = validator + + def to_dict(self) -> Dict[str, Any]: + """Convert error to dictionary.""" + return { + "message": self.message, + "path": self.path, + "line": self.line, + "column": self.column, + "severity": self.severity.value, + "suggestion": self.suggestion, + "validator": self.validator, + } + + def __str__(self) -> str: + """Format error as string.""" + location = "" + if self.path: + location = f" in '{self.path}'" + elif self.line: + location = f" at line {self.line}" + if self.column: + location += f", column {self.column}" + + result = f"{self.message}{location}" + if self.suggestion: + result += f". Suggestion: {self.suggestion}" + + return result + + def __repr__(self) -> str: + return f"ValidationError(message='{self.message}', path='{self.path}', severity={self.severity.value})" + + +class ErrorFormatter: + """Formats validation errors for display.""" + + @staticmethod + def format_error(error: ValidationError, color: bool = True) -> str: + """Format a single error.""" + prefix = "" + if color: + if error.severity == ErrorSeverity.CRITICAL: + prefix = "[red][CRITICAL][/red] " + elif error.severity == ErrorSeverity.ERROR: + prefix = "[red]✗[/red] " + elif error.severity == ErrorSeverity.WARNING: + prefix = "[yellow]![/yellow] " + else: + prefix = "[blue]i[/blue] " + + location = "" + if error.path: + location = f" [dim]({error.path})[/dim]" + elif error.line: + location = f" [dim](line {error.line})[/dim]" + + suggestion = "" + if error.suggestion: + suggestion = f"\n [dim]→ {error.suggestion}[/dim]" + + return f"{prefix}{error.message}{location}{suggestion}" + + @staticmethod + def format_errors(errors: List[ValidationError], color: bool = True) -> str: + """Format a list of errors.""" + if not errors: + return "" + + lines = [] + for i, error in enumerate(errors, 1): + lines.append(f"{i}. {ErrorFormatter.format_error(error, color)}") + + return "\n".join(lines) + + @staticmethod + def classify_severity(validator_error: Dict[str, Any]) -> ErrorSeverity: + """Classify the severity of a jsonschema error.""" + validator = validator_error.get("validator", "") + + if validator in ("required", "additionalProperties"): + return ErrorSeverity.CRITICAL + elif validator in ("type", "enum"): + return ErrorSeverity.ERROR + elif validator in ("minimum", "maximum", "minLength", "maxLength", "pattern"): + return ErrorSeverity.WARNING + else: + return ErrorSeverity.INFO + + @staticmethod + def generate_suggestion(validator_error: Dict[str, Any]) -> Optional[str]: + """Generate a suggestion for fixing a validation error.""" + validator = validator_error.get("validator", "") + validator_value = validator_error.get("validator_value", "") + + if validator == "required": + missing = validator_error.get("missing_property", "") + return f"Add the required property '{missing}' to your configuration." + elif validator == "type": + expected_type = validator_error.get("expected", "") + actual_type = validator_error.get("found", "") + return f"Expected type {expected_type}, but got {actual_type}." + elif validator == "enum": + allowed = ", ".join(validator_value) if isinstance(validator_value, list) else str(validator_value) + return f"Value must be one of: {allowed}" + elif validator == "minimum": + return f"Value must be greater than or equal to {validator_value}." + elif validator == "maximum": + return f"Value must be less than or equal to {validator_value}." + elif validator == "minLength": + return f"Value must have at least {validator_value} characters." + elif validator == "maxLength": + return f"Value must have at most {validator_value} characters." + elif validator == "pattern": + return f"Value must match the pattern: {validator_value}" + + return None