Skip to content

Commit 5da9787

Browse files
Replace AnyCodable with custom JSONValue
1 parent 55071da commit 5da9787

File tree

5 files changed

+204
-30
lines changed

5 files changed

+204
-30
lines changed

Package.resolved

Lines changed: 0 additions & 16 deletions
This file was deleted.

Package.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ let package = Package(
99
.library(name: "JSONRPC", targets: ["JSONRPC"]),
1010
],
1111
dependencies: [
12-
.package(url: "https://github.com/Flight-School/AnyCodable", "0.6.0"..<"0.6.3"),
1312
],
1413
targets: [
15-
.target(name: "JSONRPC", dependencies: ["AnyCodable"]),
14+
.target(name: "JSONRPC", dependencies: []),
1615
.testTarget(name: "JSONRPCTests", dependencies: ["JSONRPC"]),
1716
]
1817
)

Sources/JSONRPC/JSONValue.swift

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import Foundation
2+
3+
public enum JSONValue: Codable, Hashable, Sendable {
4+
case null
5+
case bool(Bool)
6+
case number(Double)
7+
case string(String)
8+
case array([JSONValue])
9+
case hash([String: JSONValue])
10+
11+
public func encode(to encoder: Encoder) throws {
12+
var container = encoder.singleValueContainer()
13+
14+
switch self {
15+
case .null:
16+
try container.encodeNil()
17+
case .bool(let value):
18+
try container.encode(value)
19+
case .number(let value):
20+
try container.encode(value)
21+
case .string(let value):
22+
try container.encode(value)
23+
case .array(let value):
24+
try container.encode(value)
25+
case .hash(let value):
26+
try container.encode(value)
27+
}
28+
}
29+
30+
public init(from decoder: Decoder) throws {
31+
let single = try? decoder.singleValueContainer()
32+
33+
if let value = try? single?.decode([String: JSONValue].self) {
34+
self = .hash(value)
35+
return
36+
}
37+
38+
if let value = try? single?.decode([JSONValue].self) {
39+
self = .array(value)
40+
return
41+
}
42+
43+
if let value = try? single?.decode(String.self) {
44+
self = .string(value)
45+
return
46+
}
47+
48+
if let value = try? single?.decode(Double.self) {
49+
self = .number(value)
50+
return
51+
}
52+
53+
if let value = try? single?.decode(Bool.self) {
54+
self = .bool(value)
55+
return
56+
}
57+
58+
if single?.decodeNil() == true {
59+
self = .null
60+
return
61+
}
62+
63+
throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "failed to decode JSON object"))
64+
}
65+
}
66+
67+
extension JSONValue: ExpressibleByNilLiteral {
68+
public init(nilLiteral: ()) {
69+
self = .null
70+
}
71+
}
72+
73+
extension JSONValue: ExpressibleByDictionaryLiteral {
74+
public init(dictionaryLiteral elements: (String, JSONValue)...) {
75+
var hash = [String: JSONValue]()
76+
77+
for element in elements {
78+
hash[element.0] = element.1
79+
}
80+
81+
self = .hash(hash)
82+
}
83+
}
84+
85+
extension JSONValue: ExpressibleByStringLiteral {
86+
public init(stringLiteral: String) {
87+
self = .string(stringLiteral)
88+
}
89+
}
90+
91+
extension JSONValue: ExpressibleByIntegerLiteral {
92+
public init(integerLiteral value: IntegerLiteralType) {
93+
self = .number(Double(value))
94+
}
95+
}
96+
97+
extension JSONValue: ExpressibleByFloatLiteral {
98+
public init(floatLiteral value: FloatLiteralType) {
99+
self = .number(value)
100+
}
101+
}
102+
103+
extension JSONValue: ExpressibleByArrayLiteral {
104+
public init(arrayLiteral elements: JSONValue...) {
105+
var array = [JSONValue]()
106+
107+
for element in elements {
108+
array.append(element)
109+
}
110+
111+
self = .array(array)
112+
}
113+
}
114+
115+
extension JSONValue: ExpressibleByBooleanLiteral {
116+
public init(booleanLiteral value: BooleanLiteralType) {
117+
self = .bool(value)
118+
}
119+
}

Sources/JSONRPC/Protocol.swift

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import Foundation
2-
import AnyCodable
32

43
public enum JSONRPCProtocolError: Error {
54
case unsupportedVersion(String)
65
case malformedMessage
76
}
87

98
public enum JSONRPCMessage {
10-
case notification(String, AnyCodable)
11-
case request(JSONId, String, AnyCodable?)
9+
case notification(String, JSONValue)
10+
case request(JSONId, String, JSONValue?)
1211
case response(JSONId)
1312
case undecodableId(AnyJSONRPCResponseError)
1413
}
@@ -34,9 +33,9 @@ extension JSONRPCMessage: Codable {
3433
// no id means notification
3534
if container.contains(.id) == false {
3635
let method = try container.decode(String.self, forKey: .method)
37-
let params = try? container.decode(AnyCodable.self, forKey: .params)
36+
let params = try? container.decode(JSONValue.self, forKey: .params)
3837

39-
self = .notification(method, params ?? nil)
38+
self = .notification(method, params ?? .null)
4039
return
4140
}
4241

@@ -52,7 +51,7 @@ extension JSONRPCMessage: Codable {
5251

5352
if container.contains(.method) {
5453
let method = try container.decode(String.self, forKey: .method)
55-
let params = try? container.decode(AnyCodable.self, forKey: .params)
54+
let params = try? container.decode(JSONValue.self, forKey: .params)
5655

5756
self = .request(id, method, params)
5857
return
@@ -73,7 +72,7 @@ extension JSONRPCMessage: Codable {
7372
case .notification(let method, let params):
7473
try container.encode(method, forKey: .method)
7574

76-
if params != AnyCodable(nilLiteral: ()) {
75+
if params != JSONValue.null {
7776
try container.encode(params, forKey: .params)
7877
}
7978
case .request(let id, let method, let params):
@@ -115,7 +114,7 @@ extension JSONRPCRequest: Equatable where T: Equatable {
115114
extension JSONRPCRequest: Hashable where T: Hashable {
116115
}
117116

118-
public typealias AnyJSONRPCRequest = JSONRPCRequest<AnyCodable>
117+
public typealias AnyJSONRPCRequest = JSONRPCRequest<JSONValue>
119118

120119
public struct JSONRPCNotification<T>: Codable where T: Codable {
121120
public var jsonrpc = "2.0"
@@ -134,7 +133,7 @@ extension JSONRPCNotification: Equatable where T: Equatable {
134133
extension JSONRPCNotification: Hashable where T: Hashable {
135134
}
136135

137-
public typealias AnyJSONRPCNotification = JSONRPCNotification<AnyCodable>
136+
public typealias AnyJSONRPCNotification = JSONRPCNotification<JSONValue>
138137

139138
public struct JSONRPCResponseError<T>: Codable where T: Codable {
140139
public var code: Int
@@ -154,7 +153,7 @@ extension JSONRPCResponseError: Equatable where T: Equatable {
154153
extension JSONRPCResponseError: Hashable where T: Hashable {
155154
}
156155

157-
public typealias AnyJSONRPCResponseError = JSONRPCResponseError<AnyCodable>
156+
public typealias AnyJSONRPCResponseError = JSONRPCResponseError<JSONValue>
158157

159158
public enum JSONRPCResponse<T> where T: Codable {
160159
case result(JSONId, T)
@@ -271,12 +270,12 @@ extension JSONRPCResponse: Hashable where T: Hashable {
271270
}
272271

273272
extension JSONRPCResponse {
274-
static func internalError(id: JSONId, message: String, data: AnyCodable = nil) -> JSONRPCResponse<AnyCodable> {
273+
static func internalError(id: JSONId, message: String, data: JSONValue = nil) -> JSONRPCResponse<JSONValue> {
275274
let error = AnyJSONRPCResponseError(code: JSONRPCErrors.internalError,
276275
message: message,
277276
data: data)
278277
return .failure(id, error)
279278
}
280279
}
281280

282-
public typealias AnyJSONRPCResponse = JSONRPCResponse<AnyCodable>
281+
public typealias AnyJSONRPCResponse = JSONRPCResponse<JSONValue>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import XCTest
2+
import JSONRPC
3+
4+
final class JSONValueTests: XCTestCase {
5+
func testNullEncoding() throws {
6+
let obj = JSONValue.null
7+
8+
let data = try JSONEncoder().encode(obj)
9+
let string = try XCTUnwrap(String(data: data, encoding: .utf8))
10+
11+
XCTAssertEqual(string, "null")
12+
}
13+
14+
func testBoolEncoding() throws {
15+
let obj = JSONValue.bool(true)
16+
17+
let data = try JSONEncoder().encode(obj)
18+
let string = try XCTUnwrap(String(data: data, encoding: .utf8))
19+
20+
XCTAssertEqual(string, "true")
21+
}
22+
23+
func testIntEncoding() throws {
24+
let obj = JSONValue.number(45)
25+
26+
let data = try JSONEncoder().encode(obj)
27+
let string = try XCTUnwrap(String(data: data, encoding: .utf8))
28+
29+
XCTAssertEqual(string, "45")
30+
}
31+
32+
func testArrayEncoding() throws {
33+
let obj = JSONValue.array([1,2,3])
34+
35+
let data = try JSONEncoder().encode(obj)
36+
let string = try XCTUnwrap(String(data: data, encoding: .utf8))
37+
38+
XCTAssertEqual(string, "[1,2,3]")
39+
}
40+
41+
func testNullInDictionary() throws {
42+
let obj = JSONValue.hash(["abc": nil])
43+
44+
let data = try JSONEncoder().encode(obj)
45+
let string = try XCTUnwrap(String(data: data, encoding: .utf8))
46+
47+
XCTAssertEqual(string, "{\"abc\":null}")
48+
}
49+
50+
func testDecoding() throws {
51+
let string = """
52+
{
53+
"string": "abc",
54+
"bool": true,
55+
"null": null,
56+
"int": 145,
57+
"double": 145.0,
58+
"array": [1,2,3]
59+
}
60+
"""
61+
let value = try JSONDecoder().decode(JSONValue.self, from: string.data(using: .utf8)!)
62+
63+
let expected: JSONValue = [
64+
"string": "abc",
65+
"bool": true,
66+
"null": nil,
67+
"int": 145,
68+
"double": 145.0,
69+
"array": [1,2,3]
70+
]
71+
XCTAssertEqual(value, expected)
72+
}
73+
}

0 commit comments

Comments
 (0)