From 58b0c3471a7a2377eff02d92002cf08731aec5dc Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Sun, 1 Feb 2026 20:49:01 +0000 Subject: [PATCH] Initial upload with full project structure --- app/src/confgen/validator.py | 101 +++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 app/src/confgen/validator.py diff --git a/app/src/confgen/validator.py b/app/src/confgen/validator.py new file mode 100644 index 0000000..6715c0c --- /dev/null +++ b/app/src/confgen/validator.py @@ -0,0 +1,101 @@ +"""Schema validation for generated configurations.""" + +from pathlib import Path +from typing import Any + + +class SchemaValidator: + """JSON Schema validator for configuration validation.""" + + def __init__(self, schema_path: str): + self.schema_path = Path(schema_path) + self._schema = None + + @property + def schema(self) -> dict[str, Any]: + """Load and cache the schema.""" + if self._schema is None: + with open(self.schema_path) as f: + import json + + self._schema = json.load(f) + return self._schema + + def validate(self, data: dict[str, Any]) -> tuple[bool, list[str]]: + """Validate data against the schema.""" + from jsonschema import Draft7Validator + + validator = Draft7Validator(self.schema) + errors = [] + + for error in validator.iter_errors(data): + errors.append(self._format_error(error)) + + return len(errors) == 0, errors + + def _format_error(self, error) -> str: + """Format a validation error for display.""" + path = " -> ".join(str(p) for p in error.absolute_path) + message = error.message + + if error.validator == "type": + message = f"expected {error.validator_value}, got {type(error.instance).__name__}" + elif error.validator == "enum": + message = f"must be one of {error.validator_value}" + elif error.validator == "minimum": + message = f"minimum value is {error.validator_value}" + elif error.validator == "maximum": + message = f"maximum value is {error.validator_value}" + elif error.validator == "minLength": + message = f"must have at least {error.validator_value} characters" + elif error.validator == "maxLength": + message = f"must have at most {error.validator_value} characters" + elif error.validator == "pattern": + message = f"must match pattern: {error.validator_value}" + + return f"[{path}] {message}" + + def validate_file(self, config_path: str) -> tuple[bool, list[str]]: + """Validate a configuration file against the schema.""" + from .parsers import ConfigParser + + parser = ConfigParser() + data = parser.load_file(config_path) + return self.validate(data) + + def get_schema_summary(self) -> dict[str, Any]: + """Get a summary of the schema.""" + summary = { + "type": self.schema.get("type", "object"), + "required": self.schema.get("required", []), + "properties": {}, + } + + for name, prop in self.schema.get("properties", {}).items(): + prop_info = { + "type": prop.get("type", "any"), + } + + if "enum" in prop: + prop_info["enum"] = prop["enum"] + if "default" in prop: + prop_info["default"] = prop["default"] + if "description" in prop: + prop_info["description"] = prop["description"] + + summary["properties"][name] = prop_info + + return summary + + def check_property_exists(self, data: dict[str, Any], property: str) -> bool: + """Check if a property exists in the data.""" + keys = property.split(".") + current = data + + for key in keys: + if isinstance(current, dict) and key in current: + current = current[key] + else: + return False + + return True