Skip to content

Commit 931e87e

Browse files
committed
feat(format): support duration
1 parent 680d64f commit 931e87e

File tree

5 files changed

+133
-2
lines changed

5 files changed

+133
-2
lines changed

Sources/Draft201909Validator.swift

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public class Draft201909Validator: Validator {
6161
"uuid": validateUUID,
6262
"regex": validateRegex,
6363
"json-pointer": validateJSONPointer,
64+
"duration": validateDuration,
6465
]
6566

6667
public required init(schema: Bool) {

Sources/Draft202012Validator.swift

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public class Draft202012Validator: Validator {
6262
"uuid": validateUUID,
6363
"regex": validateRegex,
6464
"json-pointer": validateJSONPointer,
65+
"duration": validateDuration,
6566
]
6667

6768
public required init(schema: Bool) {

Sources/Format/duration.swift

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import Foundation
2+
3+
/*
4+
https://tools.ietf.org/html/rfc3339
5+
6+
Durations:
7+
8+
dur-second = 1*DIGIT "S"
9+
dur-minute = 1*DIGIT "M" [dur-second]
10+
dur-hour = 1*DIGIT "H" [dur-minute]
11+
dur-time = "T" (dur-hour / dur-minute / dur-second)
12+
dur-day = 1*DIGIT "D"
13+
dur-week = 1*DIGIT "W"
14+
dur-month = 1*DIGIT "M" [dur-day]
15+
dur-year = 1*DIGIT "Y" [dur-month]
16+
dur-date = (dur-day / dur-month / dur-year) [dur-time]
17+
18+
duration = "P" (dur-date / dur-time / dur-week)
19+
*/
20+
var durationExpression: NSRegularExpression = {
21+
let second = "(\\d+S)"
22+
let minute = "((\\d+M)\(second)?)"
23+
let hour = "((\\d+H)\(minute)?)"
24+
let time = "(T(\(hour)|\(minute)|\(second)))"
25+
26+
let day = "(\\d+D)"
27+
let week = "(\\d+W)"
28+
let month = "((\\d+M)\(day)?)"
29+
let year = "((\\d+Y)\(month)?)"
30+
let date = "((\(day)|\(month)|\(year))\(time)?)"
31+
32+
let duration = "^P(\(date)|\(time)|\(week))$"
33+
return try! NSRegularExpression(pattern: duration, options: [])
34+
}()
35+
36+
37+
func isValidDuration(_ value: String) -> Bool {
38+
return durationExpression.numberOfMatches(in: value, range: NSMakeRange(0, value.utf16.count)) != 0
39+
}
40+
41+
42+
func validateDuration(_ context: Context, _ value: String) -> AnySequence<ValidationError> {
43+
guard isValidDuration(value) else {
44+
return AnySequence([
45+
ValidationError(
46+
"'\(value)' is not a valid duration.",
47+
instanceLocation: context.instanceLocation,
48+
keywordLocation: context.keywordLocation
49+
)
50+
])
51+
}
52+
53+
return AnySequence(EmptyCollection())
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import XCTest
2+
@testable import JSONSchema
3+
4+
5+
class DurationTests: XCTestCase {
6+
// dur-date
7+
8+
func testDay() {
9+
XCTAssertTrue(isValidDuration("P3D"))
10+
}
11+
12+
func testMonth() {
13+
XCTAssertTrue(isValidDuration("P2M"))
14+
}
15+
16+
func testMonthDay() {
17+
XCTAssertTrue(isValidDuration("P2M1D"))
18+
}
19+
20+
func testYear() {
21+
XCTAssertTrue(isValidDuration("P5Y"))
22+
}
23+
24+
func testYearMonth() {
25+
XCTAssertTrue(isValidDuration("P5Y2M"))
26+
}
27+
28+
func testYearMonthDay() {
29+
XCTAssertTrue(isValidDuration("P5Y2M1D"))
30+
}
31+
32+
func testDateTime() {
33+
XCTAssertTrue(isValidDuration("P1DT5M"))
34+
}
35+
36+
// dur-time
37+
38+
func testHour() {
39+
XCTAssertTrue(isValidDuration("PT1H"))
40+
}
41+
42+
func testHourMinute() {
43+
XCTAssertTrue(isValidDuration("PT1H5M"))
44+
}
45+
46+
func testHourMinuteSecond() {
47+
XCTAssertTrue(isValidDuration("PT1H5M20S"))
48+
}
49+
50+
func testMinute() {
51+
XCTAssertTrue(isValidDuration("PT1M"))
52+
}
53+
54+
func testMinuteSecond() {
55+
XCTAssertTrue(isValidDuration("PT5M10S"))
56+
}
57+
58+
func testSecond() {
59+
XCTAssertTrue(isValidDuration("PT1S"))
60+
}
61+
62+
// dur-week
63+
64+
func testWeek() {
65+
XCTAssertTrue(isValidDuration("P1W"))
66+
}
67+
68+
// Negative
69+
70+
func testMissingDuration() {
71+
XCTAssertFalse(isValidDuration("P"))
72+
}
73+
74+
func testMissingTime() {
75+
XCTAssertFalse(isValidDuration("PT"))
76+
}
77+
}

Tests/JSONSchemaTests/JSONSchemaCases.swift

-2
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ class JSONSchemaCases: XCTestCase {
175175
"format.json",
176176
"date-time.json",
177177
"date.json",
178-
"duration.json",
179178
"email.json",
180179
"hostname.json",
181180
"idn-email.json",
@@ -211,7 +210,6 @@ class JSONSchemaCases: XCTestCase {
211210
"format.json",
212211
"date-time.json",
213212
"date.json",
214-
"duration.json",
215213
"email.json",
216214
"hostname.json",
217215
"idn-email.json",

0 commit comments

Comments
 (0)