Skip to content

Commit 0b1fff8

Browse files
committed
JSONValuePattern
1 parent 9523b3b commit 0b1fff8

File tree

7 files changed

+992
-0
lines changed

7 files changed

+992
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//
2+
// HTTPRoute+JSONValue.swift
3+
// FlyingFox
4+
//
5+
// Created by Simon Whitty on 15/08/2024.
6+
// Copyright © 2024 Simon Whitty. All rights reserved.
7+
//
8+
// Distributed under the permissive MIT license
9+
// Get the latest version from here:
10+
//
11+
// https://github.com/swhitty/FlyingFox
12+
//
13+
// Permission is hereby granted, free of charge, to any person obtaining a copy
14+
// of this software and associated documentation files (the "Software"), to deal
15+
// in the Software without restriction, including without limitation the rights
16+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17+
// copies of the Software, and to permit persons to whom the Software is
18+
// furnished to do so, subject to the following conditions:
19+
//
20+
// The above copyright notice and this permission notice shall be included in all
21+
// copies or substantial portions of the Software.
22+
//
23+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29+
// SOFTWARE.
30+
//
31+
32+
import Foundation
33+
34+
public extension HTTPRoute {
35+
36+
/// Create a route to a request with a JSON body matching the supplued predicate.
37+
/// - Parameters:
38+
/// - string: String representing the method, path and query parameters of the route `POST /fish`
39+
/// - headers: Headers to evaluate and match
40+
/// - predicate: Predicate to evaluate body of the request via a `JSONValue`
41+
init(
42+
_ string: String,
43+
headers: [HTTPHeader: String] = [:],
44+
jsonBody predicate: @escaping @Sendable (JSONValue) throws -> Bool
45+
) {
46+
self.init(string, headers: headers, body: .jsonValue(where: predicate))
47+
}
48+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//
2+
// JSONPath.swift
3+
// FlyingFox
4+
//
5+
// Created by Simon Whitty on 29/05/2023.
6+
// Copyright © 2023 Simon Whitty. All rights reserved.
7+
//
8+
// Distributed under the permissive MIT license
9+
// Get the latest version from here:
10+
//
11+
// https://github.com/swhitty/FlyingFox
12+
//
13+
// Permission is hereby granted, free of charge, to any person obtaining a copy
14+
// of this software and associated documentation files (the "Software"), to deal
15+
// in the Software without restriction, including without limitation the rights
16+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17+
// copies of the Software, and to permit persons to whom the Software is
18+
// furnished to do so, subject to the following conditions:
19+
//
20+
// The above copyright notice and this permission notice shall be included in all
21+
// copies or substantial portions of the Software.
22+
//
23+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29+
// SOFTWARE.
30+
//
31+
32+
import Foundation
33+
34+
public struct JSONPath {
35+
36+
var components: [Component]
37+
38+
enum Component: Equatable {
39+
case field(String)
40+
case array(Int)
41+
}
42+
43+
init(components: [Component]) {
44+
self.components = components
45+
}
46+
47+
public init(parsing path: String) throws {
48+
self.components = try Self.parseComponents(from: path)
49+
}
50+
51+
private struct Error: LocalizedError {
52+
var errorDescription: String?
53+
54+
init(_ description: String) {
55+
self.errorDescription = description
56+
}
57+
}
58+
}
59+
60+
extension JSONPath {
61+
62+
static func parseComponents(from path: String) throws -> [Component] {
63+
var scanner = Scanner(string: path)
64+
guard scanner.scanString("$") != nil else {
65+
throw Error("Expected $")
66+
}
67+
68+
var comps = [Component]()
69+
while let comp = try scanComponent(from: &scanner) {
70+
comps.append(comp)
71+
}
72+
return comps
73+
}
74+
75+
static func scanComponent(from scanner: inout Scanner) throws -> Component? {
76+
if scanner.scanString(".") != nil {
77+
guard let name = scanner.scanUpToCharacters(from: CharacterSet(charactersIn: ".[")) else {
78+
throw Error("Expected field name")
79+
}
80+
return .field(name)
81+
} else if scanner.scanString("[") != nil {
82+
guard let index = scanner.scanCharacters(from: CharacterSet(charactersIn: "0123456789")) else {
83+
throw Error("Expected index")
84+
}
85+
guard scanner.scanString("]") != nil else {
86+
throw Error("Expected ]")
87+
}
88+
return .array(Int(index)!)
89+
}
90+
guard scanner.isAtEnd else {
91+
throw Error("Expected end")
92+
}
93+
return nil
94+
}
95+
}

0 commit comments

Comments
 (0)