Introduce the core of the validation-rules idiom: a Check<Subject> with a positive description, a when predicate, and a Bool or multi-error check that fires only on values of its Subject type wherever they occur. AnyCheck erases the subject and dispatches by runtime type, guarding an optional or a wrong type from matching. The validator gains a checks list, a validating(_:) overload, and a traversal that offers each node the values it may specialise on. BuiltinCheck.mappingKeysAreUnique is the type-directed counterpart of the legacy rule. The machinery has its own negative tests (wrong type, optional, false predicate yield nothing; same type twice yields two errors) and the builtin ships a failing and a near-miss test. The coverage gate catches the five new public types, which are classified as validation machinery. The default validator is unchanged, so this is additive; retiring the legacy Rule, moving the default onto the check, the combinator algebra, the error rendering, and de-duplicating the Tagged validator are the remaining steps.
28 lines
1.1 KiB
Swift
28 lines
1.1 KiB
Swift
public extension PureYAML.Validation {
|
|
/// A type-erased `Check`, so checks specialised on different subject types live in one list. It
|
|
/// filters by runtime type: a `Check<Subject>` fires only on a `Subject` value, never on an
|
|
/// optional of it (the `type(of:)` guard) or a value of any other type.
|
|
struct AnyCheck: Sendable {
|
|
public let description: String
|
|
private let applyErased: @Sendable (any Validatable, Path, PureYAML.Model.Value) -> [Issue]
|
|
|
|
public init<Subject: Validatable>(_ check: Check<Subject>) {
|
|
description = check.description
|
|
applyErased = { subject, path, document in
|
|
guard let typed = subject as? Subject, type(of: subject) == Subject.self else {
|
|
return []
|
|
}
|
|
return check.apply(SubjectContext(document: document, subject: typed, path: path))
|
|
}
|
|
}
|
|
|
|
public func apply(
|
|
to subject: any Validatable,
|
|
at path: Path,
|
|
in document: PureYAML.Model.Value,
|
|
) -> [Issue] {
|
|
applyErased(subject, path, document)
|
|
}
|
|
}
|
|
}
|