154 lines
4.2 KiB
Python
154 lines
4.2 KiB
Python
"""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(
|
|
"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(
|
|
"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(
|
|
"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
|