Skip to content

Commit 095735c

Browse files
committed
Add RedisData and properly return parsed data in DataDecoder
1 parent 6318e67 commit 095735c

File tree

2 files changed

+106
-17
lines changed

2 files changed

+106
-17
lines changed

Sources/NIORedis/Coders/RedisDataDecoder.swift

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ extension UInt8 {
4141
extension RedisDataDecoder {
4242
enum _RedisDataDecodingState {
4343
case notYetParsed
44-
#warning("parsed needs to be implemented to include RedisData!")
45-
case parsed
44+
case parsed(RedisData)
4645
}
4746

4847
func _parse(at position: inout Int, from buffer: inout ByteBuffer) throws -> _RedisDataDecodingState {
@@ -53,18 +52,22 @@ extension RedisDataDecoder {
5352
switch token {
5453
case .plus:
5554
guard let string = try _parseSimpleString(at: &position, from: &buffer) else { return .notYetParsed }
56-
return .parsed
55+
return .parsed(.basicString(string))
56+
5757
case .colon:
5858
guard let number = try _parseInteger(at: &position, from: &buffer) else { return .notYetParsed }
59-
return .parsed
59+
return .parsed(.integer(number))
60+
6061
case .dollar:
6162
return try _parseBulkString(at: &position, from: &buffer)
63+
6264
case .asterisk:
6365
return try _parseArray(at: &position, from: &buffer)
66+
6467
case .hyphen:
6568
guard let string = try _parseSimpleString(at: &position, from: &buffer) else { return .notYetParsed }
66-
let error = RedisError(identifier: "serverSide", reason: string)
67-
return .parsed
69+
return .parsed(.error(RedisError(identifier: "serverSide", reason: string)))
70+
6871
default:
6972
throw RedisError(
7073
identifier: "invalidTokenType",
@@ -122,19 +125,17 @@ extension RedisDataDecoder {
122125
func _parseBulkString(at position: inout Int, from buffer: inout ByteBuffer) throws -> _RedisDataDecodingState {
123126
guard let size = try _parseInteger(at: &position, from: &buffer) else { return .notYetParsed }
124127

125-
#warning("TODO: Return null data if null is sent from Redis")
126128
// Redis sends '-1' to represent a null string
127-
guard size > -1 else { return .parsed }
129+
guard size > -1 else { return .parsed(.null) }
128130

129131
// Redis can hold empty bulk strings, and represents it with a 0 size
130132
// so return an empty string
131-
#warning("TODO: Return an empty bulk string")
132133
guard size > 0 else {
133134
// Move the tip of the message position
134135
// since size = 0, and we successfully parsed the size
135136
// the beginning of the next message should be 2 further (the final \r\n - $0\r\n\r\n)
136137
position += 2
137-
return .parsed
138+
return .parsed(.bulkString("".convertedToData()))
138139
}
139140

140141
// verify that we have at least our expected bulk string message
@@ -149,16 +150,16 @@ extension RedisDataDecoder {
149150
// of the bulk string content
150151
position += expectedRemainingMessageSize
151152

152-
return .parsed // bulkString(Data(bytes[ ..<(size - 1) ]))
153+
return .parsed(
154+
.bulkString(Data(bytes[ ..<size ]))
155+
)
153156
}
154157

155158
/// See https://redis.io/topics/protocol#resp-arrays
156159
func _parseArray(at position: inout Int, from buffer: inout ByteBuffer) throws -> _RedisDataDecodingState {
157160
guard let arraySize = try _parseInteger(at: &position, from: &buffer) else { return .notYetParsed }
158-
#warning("TODO: return null array")
159-
guard arraySize > -1 else { return .parsed }
160-
#warning("TODO: return empty array")
161-
guard arraySize > 0 else { return .parsed }
161+
guard arraySize > -1 else { return .parsed(.null) }
162+
guard arraySize > 0 else { return .parsed(.array([])) }
162163

163164
var array = [_RedisDataDecodingState](repeating: .notYetParsed, count: arraySize)
164165
for index in 0..<arraySize {
@@ -173,8 +174,16 @@ extension RedisDataDecoder {
173174
}
174175
}
175176

176-
#warning("TODO: Mapping to data and return the array of values")
177-
return .parsed
177+
let values = try array.map { state -> RedisData in
178+
guard case .parsed(let value) = state else {
179+
throw RedisError(
180+
identifier: "parseArray",
181+
reason: "Unexpected error while parsing Redis RESP."
182+
)
183+
}
184+
return value
185+
}
186+
return .parsed(.array(values))
178187
}
179188
}
180189

Sources/NIORedis/Data/RedisData.swift

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import Foundation
2+
3+
/// A representation of a Redis primitive value
4+
///
5+
/// See: https://redis.io/topics/protocol
6+
public enum RedisData {
7+
case null
8+
case basicString(String)
9+
case bulkString(Data)
10+
case error(RedisError)
11+
case integer(Int)
12+
case array([RedisData])
13+
}
14+
15+
extension RedisData: ExpressibleByStringLiteral {
16+
/// Initializes a bulk string from a String literal
17+
public init(stringLiteral value: String) {
18+
self = .bulkString(Data(value.utf8))
19+
}
20+
}
21+
22+
extension RedisData: ExpressibleByArrayLiteral {
23+
/// Initializes an array from an Array literal
24+
public init(arrayLiteral elements: RedisData...) {
25+
self = .array(elements)
26+
}
27+
}
28+
29+
extension RedisData: ExpressibleByNilLiteral {
30+
/// Initializes null from a nil literal
31+
public init(nilLiteral: ()) {
32+
self = .null
33+
}
34+
}
35+
36+
extension RedisData: ExpressibleByIntegerLiteral {
37+
/// Initializes an integer from an integer literal
38+
public init(integerLiteral value: Int) {
39+
self = .integer(value)
40+
}
41+
}
42+
43+
// Internal convienence computed properties
44+
45+
extension RedisData {
46+
/// Extracts the basic/bulk string as a `String`.
47+
var string: String? {
48+
switch self {
49+
case .basicString(let string): return string
50+
case .bulkString(let data): return String(bytes: data, encoding: .utf8)
51+
default: return nil
52+
}
53+
}
54+
55+
/// Extracts the binary data from a Redis BulkString
56+
public var data: Data? {
57+
guard case .bulkString(let data) = self else { return nil }
58+
return data
59+
}
60+
61+
/// Extracts an array type from this data
62+
public var array: [RedisData]? {
63+
guard case .array(let array) = self else { return nil }
64+
return array
65+
}
66+
67+
/// Extracts an array type from this data
68+
public var int: Int? {
69+
guard case .integer(let int) = self else { return nil }
70+
return int
71+
}
72+
73+
/// `true` if this data is null.
74+
public var isNull: Bool {
75+
switch self {
76+
case .null: return true
77+
default: return false
78+
}
79+
}
80+
}

0 commit comments

Comments
 (0)