diff --git a/FlyingFox/Sources/JSON/JSONValuePattern.swift b/FlyingFox/Sources/JSON/JSONBodyPattern.swift similarity index 90% rename from FlyingFox/Sources/JSON/JSONValuePattern.swift rename to FlyingFox/Sources/JSON/JSONBodyPattern.swift index ac5f3bf..0b32229 100644 --- a/FlyingFox/Sources/JSON/JSONValuePattern.swift +++ b/FlyingFox/Sources/JSON/JSONBodyPattern.swift @@ -31,14 +31,14 @@ import Foundation -public extension HTTPBodyPattern where Self == JSONValuePattern { +public extension HTTPBodyPattern where Self == JSONBodyPattern { - static func jsonValue(where predicate: @escaping @Sendable (JSONValue) throws -> Bool) -> JSONValuePattern { - JSONValuePattern(predicate) + static func jsonValue(where predicate: @escaping @Sendable (JSONValue) throws -> Bool) -> JSONBodyPattern { + JSONBodyPattern(predicate) } } -public struct JSONValuePattern: HTTPBodyPattern { +public struct JSONBodyPattern: HTTPBodyPattern { private let predicate: @Sendable (JSONValue) throws -> Bool diff --git a/FlyingFox/Sources/JSON/JSONValue.swift b/FlyingFox/Sources/JSON/JSONValue.swift index 699df3a..1a68997 100644 --- a/FlyingFox/Sources/JSON/JSONValue.swift +++ b/FlyingFox/Sources/JSON/JSONValue.swift @@ -130,52 +130,22 @@ public extension JSONValue { } else { self = .number(nsNumber.doubleValue) } - } else if let int = any as? Int { - self = .number(Double(int)) - } else if let double = any as? Double { - self = .number(double) - } else if let bool = any as? Bool { - self = .boolean(bool) } else if any is NSNull { self = .null + } else if case nil as Any? = any { + self = .null } else { throw Error("Unsupported Value") } } - init?(_ value: JSONValue?) { - guard let value else { return nil } - self = value - } - - init?(_ value: [String: JSONValue]?) { - guard let value else { return nil } - self = .object(value) - } - - init?(_ value: [JSONValue]?) { - guard let value else { return nil } - self = .array(value) - } - - init?(_ value: String?) { - guard let value else { return nil } - self = .string(value) - } - - init?(_ value: Double?) { - guard let value else { return nil } - self = .number(value) - } - - init?(_ value: Int?) { - guard let value else { return nil } - self = .number(Double(value)) - } - - init?(_ value: Bool?) { - guard let value else { return nil } - self = .boolean(value) + init(_ any: T?) throws { + switch any { + case .none: + self = .null + case .some(let value): + self = try JSONValue(value) + } } func asAny() -> Any { @@ -195,54 +165,20 @@ public extension JSONValue { } } - func asObject() throws -> [String: JSONValue] { + private func asObject() throws -> [String: JSONValue] { guard case let .object(val) = self else { throw Error("Expected object") } return val } - func asArray() throws -> [JSONValue] { + private func asArray() throws -> [JSONValue] { guard case let .array(val) = self else { throw Error("Expected array") } return val } - func asString() throws -> String { - guard case let .string(val) = self else { - throw Error("Expected string") - } - return val - } - - func asNumber() throws -> Double { - guard case .number(let val) = self else { - throw Error("Expected number") - } - return val - } - - func asBool() throws -> Bool { - switch self { - case .boolean(let val): - return val - case .number(let val) where val == 0: - return false - case .number(let val) where val == 1: - return true - default: - throw Error("Expected boolean") - } - } - - func asNull() throws -> NSNull { - guard case .null = self else { - throw Error("Expected null") - } - return NSNull() - } - private struct Error: LocalizedError { var errorDescription: String? @@ -261,96 +197,34 @@ public extension JSONValue { } } -public extension JSONValue { - - mutating func updateValue(parsing text: String) throws { - if let null = try? Self.parseNull(string: text) { - self = null - return - } - switch self { - case .object: - self = try Self.parseObject(string: text) - case .array: - self = try Self.parseArray(string: text) - case .string: - self = .string(text) - case .number: - self = try Self.parseNumber(string: text) - case .boolean: - self = try Self.parseBoolean(string: text) - case .null: - self = Self.parseAny(string: text) - } - } +public func == (lhs: JSONValue, rhs: String) -> Bool { + lhs == JSONValue.string(rhs) +} - static func parseObject(string: String) throws -> JSONValue { - let data = string.data(using: .utf8)! - guard case let .object(object) = try JSONValue(data: data) else { - throw Error("Invalid object") - } - return .object(object) - } +public func != (lhs: JSONValue, rhs: String) -> Bool { + !(lhs == rhs) +} - static func parseArray(string: String) throws -> JSONValue { - let data = string.data(using: .utf8)! - guard case let .array(array) = try JSONValue(data: data) else { - throw Error("Invalid array") - } - return .array(array) - } +public func == (lhs: JSONValue, rhs: Double) -> Bool { + lhs == JSONValue.number(rhs) +} - static func parseNumber(string: String) throws -> JSONValue { - guard let value = JSONValue.numberFormatter.number(from: string)?.doubleValue else { - throw Error("Invalid number") - } - return .number(value) - } +public func != (lhs: JSONValue, rhs: Double) -> Bool { + !(lhs == rhs) +} - static func parseBoolean(string: String) throws -> JSONValue { - switch string.lowercased() { - case "true": - return .boolean(true) - case "false": - return .boolean(false) - default: - throw Error("Invalid boolean") - } - } +public func == (lhs: JSONValue, rhs: Bool) -> Bool { + lhs == JSONValue.boolean(rhs) +} - static func parseNull(string: String) throws -> JSONValue { - switch string.lowercased() { - case "null", "": - return .null - default: - throw Error("Invalid null") - } - } +public func != (lhs: JSONValue, rhs: Bool) -> Bool { + !(lhs == rhs) +} - static func parseAny(string: String) -> JSONValue { - if let object = try? parseObject(string: string) { - return object - } else if let array = try? parseArray(string: string) { - return array - } else if let number = try? parseNumber(string: string) { - return number - } else if let bool = try? parseBoolean(string: string) { - return bool - } else if let null = try? parseNull(string: string) { - return null - } else { - return .string(string) - } - } +public func == (lhs: JSONValue, rhs: some BinaryInteger) -> Bool { + lhs == JSONValue.number(Double(rhs)) } -public extension JSONValue { - static let numberFormatter: NumberFormatter = { - let formatter = NumberFormatter() - formatter.locale = Locale(identifier: "en_US") - formatter.minimumFractionDigits = 0 - formatter.maximumFractionDigits = 6 - formatter.roundingMode = .halfUp - return formatter - }() +public func != (lhs: JSONValue, rhs: some BinaryInteger) -> Bool { + !(lhs == rhs) } diff --git a/FlyingFox/Tests/JSON/JSONValuePatternTests.swift b/FlyingFox/Tests/JSON/JSONBodyPatternTests.swift similarity index 91% rename from FlyingFox/Tests/JSON/JSONValuePatternTests.swift rename to FlyingFox/Tests/JSON/JSONBodyPatternTests.swift index 4db8bd2..0f9e240 100644 --- a/FlyingFox/Tests/JSON/JSONValuePatternTests.swift +++ b/FlyingFox/Tests/JSON/JSONBodyPatternTests.swift @@ -1,5 +1,5 @@ // -// JSONValuePatternTests.swift +// JSONBodyPatternTests.swift // FlyingFox // // Created by Simon Whitty on 15/08/2024. @@ -33,12 +33,12 @@ import FlyingFox import Foundation import Testing -struct JSONValuePatternTests { +struct JSONBodyPatternTests { @Test func pattern_MatchesJSONPath() async throws { // given - let pattern = JSONValuePattern { try $0.getValue(for: "$.name") == .string("fish") } + let pattern = JSONBodyPattern { try $0.getValue(for: "$.name") == "fish" } // when then #expect(pattern.evaluate(json: #"{"name": "fish"}"#)) @@ -53,7 +53,7 @@ struct JSONValuePatternTests { // given let route = HTTPRoute( "POST /fish", - jsonBody: { try $0.getValue(for: "$.food") == .string("chips") } + jsonBody: { try $0.getValue(for: "$.food") == "chips" } ) // when @@ -70,14 +70,13 @@ struct JSONValuePatternTests { } } -private extension JSONValuePattern { +private extension JSONBodyPattern { func evaluate(json: String) -> Bool { self.evaluate(Data(json.utf8)) } } - private extension HTTPRequest { static func make(method: HTTPMethod = .POST, version: HTTPVersion = .http11, diff --git a/FlyingFox/Tests/JSON/JSONPathTests.swift b/FlyingFox/Tests/JSON/JSONPathTests.swift index 3b99b58..561ecaa 100644 --- a/FlyingFox/Tests/JSON/JSONPathTests.swift +++ b/FlyingFox/Tests/JSON/JSONPathTests.swift @@ -85,16 +85,16 @@ struct JSONPathTests { """#) #expect( - try json.getValue(for: "$.owner.age").asNumber() == 7 + try json.getValue(for: "$.owner.age") == 7 ) #expect( - try json.getValue(for: "$.owner.isAdmin").asBool() + try json.getValue(for: "$.owner.isAdmin") == true ) #expect( - try json.getValue(for: "$.users[1].food").asString() == "chips" + try json.getValue(for: "$.users[1].food") == "chips" ) #expect( - try json.getValue(for: "$.users[2].age").asNumber() == 9 + try json.getValue(for: "$.users[2].age") == 9 ) } diff --git a/FlyingFox/Tests/JSON/JSONValueTests.swift b/FlyingFox/Tests/JSON/JSONValueTests.swift index 223ef31..e02087a 100644 --- a/FlyingFox/Tests/JSON/JSONValueTests.swift +++ b/FlyingFox/Tests/JSON/JSONValueTests.swift @@ -30,140 +30,96 @@ // import FlyingFox +import Foundation import Testing struct JSONValueTests { @Test - func objects_CanBeUpdated_ToNull() throws { + func json_string() throws { // given - var val = JSONValue.object([:]) - - // when - try val.updateValue(parsing: "null") + let val = try JSONValue("fish") // then - #expect(val == .null) + #expect(val == "fish") + #expect(val != "chips") } @Test - func arrays_CanBeUpdated_ToNull() throws { + func json_int() throws { // given - var val = JSONValue.array([]) - - // when - try val.updateValue(parsing: "null") + let val = try JSONValue(Int(10)) // then - #expect(val == .null) + #expect(val == 10) + #expect(val != 100) } @Test - func string_CanBeUpdated_ToNull() throws { + func json_double() throws { // given - var val = JSONValue.string("") - - // when - try val.updateValue(parsing: "null") + let val = try JSONValue(10.5) // then - #expect(val == .null) + #expect(val == 10.5) + #expect(val != 100.5) } @Test - func number_CanBeUpdated_ToNull() throws { + func json_bool() throws { // given - var val = JSONValue.number(10) - - // when - try val.updateValue(parsing: "null") + let val = try JSONValue(true) // then - #expect(val == .null) + #expect(val == true) + #expect(val != false) } @Test - func bool_CanBeUpdated_ToNull() throws { + func json_nsnull() throws { // given - var val = JSONValue.boolean(true) - - // when - try val.updateValue(parsing: "null") + let val = try JSONValue(NSNull()) // then #expect(val == .null) + #expect(val != "fish") } @Test - func null_CanBeUpdated_ToNull() throws { + func json_anyNone() throws { // given - var val = JSONValue.null - - // when - try val.updateValue(parsing: "null") + let val = try JSONValue(String?.none as Any) // then #expect(val == .null) + #expect(val != "fish") } @Test - func null_CanBeUpdated_ToObject() throws { - // given - var val = JSONValue.null - - // when - try val.updateValue(parsing: "{\"foo\":\"bar\"}") - - // then - #expect(val == .object(["foo": .string("bar")])) - } - - @Test - func null_CanBeUpdated_ToArray() throws { - // given - var val = JSONValue.null - - // when - try val.updateValue(parsing: "[1,2]") - - // then - #expect(val == .array([.number(1), .number(2)])) - } - - @Test - func null_CanBeUpdated_ToNumber() throws { + func json_optionalNone() throws { // given - var val = JSONValue.null - - // when - try val.updateValue(parsing: "1") + let val = try JSONValue(String?.none) // then - #expect(val == .number(1)) + #expect(val == .null) + #expect(val != "fish") } @Test - func null_CanBeUpdated_ToBool() throws { + func json_optionalSome() throws { // given - var val = JSONValue.null - - // when - try val.updateValue(parsing: "true") + let val = try JSONValue(String?.some("fish")) // then - #expect(val == .boolean(true)) + #expect(val == "fish") + #expect(val != .null) } @Test - func null_CanBeUpdated_ToString() throws { - // given - var val = JSONValue.null - - // when - try val.updateValue(parsing: "foo") - - // then - #expect(val == .string("foo")) + func json_invalid() { + #expect(throws: (any Error).self) { + try JSONValue(HTTPRequest.make()) + } } #if canImport(Darwin)