Skip to content

Commit 6df4b72

Browse files
committed
All JSON loading done by JSON scanner
1 parent 64b798f commit 6df4b72

File tree

14 files changed

+148
-598
lines changed

14 files changed

+148
-598
lines changed

Sources/JMESPath/Array.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/// Array storage for JMES
12
typealias JMESArray = [Any]
23

34
extension JMESArray {

Sources/JMESPath/Ast.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ indirect enum Ast: Equatable {
3939
}
4040

4141
/// Comparator used in comparison AST nodes
42-
public enum Comparator: Equatable, JMESSendable {
42+
public enum Comparator: Equatable, Sendable {
4343
case equal
4444
case notEqual
4545
case lessThan

Sources/JMESPath/Expression.swift

Lines changed: 14 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import Foundation
2-
31
/// JMES Expression
42
///
53
/// Holds a compiled JMES expression and allows you to search Json text or a type already in memory
6-
public struct JMESExpression: JMESSendable {
4+
public struct JMESExpression: Sendable {
75
let ast: Ast
86

97
public static func compile(_ text: String) throws -> Self {
@@ -22,20 +20,12 @@ public struct JMESExpression: JMESSendable {
2220
/// - runtime: JMES runtime (includes functions)
2321
/// - Throws: JMESPathError
2422
/// - Returns: Search result
25-
public func search<Value>(json: Data, as: Value.Type = Value.self, runtime: JMESRuntime = .init()) throws -> Value? {
26-
try self.search(json: json, runtime: runtime) as? Value
27-
}
28-
29-
/// Search JSON
30-
///
31-
/// - Parameters:
32-
/// - any: JSON to search
33-
/// - as: Swift type to return
34-
/// - runtime: JMES runtime (includes functions)
35-
/// - Throws: JMESPathError
36-
/// - Returns: Search result
37-
public func search<Value>(json: String, as: Value.Type = Value.self, runtime: JMESRuntime = .init()) throws -> Value? {
38-
try self.search(json: json, runtime: runtime) as? Value
23+
public func search<Value>(json: String, as: Value.Type = Value.self, runtime: JMESRuntime = .init()) throws -> Value {
24+
let searchResult = try self.search(json: json, runtime: runtime)
25+
guard let value = searchResult as? Value else {
26+
throw JMESPathError.runtime("Expected \(Value.self)) but got a \(type(of: searchResult))")
27+
}
28+
return value
3929
}
4030

4131
/// Search Swift type
@@ -46,27 +36,12 @@ public struct JMESExpression: JMESSendable {
4636
/// - runtime: JMES runtime (includes functions)
4737
/// - Throws: JMESPathError
4838
/// - Returns: Search result
49-
public func search<Value>(object: Any, as: Value.Type = Value.self, runtime: JMESRuntime = .init()) throws -> Value? {
50-
let value = try self.search(object: object, runtime: runtime)
51-
return value as? Value
52-
}
53-
54-
/// Search JSON
55-
///
56-
/// - Parameters:
57-
/// - any: JSON to search
58-
/// - runtime: JMES runtime (includes functions)
59-
/// - Throws: JMESPathError
60-
/// - Returns: Search result
61-
public func search(json: Data, runtime: JMESRuntime = .init()) throws -> Any? {
62-
let variable = try json.withBufferView { view -> JMESVariable? in
63-
var scanner = JSONScanner(bytes: view, options: .init())
64-
let map = try scanner.scan()
65-
guard let value = map.loadValue(at: 0) else { return nil }
66-
return try JMESJSONVariable(value: value).getJMESVariable(map)
39+
public func search<Value>(object: Any, as: Value.Type = Value.self, runtime: JMESRuntime = .init()) throws -> Value {
40+
let searchResult = try self.search(object: object, runtime: runtime)
41+
guard let value = searchResult as? Value else {
42+
throw JMESPathError.runtime("Expected \(Value.self)) but got a \(type(of: searchResult))")
6743
}
68-
guard let variable else { return nil }
69-
return try runtime.interpret(variable, ast: self.ast).collapse()
44+
return value
7045
}
7146

7247
/// Search JSON
@@ -77,14 +52,8 @@ public struct JMESExpression: JMESSendable {
7752
/// - Throws: JMESPathError
7853
/// - Returns: Search result
7954
public func search(json: String, runtime: JMESRuntime = .init()) throws -> Any? {
80-
let variable = try json.withBufferView { view -> JMESVariable? in
81-
var scanner = JSONScanner(bytes: view, options: .init())
82-
let map = try scanner.scan()
83-
guard let value = map.loadValue(at: 0) else { return nil }
84-
return try JMESJSONVariable(value: value).getJMESVariable(map)
85-
}
86-
guard let variable else { return nil }
87-
return try runtime.interpret(variable, ast: self.ast).collapse()
55+
let value = try JMESJSON.parse(json: json)
56+
return try runtime.interpret(JMESVariable(from: value), ast: self.ast).collapse()
8857
}
8958

9059
/// Search Swift type

Sources/JMESPath/Functions.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import Foundation
2-
31
/// Used to validate arguments of a function before it is run
42
public struct FunctionSignature {
53
/// Function argument used in function signature to verify arguments
@@ -310,7 +308,7 @@ struct MapFunction: JMESFunction {
310308
static func evaluate(args: [JMESVariable], runtime: JMESRuntime) throws -> JMESVariable {
311309
switch (args[0], args[1]) {
312310
case (.expRef(let ast), .array(let array)):
313-
let results = try array.map { try runtime.interpret(JMESVariable(from: $0), ast: ast).collapse() ?? NSNull() }
311+
let results = try array.map { try runtime.interpret(JMESVariable(from: $0), ast: ast).collapse() ?? JMESNull() }
314312
return .array(results)
315313
default:
316314
preconditionFailure()
@@ -665,7 +663,7 @@ struct ToArrayFunction: JMESFunction {
665663
case .array:
666664
return args[0]
667665
default:
668-
return .array([args[0].collapse() ?? NSNull()])
666+
return .array([args[0].collapse() ?? JMESNull()])
669667
}
670668
}
671669
}

Sources/JMESPath/Interpreter.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import Foundation
2-
1+
/// Extend runtime with intepret function
32
extension JMESRuntime {
43
/// Interpret Ast given object to search
54
/// - Parameters:
@@ -78,7 +77,7 @@ extension JMESRuntime {
7877
for element in array {
7978
let currentResult = try interpret(.init(from: element), ast: rhs)
8079
if currentResult != .null {
81-
collected.append(currentResult.collapse() ?? NSNull())
80+
collected.append(currentResult.collapse() ?? JMESNull())
8281
}
8382
}
8483
return .array(collected)
@@ -108,7 +107,7 @@ extension JMESRuntime {
108107
}
109108
var collected: JMESArray = []
110109
for node in elements {
111-
collected.append(try self.interpret(data, ast: node).collapse() ?? NSNull())
110+
collected.append(try self.interpret(data, ast: node).collapse() ?? JMESNull())
112111
}
113112
return .array(collected)
114113

@@ -119,7 +118,7 @@ extension JMESRuntime {
119118
var collected: JMESObject = [:]
120119
for element in elements {
121120
let valueResult = try self.interpret(data, ast: element.value)
122-
collected[element.key] = valueResult.collapse() ?? NSNull()
121+
collected[element.key] = valueResult.collapse() ?? JMESNull()
123122
}
124123
return .object(collected)
125124

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import Foundation
2+
3+
/// JMESExpression extensions for Data
4+
extension JMESExpression {
5+
/// Search JSON
6+
///
7+
/// - Parameters:
8+
/// - any: JSON to search
9+
/// - as: Swift type to return
10+
/// - runtime: JMES runtime (includes functions)
11+
/// - Throws: JMESPathError
12+
/// - Returns: Search result
13+
public func search<Value>(json: Data, as: Value.Type = Value.self, runtime: JMESRuntime = .init()) throws -> Value {
14+
let searchResult = try self.search(json: json, runtime: runtime)
15+
guard let value = searchResult as? Value else {
16+
throw JMESPathError.runtime("Expected \(Value.self)) but got a \(type(of: searchResult))")
17+
}
18+
return value
19+
}
20+
21+
/// Search JSON
22+
///
23+
/// - Parameters:
24+
/// - any: JSON to search
25+
/// - runtime: JMES runtime (includes functions)
26+
/// - Throws: JMESPathError
27+
/// - Returns: Search result
28+
public func search(json: Data, runtime: JMESRuntime = .init()) throws -> Any? {
29+
let value = try JMESJSON.parse(json: json)
30+
return try runtime.interpret(JMESVariable(from: value), ast: self.ast).collapse()
31+
}
32+
}
33+
34+
/// Parse json in the form of Data
35+
extension JMESJSON {
36+
static func parse(json: Data) throws -> Any {
37+
try json.withBufferView { view in
38+
var scanner = JSONScanner(bytes: view, options: .init())
39+
let map = try scanner.scan()
40+
guard let value = map.loadValue(at: 0) else { throw JMESPathError.runtime("Empty JSON file") }
41+
return try JMESJSONVariable(value: value).getValue(map)
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)