# - * -coding: utf - 8 - * -
"""
@author: ☙ Ryan McConnell ❧
"""
from collections import defaultdict
from dataclasses import dataclass
from typing import Callable, MutableMapping, Type, Iterable
VALIDATION_FIX_SIGNATURE = Callable[[MutableMapping, str], bool]
VALIDATION_FUNCTION_SIGNATURE = Callable[[MutableMapping, str, ...], VALIDATION_FIX_SIGNATURE | None]
[docs]@dataclass(slots=True, frozen=True)
class ValidationReport(object):
settings: MutableMapping
settings_key: str
name: str
description: str
long_description: str
fixes: list[VALIDATION_FIX_SIGNATURE] | None = None
[docs]class Validation:
def __init__(self, key: str):
self.settings_key = key
[docs] def validate(self, settings: MutableMapping, settings_key: str) -> ValidationReport | None:
return None
[docs] def make_validation_report(self, *args):
return ValidationReport(*args)
[docs]class ValidationCapsule(Validation):
validate: VALIDATION_FUNCTION_SIGNATURE
make_validation_report: Callable[..., ValidationReport] = ValidationReport
[docs]class NotNoneValidation(Validation):
[docs] def validate(self, settings: MutableMapping, settings_key: str) -> ValidationReport | None:
if settings[settings_key] is None:
return self.make_validation_report(settings, settings_key, 'NoneType Error',
'Parameter has a value of None',
f'Parameter {settings_key} in settings object {settings} has a value of None')
[docs]class TypeCheckValidation(Validation):
def __init__(self, key: str, _type: Type):
super().__init__(key)
self.check_type = _type
[docs] def validate(self, settings: MutableMapping, settings_key: str) -> ValidationReport | None:
sot = type(settings[settings_key])
check_type = self.check_type
if sot is not check_type:
return ValidationReport(settings, settings_key, 'TypeError',
f'Parameter is expected to be {check_type.__class__.__name__}',
(f'Parameter {settings_key} of {settings} is expected to be of type {check_type}, '
'but instead is of type {sot}'))
[docs]class SettingsValidator(object):
def __init__(self, settings: MutableMapping):
self.settings = settings
self.validations: list[Validation] = []
[docs] def create_validation(self, name: str, validation_function: VALIDATION_FUNCTION_SIGNATURE,
v_t: Type[Validation] = Validation):
self.add_validation(v_t(name, validation_function))
[docs] def create_validations(self, validations: list[tuple[str, VALIDATION_FUNCTION_SIGNATURE]],
v_t: Type[Validation] = Validation):
self.add_validations(v_t(*x) for x in validations)
[docs] def add_validation(self, validation: Validation):
self.validations.append(validation)
[docs] def add_validations(self, validations: Iterable[Validation]):
self.validations.extend(validations)
[docs] def get_validation_issues(self) -> dict:
issues = defaultdict(list)
for validation in self.validations:
settings_key = validation.settings_key
if vr := validation.validate(self.settings, settings_key):
issues[settings_key].append(vr)
return issues