PureYAMLJSON is a separate library product layered on the core engine. JSON is a subset of YAML 1.2, so the existing parser already reads both into one value tree; this product adds the missing half and the value-level policy: - JSON.Writer: a pure-Swift RFC-8259 serializer (no Foundation; WASI-clean), pretty or compact, rejecting non-string keys and non-finite numbers. - JSON representability rules expressed in PureYAML's own Validation framework: mappingKeysAreStrings and numbersAreFinite join the core mappingKeysAreUnique. - JSON.Validator: the representability rule set plus issues(for:)/isRepresentable. - JSON.Convert: standalone convert(_:to:) with the round-trip law (parse -> validate representability -> emit -> re-parse must equal the source). To JSON rejects complex keys and non-finite numbers; to YAML rejects duplicate keys, which are legal in JSON but not in YAML. 19 tests cover the writer, the rules, and both conversion directions including the dual-rejection edges. Carved out of the catch-all Sources/Tests paths via exclude so the core PureYAML target is unchanged.
53 lines
2.0 KiB
Swift
53 lines
2.0 KiB
Swift
import PureYAML
|
|
|
|
public extension PureYAML.Validation.Rule {
|
|
/// Reports any mapping key that is not a string. JSON object names must be
|
|
/// strings, so a YAML complex key (a sequence or mapping used as a key) has no
|
|
/// JSON form. PureYAML core already ships ``mappingKeysAreUnique``; this rule and
|
|
/// ``numbersAreFinite`` complete the JSON-representability set.
|
|
static var mappingKeysAreStrings: Self {
|
|
Self(description: "Mapping keys are strings") { context in
|
|
guard case let .mapping(mapping) = context.subject else {
|
|
return []
|
|
}
|
|
return mapping.pairs.compactMap { pair in
|
|
guard pair.keyNode.stringValue == nil else {
|
|
return nil
|
|
}
|
|
return PureYAML.Validation.Issue(
|
|
severity: .error,
|
|
reason: "Mapping key '\(pair.keyNode.description)' is not a string",
|
|
path: context.path.appending(.complexKey(pair.keyNode)),
|
|
)
|
|
}
|
|
} when: { context in
|
|
if case .mapping = context.subject {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Reports any non-finite number (NaN or infinity). JSON has no literal for
|
|
/// these, so a value tree carrying one is not representable as JSON.
|
|
static var numbersAreFinite: Self {
|
|
Self(description: "Numbers are finite") { context in
|
|
guard case let .double(number) = context.subject, !number.isFinite else {
|
|
return []
|
|
}
|
|
return [
|
|
PureYAML.Validation.Issue(
|
|
severity: .error,
|
|
reason: "Number '\(number)' is not finite",
|
|
path: context.path,
|
|
),
|
|
]
|
|
} when: { context in
|
|
if case .double = context.subject {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|