Skip to content

Commit 588a6ee

Browse files
authored
Use a version of json scanner from Foundation source code to load json files (#11)
* Use json scanner from foundation * Load as complete object * All JSON loading done by JSON scanner * Add musl, winsdk etc * Fix for linux * Use ContiguousBytes protocol instead of Data
1 parent c722898 commit 588a6ee

20 files changed

+2346
-140
lines changed

Sources/JMESPath/Array.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/// Array storage for JMES
2+
typealias JMESArray = [Any]
3+
4+
extension JMESArray {
5+
/// return if arrays are equal by converting entries to `JMESVariable`
6+
func equalTo(_ rhs: JMESArray) -> Bool {
7+
guard self.count == rhs.count else { return false }
8+
for i in 0..<self.count {
9+
guard JMESVariable(from: self[i]) == JMESVariable(from: rhs[i]) else {
10+
return false
11+
}
12+
}
13+
return true
14+
}
15+
}
16+
17+
extension Array {
18+
/// calculate actual index. Negative indices read backwards from end of array
19+
func calculateIndex(_ index: Int) -> Int {
20+
if index >= 0 {
21+
return index
22+
} else {
23+
return count + index
24+
}
25+
}
26+
}

Sources/JMESPath/Ast.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/// JMES expression abstract syntax tree
2-
public indirect enum Ast: Equatable {
2+
indirect enum Ast: Equatable {
33
/// compares two nodes using a comparator
44
case comparison(comparator: Comparator, lhs: Ast, rhs: Ast)
55
/// if `predicate` evaluates to a truthy value returns result from `then`
@@ -39,7 +39,7 @@ public 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
@@ -66,4 +66,4 @@ public enum Comparator: Equatable, JMESSendable {
6666
// have to force Sendable conformance as enum `.literal` uses `JMESVariable` which
6767
// is not necessarily sendable but in the use here it is
6868
extension Ast: @unchecked Sendable {}
69-
#endif
69+
#endif

Sources/JMESPath/Expression.swift

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
#if canImport(FoundationEssentials)
2+
import FoundationEssentials
3+
#else
14
import Foundation
5+
#endif
26

37
/// JMES Expression
48
///
59
/// Holds a compiled JMES expression and allows you to search Json text or a type already in memory
6-
public struct JMESExpression: JMESSendable {
10+
public struct JMESExpression: Sendable {
711
let ast: Ast
812

913
public static func compile(_ text: String) throws -> Self {
@@ -22,8 +26,12 @@ public struct JMESExpression: JMESSendable {
2226
/// - runtime: JMES runtime (includes functions)
2327
/// - Throws: JMESPathError
2428
/// - 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
29+
public func search<Value>(json: String, as: Value.Type = Value.self, runtime: JMESRuntime = .init()) throws -> Value {
30+
let searchResult = try self.search(json: json, runtime: runtime)
31+
guard let value = searchResult as? Value else {
32+
throw JMESPathError.runtime("Expected \(Value.self)) but got a \(type(of: searchResult))")
33+
}
34+
return value
2735
}
2836

2937
/// Search JSON
@@ -34,8 +42,12 @@ public struct JMESExpression: JMESSendable {
3442
/// - runtime: JMES runtime (includes functions)
3543
/// - Throws: JMESPathError
3644
/// - 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
45+
public func search<Value>(json: some ContiguousBytes, as: Value.Type = Value.self, runtime: JMESRuntime = .init()) throws -> Value {
46+
let searchResult = try self.search(json: json, runtime: runtime)
47+
guard let value = searchResult as? Value else {
48+
throw JMESPathError.runtime("Expected \(Value.self)) but got a \(type(of: searchResult))")
49+
}
50+
return value
3951
}
4052

4153
/// Search Swift type
@@ -46,9 +58,12 @@ public struct JMESExpression: JMESSendable {
4658
/// - runtime: JMES runtime (includes functions)
4759
/// - Throws: JMESPathError
4860
/// - 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
61+
public func search<Value>(object: Any, as: Value.Type = Value.self, runtime: JMESRuntime = .init()) throws -> Value {
62+
let searchResult = try self.search(object: object, runtime: runtime)
63+
guard let value = searchResult as? Value else {
64+
throw JMESPathError.runtime("Expected \(Value.self)) but got a \(type(of: searchResult))")
65+
}
66+
return value
5267
}
5368

5469
/// Search JSON
@@ -58,9 +73,9 @@ public struct JMESExpression: JMESSendable {
5873
/// - runtime: JMES runtime (includes functions)
5974
/// - Throws: JMESPathError
6075
/// - Returns: Search result
61-
public func search(json: Data, runtime: JMESRuntime = .init()) throws -> Any? {
62-
let value = try JMESVariable.fromJson(json)
63-
return try runtime.interpret(value, ast: self.ast).collapse()
76+
public func search(json: String, runtime: JMESRuntime = .init()) throws -> Any? {
77+
let value = try JMESJSON.parse(json: json)
78+
return try runtime.interpret(JMESVariable(from: value), ast: self.ast).collapse()
6479
}
6580

6681
/// Search JSON
@@ -70,9 +85,9 @@ public struct JMESExpression: JMESSendable {
7085
/// - runtime: JMES runtime (includes functions)
7186
/// - Throws: JMESPathError
7287
/// - Returns: Search result
73-
public func search(json: String, runtime: JMESRuntime = .init()) throws -> Any? {
74-
let value = try JMESVariable.fromJson(json)
75-
return try runtime.interpret(value, ast: self.ast).collapse()
88+
public func search(json: some ContiguousBytes, runtime: JMESRuntime = .init()) throws -> Any? {
89+
let value = try JMESJSON.parse(json: json)
90+
return try runtime.interpret(JMESVariable(from: value), ast: self.ast).collapse()
7691
}
7792

7893
/// Search Swift type

Sources/JMESPath/Functions.swift

Lines changed: 3 additions & 5 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
@@ -118,7 +116,7 @@ extension JMESVariable {
118116
/// let expression = try Expression.compile(myExpression)
119117
/// let result = try expression.search(json: myJson, runtime: runtime)
120118
/// ```
121-
public protocol JMESFunction {
119+
protocol JMESFunction {
122120
/// function signature
123121
static var signature: FunctionSignature { get }
124122
/// Evaluate function
@@ -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

0 commit comments

Comments
 (0)