From e01cdf565df44f0fda1d30aa1dea7e8937ae0432 Mon Sep 17 00:00:00 2001 From: Nathan Harris Date: Wed, 9 Jan 2019 13:10:59 -0800 Subject: [PATCH] Clarify and make public `RESPEncoder` --- .../ChannelHandlers/RESPEncoder.swift | 35 ++++++++++++------- .../RESPEncoder+ParsingTests.swift | 24 ++++++------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/Sources/NIORedis/ChannelHandlers/RESPEncoder.swift b/Sources/NIORedis/ChannelHandlers/RESPEncoder.swift index 0dfe051..400ba61 100644 --- a/Sources/NIORedis/ChannelHandlers/RESPEncoder.swift +++ b/Sources/NIORedis/ChannelHandlers/RESPEncoder.swift @@ -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)) + } +} diff --git a/Tests/NIORedisTests/ChannelHandlers/RESPEncoder+ParsingTests.swift b/Tests/NIORedisTests/ChannelHandlers/RESPEncoder+ParsingTests.swift index 657eb78..ee1fa01 100644 --- a/Tests/NIORedisTests/ChannelHandlers/RESPEncoder+ParsingTests.swift +++ b/Tests/NIORedisTests/ChannelHandlers/RESPEncoder+ParsingTests.swift @@ -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()) } }