style(schema): satisfy swiftlint on the XSTS conformance additions
The conformance fixes tripped swiftlint --strict (it had been run only as swiftformat): the test helper binding `s` is too short (now `doc`), the duration reference dateTimes were a three-member tuple (now (year, month) with day fixed at 1), and DurationValue.Order nested two levels deep (now the sibling DurationOrder). No behavior change; full suite and XSTS stay green.
This commit is contained in:
parent
a0ccd017f5
commit
f94b2e43e0
@ -1,4 +1,7 @@
|
||||
extension PureXML.Schema {
|
||||
/// The outcome of comparing two durations, which form a partial order.
|
||||
enum DurationOrder { case lessThan, equal, greaterThan, incomparable }
|
||||
|
||||
/// An `xs:duration` as its two independent signed components, months and
|
||||
/// seconds, with the XSD 1.0 order relation. Because a month is 28 to 31
|
||||
/// days, the order is partial: P and Q compare by adding each to the four
|
||||
@ -10,14 +13,13 @@ extension PureXML.Schema {
|
||||
let months: Int
|
||||
let seconds: Double
|
||||
|
||||
enum Order { case lessThan, equal, greaterThan, incomparable }
|
||||
|
||||
/// The four reference dateTimes (year, month, day) at T00:00:00Z.
|
||||
private static let references: [(year: Int, month: Int, day: Int)] = [
|
||||
(1696, 9, 1), (1697, 2, 1), (1903, 3, 1), (1903, 7, 1),
|
||||
/// The four reference points (year, month); each is the first of its
|
||||
/// month at T00:00:00Z, so the day is always 1 and need not be carried.
|
||||
private static let references: [(year: Int, month: Int)] = [
|
||||
(1696, 9), (1697, 2), (1903, 3), (1903, 7),
|
||||
]
|
||||
|
||||
func compare(to other: DurationValue) -> Order {
|
||||
func compare(to other: DurationValue) -> DurationOrder {
|
||||
var sawLess = false
|
||||
var sawGreater = false
|
||||
var sawEqual = false
|
||||
@ -40,11 +42,11 @@ extension PureXML.Schema {
|
||||
return .incomparable
|
||||
}
|
||||
|
||||
/// The instant, in seconds, of `reference` + (months, seconds): add the
|
||||
/// months with calendar rollover (clamping the day to the resulting
|
||||
/// month's length, per the spec), then add the seconds component.
|
||||
/// The instant, in seconds, of `reference` (day 1) + (months, seconds):
|
||||
/// add the months with calendar rollover (clamping the day to the
|
||||
/// resulting month's length, per the spec), then add the seconds.
|
||||
private static func instant(
|
||||
_ reference: (year: Int, month: Int, day: Int),
|
||||
_ reference: (year: Int, month: Int),
|
||||
addingMonths addMonths: Int,
|
||||
seconds: Double,
|
||||
) -> Double {
|
||||
@ -52,7 +54,7 @@ extension PureXML.Schema {
|
||||
let yearsCarried = Int((Double(total) / 12.0).rounded(.down))
|
||||
let newYear = reference.year + yearsCarried
|
||||
let newMonth = total - yearsCarried * 12 + 1
|
||||
let newDay = Swift.min(reference.day, DateTimeValue.daysInMonth(year: newYear, month: newMonth))
|
||||
let newDay = Swift.min(1, DateTimeValue.daysInMonth(year: newYear, month: newMonth))
|
||||
let days = DateTimeValue.daysFromCivil(year: newYear, month: newMonth, day: newDay)
|
||||
return Double(days) * 86400 + seconds
|
||||
}
|
||||
|
||||
@ -266,7 +266,7 @@ public extension PureXML.Schema {
|
||||
/// the ordered range), so an incomparable result is a violation.
|
||||
private func durationRangeError(_ value: String) -> String? {
|
||||
guard let actual = DurationValue(value) else { return nil }
|
||||
func order(_ bound: String?) -> DurationValue.Order? {
|
||||
func order(_ bound: String?) -> DurationOrder? {
|
||||
bound.flatMap(DurationValue.init).map(actual.compare(to:))
|
||||
}
|
||||
if let order = order(facets.minInclusive), order == .lessThan || order == .incomparable {
|
||||
|
||||
@ -21,25 +21,25 @@ struct SchemaDurationFacetTests {
|
||||
|
||||
@Test("minInclusive enforces the lower bound")
|
||||
func test_minInclusive() throws {
|
||||
let s = try schema(#"<xs:minInclusive value="P1Y"/>"#)
|
||||
#expect(try s.validate("<v>P1Y</v>").isEmpty) // equal
|
||||
#expect(try s.validate("<v>P2Y</v>").isEmpty) // greater
|
||||
#expect(try !s.validate("<v>P6M</v>").isEmpty) // less
|
||||
let doc = try schema(#"<xs:minInclusive value="P1Y"/>"#)
|
||||
#expect(try doc.validate("<v>P1Y</v>").isEmpty) // equal
|
||||
#expect(try doc.validate("<v>P2Y</v>").isEmpty) // greater
|
||||
#expect(try !doc.validate("<v>P6M</v>").isEmpty) // less
|
||||
}
|
||||
|
||||
@Test("minExclusive rejects the bound itself")
|
||||
func test_minExclusive() throws {
|
||||
let s = try schema(#"<xs:minExclusive value="P1Y"/>"#)
|
||||
#expect(try !s.validate("<v>P1Y</v>").isEmpty) // equal: rejected
|
||||
#expect(try s.validate("<v>P2Y</v>").isEmpty) // greater
|
||||
let doc = try schema(#"<xs:minExclusive value="P1Y"/>"#)
|
||||
#expect(try !doc.validate("<v>P1Y</v>").isEmpty) // equal: rejected
|
||||
#expect(try doc.validate("<v>P2Y</v>").isEmpty) // greater
|
||||
}
|
||||
|
||||
@Test("maxInclusive enforces the upper bound and rejects incomparable values")
|
||||
func test_maxInclusivePartialOrder() throws {
|
||||
let s = try schema(#"<xs:maxInclusive value="P30D"/>"#)
|
||||
#expect(try s.validate("<v>P20D</v>").isEmpty) // less
|
||||
#expect(try !s.validate("<v>P40D</v>").isEmpty) // greater
|
||||
let doc = try schema(#"<xs:maxInclusive value="P30D"/>"#)
|
||||
#expect(try doc.validate("<v>P20D</v>").isEmpty) // less
|
||||
#expect(try !doc.validate("<v>P40D</v>").isEmpty) // greater
|
||||
// P1M is incomparable to P30D (28..31 days) so it is not <= P30D.
|
||||
#expect(try !s.validate("<v>P1M</v>").isEmpty)
|
||||
#expect(try !doc.validate("<v>P1M</v>").isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,18 +21,18 @@ struct SchemaListFacetTests {
|
||||
|
||||
@Test("length on NMTOKENS counts items, not characters")
|
||||
func test_nmtokensLength() throws {
|
||||
let s = try schema("xs:NMTOKENS", #"<xs:length value="3"/>"#)
|
||||
#expect(try s.validate("<v>alpha beta gamma</v>").isEmpty) // 3 items: valid
|
||||
#expect(try !s.validate("<v>alpha beta</v>").isEmpty) // 2 items: invalid
|
||||
#expect(try !s.validate("<v>alpha beta gamma delta</v>").isEmpty) // 4 items: invalid
|
||||
let doc = try schema("xs:NMTOKENS", #"<xs:length value="3"/>"#)
|
||||
#expect(try doc.validate("<v>alpha beta gamma</v>").isEmpty) // 3 items: valid
|
||||
#expect(try !doc.validate("<v>alpha beta</v>").isEmpty) // 2 items: invalid
|
||||
#expect(try !doc.validate("<v>alpha beta gamma delta</v>").isEmpty) // 4 items: invalid
|
||||
}
|
||||
|
||||
@Test("minLength and maxLength on IDREFS count items")
|
||||
func test_idrefsMinMax() throws {
|
||||
let s = try schema("xs:IDREFS", #"<xs:minLength value="2"/><xs:maxLength value="3"/>"#)
|
||||
#expect(try s.validate("<v>a b</v>").isEmpty) // 2 items: valid
|
||||
#expect(try s.validate("<v>a b c</v>").isEmpty) // 3 items: valid
|
||||
#expect(try !s.validate("<v>a</v>").isEmpty) // 1 item: invalid
|
||||
#expect(try !s.validate("<v>a b c d</v>").isEmpty) // 4 items: invalid
|
||||
let doc = try schema("xs:IDREFS", #"<xs:minLength value="2"/><xs:maxLength value="3"/>"#)
|
||||
#expect(try doc.validate("<v>a b</v>").isEmpty) // 2 items: valid
|
||||
#expect(try doc.validate("<v>a b c</v>").isEmpty) // 3 items: valid
|
||||
#expect(try !doc.validate("<v>a</v>").isEmpty) // 1 item: invalid
|
||||
#expect(try !doc.validate("<v>a b c d</v>").isEmpty) // 4 items: invalid
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,31 +23,31 @@ struct SchemaUnionFacetTests {
|
||||
|
||||
@Test("Enumeration on a union restricts to the listed values")
|
||||
func test_enumeration() throws {
|
||||
let s = try schema(#"<xs:enumeration value="5"/><xs:enumeration value="foo"/>"#)
|
||||
#expect(try s.validate("<v>5</v>").isEmpty) // listed
|
||||
#expect(try s.validate("<v>foo</v>").isEmpty) // listed
|
||||
#expect(try !s.validate("<v>7</v>").isEmpty) // valid int, not listed
|
||||
#expect(try !s.validate("<v>bar</v>").isEmpty) // valid string, not listed
|
||||
let doc = try schema(#"<xs:enumeration value="5"/><xs:enumeration value="foo"/>"#)
|
||||
#expect(try doc.validate("<v>5</v>").isEmpty) // listed
|
||||
#expect(try doc.validate("<v>foo</v>").isEmpty) // listed
|
||||
#expect(try !doc.validate("<v>7</v>").isEmpty) // valid int, not listed
|
||||
#expect(try !doc.validate("<v>bar</v>").isEmpty) // valid string, not listed
|
||||
}
|
||||
|
||||
@Test("Union enumeration compares in value space, not lexically")
|
||||
func test_enumerationValueSpace() throws {
|
||||
let s = try schema(#"<xs:enumeration value="5"/>"#)
|
||||
let doc = try schema(#"<xs:enumeration value="5"/>"#)
|
||||
// 05 is a valid xs:int equal to the enumerated 5, so it is in the set.
|
||||
#expect(try s.validate("<v>05</v>").isEmpty)
|
||||
#expect(try doc.validate("<v>05</v>").isEmpty)
|
||||
}
|
||||
|
||||
@Test("Pattern on a union constrains the lexical value")
|
||||
func test_pattern() throws {
|
||||
let s = try schema(#"<xs:pattern value="[a-z]+"/>"#)
|
||||
#expect(try s.validate("<v>abc</v>").isEmpty) // valid string, matches
|
||||
#expect(try !s.validate("<v>5</v>").isEmpty) // valid int, fails pattern
|
||||
let doc = try schema(#"<xs:pattern value="[a-z]+"/>"#)
|
||||
#expect(try doc.validate("<v>abc</v>").isEmpty) // valid string, matches
|
||||
#expect(try !doc.validate("<v>5</v>").isEmpty) // valid int, fails pattern
|
||||
}
|
||||
|
||||
@Test("A union with no facets still admits any member value")
|
||||
func test_noFacets() throws {
|
||||
let s = try schema("")
|
||||
#expect(try s.validate("<v>42</v>").isEmpty)
|
||||
#expect(try s.validate("<v>anything</v>").isEmpty)
|
||||
let doc = try schema("")
|
||||
#expect(try doc.validate("<v>42</v>").isEmpty)
|
||||
#expect(try doc.validate("<v>anything</v>").isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user