Clarify and make public RESPEncoder

This commit is contained in:
Nathan Harris
2019-01-09 13:10:59 -08:00
parent 26e3ac2aab
commit e01cdf565d
2 changed files with 34 additions and 25 deletions
@@ -1,20 +1,17 @@
import Foundation
import NIO
/// Handles outgoing `RESPValue` on the wire by encoding it to the Redis RESP protocol.
/// Translates `RedisValue` into raw bytes, formatted according to the Redis Serialization Protocol (RESP).
///
/// See: https://redis.io/topics/protocol
internal final class RESPEncoder: MessageToByteEncoder {
/// See `MessageToByteEncoder.OutboundIn`
typealias OutboundIn = RESPValue
/// See `RESPEncoder.encode(ctx:data:out:)`
func encode(ctx: ChannelHandlerContext, data: RESPValue, out: inout ByteBuffer) throws {
out.write(bytes: _encode(data: data))
}
func _encode(data: RESPValue) -> Data {
switch data {
public final class RESPEncoder {
/// Encodes the `RedisValue` to bytes, following the RESP specification.
///
/// See https://redis.io/topics/protocol
/// - Parameter value: The `RESPValue` to encode.
/// - Returns: The encoded value as a collection of bytes.
public func encode(_ value: RESPValue) -> Data {
switch value {
case .simpleString(let string):
return "+\(string)\r\n".convertedToData()
@@ -31,8 +28,20 @@ internal final class RESPEncoder: MessageToByteEncoder {
return "-\(error.description)\r\n".convertedToData()
case .array(let array):
let encodedArray = array.map { _encode(data: $0) }.joined()
let encodedArray = array.map(encode).joined()
return "*\(array.count)\r\n".convertedToData() + encodedArray
}
}
}
// MARK: MessageToByteEncoder
extension RESPEncoder: MessageToByteEncoder {
/// See `MessageToByteEncoder.OutboundIn`
public typealias OutboundIn = RESPValue
/// See `RESPEncoder.encode(ctx:data:out:)`
public func encode(ctx: ChannelHandlerContext, data: RESPValue, out: inout ByteBuffer) throws {
out.write(bytes: encode(data))
}
}
@@ -8,11 +8,11 @@ final class RESPEncoderParsingTests: XCTestCase {
func testSimpleStrings() {
XCTAssertEqual(
encoder._encode(data: .simpleString("Test1")),
encoder.encode(.simpleString("Test1")),
"+Test1\r\n".convertedToData()
)
XCTAssertEqual(
encoder._encode(data: .simpleString("®in§³¾")),
encoder.encode(.simpleString("®in§³¾")),
"+®in§³¾\r\n".convertedToData()
)
}
@@ -20,40 +20,40 @@ final class RESPEncoderParsingTests: XCTestCase {
func testBulkStrings() {
let t1 = Data(bytes: [0x01, 0x02, 0x0a, 0x1b, 0xaa])
XCTAssertEqual(
encoder._encode(data: .bulkString(t1)),
encoder.encode(.bulkString(t1)),
"$5\r\n".convertedToData() + t1 + "\r\n".convertedToData()
)
let t2 = "®in§³¾".convertedToData()
XCTAssertEqual(
encoder._encode(data: .bulkString(t2)),
encoder.encode(.bulkString(t2)),
"$10\r\n".convertedToData() + t2 + "\r\n".convertedToData()
)
let t3 = "".convertedToData()
XCTAssertEqual(
encoder._encode(data: .bulkString(t3)),
encoder.encode(.bulkString(t3)),
"$0\r\n\r\n".convertedToData()
)
}
func testIntegers() {
XCTAssertEqual(
encoder._encode(data: .integer(Int.min)),
encoder.encode(.integer(Int.min)),
":\(Int.min)\r\n".convertedToData()
)
XCTAssertEqual(
encoder._encode(data: .integer(0)),
encoder.encode(.integer(0)),
":0\r\n".convertedToData()
)
}
func testArrays() {
XCTAssertEqual(
encoder._encode(data: .array([])),
encoder.encode(.array([])),
"*0\r\n".convertedToData()
)
let a1: RESPValue = .array([.integer(3), .simpleString("foo")])
XCTAssertEqual(
encoder._encode(data: a1),
encoder.encode(a1),
"*2\r\n:3\r\n+foo\r\n".convertedToData()
)
let bytes = Data(bytes: [ 0x0a, 0x1a, 0x1b, 0xff ])
@@ -62,19 +62,19 @@ final class RESPEncoderParsingTests: XCTestCase {
.bulkString(bytes)
])])
XCTAssertEqual(
encoder._encode(data: a2),
encoder.encode(a2),
"*1\r\n*2\r\n:3\r\n$4\r\n".convertedToData() + bytes + "\r\n".convertedToData()
)
}
func testError() {
let error = RedisError(identifier: "testError", reason: "Manual error")
let result = encoder._encode(data: .error(error))
let result = encoder.encode(.error(error))
XCTAssertEqual(result, "-\(error.description)\r\n".convertedToData())
}
func testNull() {
XCTAssertEqual(encoder._encode(data: .null), "$-1\r\n".convertedToData())
XCTAssertEqual(encoder.encode(.null), "$-1\r\n".convertedToData())
}
}