diff --git a/src/veritext/validators/composite.py b/src/veritext/validators/composite.py new file mode 100644 index 0000000..ab608ca --- /dev/null +++ b/src/veritext/validators/composite.py @@ -0,0 +1,90 @@ +"""Composite validators for combining multiple checks.""" + +from veritext.core.types import CheckResult, ValidationContext, ValidationResult +from veritext.validators.base import Check + + +class AllOf: + """Passes only if all checks pass.""" + + def __init__(self, checks: list[Check]) -> None: + """ + Initialise the AllOf composite validator. + + Args: + checks: List of checks that must all pass. + + Raises: + ValueError: If checks list is empty. + """ + if not checks: + raise ValueError("checks list cannot be empty") + + self._checks = checks + + @property + def name(self) -> str: + """Return the name of this composite check.""" + return "all_of" + + def check(self, text: str, context: ValidationContext) -> ValidationResult: + """ + Run all checks and return aggregate result. + + Args: + text: The text to validate. + context: Validation context containing reference text and metadata. + + Returns: + ValidationResult that passes only if all checks pass. + """ + results: list[CheckResult] = [] + for check in self._checks: + results.append(check.check(text, context)) + + all_passed = all(r.passed for r in results) + + return ValidationResult(passed=all_passed, checks=results) + + +class AnyOf: + """Passes if any check passes.""" + + def __init__(self, checks: list[Check]) -> None: + """ + Initialise the AnyOf composite validator. + + Args: + checks: List of checks where at least one must pass. + + Raises: + ValueError: If checks list is empty. + """ + if not checks: + raise ValueError("checks list cannot be empty") + + self._checks = checks + + @property + def name(self) -> str: + """Return the name of this composite check.""" + return "any_of" + + def check(self, text: str, context: ValidationContext) -> ValidationResult: + """ + Run all checks and return aggregate result. + + Args: + text: The text to validate. + context: Validation context containing reference text and metadata. + + Returns: + ValidationResult that passes if any check passes. + """ + results: list[CheckResult] = [] + for check in self._checks: + results.append(check.check(text, context)) + + any_passed = any(r.passed for r in results) + + return ValidationResult(passed=any_passed, checks=results)