@startuml

abstract class Validator {
    + validate(name: string, val: mixed): array {abstract}
    + then(other: Validator): Validator
}

class ComposedValidator extends Validator {

    + __construct(first: Validator, then: Validator)
    + validate(name: string, val: mixed): array
}

ComposedValidator -->"- first" Validator
ComposedValidator -->"- then" Validator


class SimpleFunctionValidator extends Validator {
    - predicate: callable
    - error_factory: callable

    + __construct(predicate: callable, errorsFactory: callable)
    + validate(name: string, val: mixed): array
}

class ValidationFail implements JsonSerialize {
    - kind: string
    - message: string

    + __construct(kind: string, message: string)
    + getMessage(): string
    + getKind(): string
    + <u>notFound(message: string): ValidationFail
    + <u>unauthorized(message:string): ValidationFail
    + <u>error(message:string): ValidationFail
}

class FieldValidationFail extends ValidationFail {
    - fieldName: string
    + __construct(fieldName: string, message: string)
    + getFieldName(): string
    + jsonSerialize()

    + <u>invalidChars(fieldName: string): FieldValidationFail
    + <u>empty(fieldName: string): FieldValidationFail
    + <u>missing(fieldName: string): FieldValidationFail
}


class Validation {
    <u> + validate(val: mixed, valName: string, failures: &array, validators: Validator...): bool
}

Validation ..> Validator

class DefaultValidators {
    + <u>nonEmpty(): Validator
    + <u>shorterThan(limit: int): Validator
    + <u>userString(maxLen: int): Validator
    + <u>regex(regex:string, msg:string): Validator
    + <u>hex(msg:string): Validator
    + <u>name(msg:string): Validator
    + <u>nameWithSpaces(): Validator
    + <u>lenBetween(min:int, max:int): Validator
    + <u>email(msg:string): Validator
    + <u>isInteger(): Validator
    + <u>isIntInRange(min:int, max:int): Validator
    + <u>isURL(): Validator
}

DefaultValidators ..> Validator

class FunctionValidator{
    - validate_fn: callable
    + __construct(validate_fn:callable)
    + validate(name:string, val:mixed): array
}

Validator <|-- FunctionValidator

@enduml