PureYAML/Sources/Validation/AnyCheck.swift
Mihaela M 2e45ecaffe feat(validation): add type-directed Check dispatch
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.
2026-06-28 07:23:23 +02:00

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)
}
}
}