"""Type validators for environment variable values.""" import re from typing import Optional from envschema.schema import EnvVarType class ValidationError: """Represents a validation error.""" def __init__(self, message: str, value: Optional[str] = None): self.message = message self.value = value def __str__(self) -> str: if self.value is not None: return f"{self.message} (got: {self.value!r})" return self.message class StringValidator: """Validator for string type - always passes.""" @staticmethod def validate(value: Optional[str]) -> tuple[bool, Optional[ValidationError]]: if value is None: return True, None return True, None class IntegerValidator: """Validator for integer type.""" @staticmethod def validate(value: Optional[str]) -> tuple[bool, Optional[ValidationError]]: if value is None: return True, None try: int(value) return True, None except ValueError: return False, ValidationError( f"Invalid integer value", value=value ) class BooleanValidator: """Validator for boolean type.""" TRUE_VALUES = {"true", "1", "yes", "on"} FALSE_VALUES = {"false", "0", "no", "off"} @staticmethod def validate(value: Optional[str]) -> tuple[bool, Optional[ValidationError]]: if value is None: return True, None value_lower = value.lower().strip() if value_lower in BooleanValidator.TRUE_VALUES: return True, None if value_lower in BooleanValidator.FALSE_VALUES: return True, None return False, ValidationError( f"Invalid boolean value (expected: true, false, 1, 0, yes, no, on, off)", value=value ) class ListValidator: """Validator for list type (comma-separated values).""" @staticmethod def validate(value: Optional[str]) -> tuple[bool, Optional[ValidationError]]: if value is None: return True, None if "," in value: return True, None return False, ValidationError( f"Invalid list value (expected comma-separated values)", value=value ) @staticmethod def parse(value: str) -> list[str]: """Parse a comma-separated string into a list. Args: value: Comma-separated string. Returns: List of values. """ return [item.strip() for item in value.split(",") if item.strip()] class PatternValidator: """Validator for pattern/regex validation.""" @staticmethod def validate(value: Optional[str], pattern: str) -> tuple[bool, Optional[ValidationError]]: if value is None: return True, None try: if re.match(pattern, value): return True, None return False, ValidationError( f"Value does not match pattern: {pattern}", value=value ) except re.error: return False, ValidationError( f"Invalid regex pattern: {pattern}", value=value ) def get_validator(var_type: EnvVarType): """Get the validator class for a given type. Args: var_type: The environment variable type. Returns: Validator class. """ validators = { EnvVarType.STRING: StringValidator, EnvVarType.INTEGER: IntegerValidator, EnvVarType.BOOLEAN: BooleanValidator, EnvVarType.LIST: ListValidator, } return validators.get(var_type, StringValidator) def validate_value(value: Optional[str], var_type: EnvVarType, pattern: Optional[str] = None) -> tuple[bool, Optional[ValidationError]]: """Validate a value against a type and optional pattern. Args: value: The value to validate. var_type: The expected type. pattern: Optional regex pattern. Returns: Tuple of (is_valid, error). """ validator = get_validator(var_type) is_valid, error = validator.validate(value) if is_valid and pattern and value is not None: is_valid, error = PatternValidator.validate(value, pattern) return is_valid, error