A Rule was exactly a Check<Model.Value, Model.Value>: it fired on every node, switched on the value itself, and carried the root. It was therefore a second copy of the one check machinery, the duplication the validation idiom exists to prevent (principle 10), now also resolved for the tagged layer. Remove Rule and its Context. The model validator holds only checks; its default is the type-directed BuiltinCheck.mappingKeysAreUnique; validating takes a Check. Custom whole-tree rules become Check<Model.Value, Model.Value> with the former context.root now context.document. Behaviour is byte-identical: the mapping-key builtin reports the same issues at the same paths in the same DFS order, so the public validate/collect/blank surface is unchanged. All 399 tests pass; the coverage gate reconciles to 60 public types.
36 lines
1.5 KiB
Swift
36 lines
1.5 KiB
Swift
public extension PureYAML.Validation {
|
|
/// The caseless namespace of built-in type-directed checks, referenceable by name.
|
|
enum BuiltinCheck {
|
|
/// Reports each duplicate key within a single mapping. The type-directed counterpart of the
|
|
/// legacy `Rule.mappingKeysAreUnique`; fires on `Model.Mapping` values directly rather than
|
|
/// switching on a `Model.Value`.
|
|
public static var mappingKeysAreUnique: Check<PureYAML.Model.Mapping, PureYAML.Model.Value> {
|
|
Check(description: "Mapping keys are unique") { context in
|
|
var seen = Set<PureYAML.Model.Key>()
|
|
var issues: [Issue] = []
|
|
for pair in context.subject.pairs where !seen.insert(pair.keyNode).inserted {
|
|
issues.append(Issue(
|
|
severity: .error,
|
|
reason: "Duplicate mapping key '\(pair.keyNode.description)'",
|
|
path: context.path.appending(pair.keyNode.pathComponent),
|
|
))
|
|
}
|
|
return issues
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension PureYAML.Model.Key {
|
|
/// The path component a duplicate-key issue points at: a plain `.key` for a string key, a
|
|
/// `.complexKey` for a sequence or mapping key (which has no string spelling).
|
|
var pathComponent: PureYAML.Validation.Path.Component {
|
|
switch self {
|
|
case let .string(value):
|
|
.key(value)
|
|
case .mapping, .sequence:
|
|
.complexKey(self)
|
|
}
|
|
}
|
|
}
|