This looks great! Huge +1 to this proposal and the desired sandbox maturity level.
I have some comments on the proposal below. It's mostly just small things that I think could make the API more consistent w/ Swift and NIO. But, as it stands, I think this proposal is good to move forward regardless of whether these changes are made.
Thanks, @Mordil !
RESPTranslator
as a struct
We may want to add some configuration options for RESP translation in the future. If RESPTranslator
were a struct with instance methods, we could pass those options in during init without needing to change the parsing / serialization API. This would give us more flexibility in the future.
RESPTranslator.parseBytes
using position
Changing the current reader index of a ByteBuffer
is a very cheap operation. Generally, you should always use the read...
prefixed methods that mutate a buffer instead of the get
methods plus a custom offset.
Because of this, I think we should expect that the buffer supplied to RESPTranslator.parseBytes
has the message as the first readable byte, so that we can omit the fromIndex: inout Int
. This would simplify the API by relying on the great work the NIO team has already done in ByteBuffer.
RESPValueConvertible
API
I think this package could benefit from a convertible-protocol pattern that Vapor's DB modules are using:
public protocol FooConvertible {
init?(foo: Foo)
var foo: Foo? { get }
}
There are a few key differences with this pattern compared to RESPValueConvertible
:
1: The init method uses a label. Given that very heavily used types like String
and Int
will conform, we should probably avoid overloading method names like init(_:)
too much.
2: The convert-to method allows returning nil
if the conversion fails (just like the init method does)
3. This more closely matches patterns already found in Swift like CustomStringConvertible
.
Bootstrap helpers as extensions on NIO types
NIO at least offers bootstrap helpers (like addHTTPHandlers()
) as methods on NIO types like ChannelPipeline
. Perhaps this package should consider matching that instead of using static methods on the Redis
type.
sub-class vs. protocol
I'd personally rather see RedisCommandHandler
being a protocol w/ default extensions than an open
class. IMO inheritance is only really appropriate for Objective-C compatible development (like w/ UIKit, AppKit, etc). With pure Swift, protocols + extensions can do the same job, while also being composable.
Redis.makeConnection
I think this method would make more sense on RedisConnection
itself. If I were attempting to create a new RedisConnection
in my code (without reading the docs first), I would probably write something like:
import RedisNIO
let conn = RedisConnection.
After writing this, I'd inspect the auto-completion options available. At the current state, I'd see the init(channel:logger:)
method which I might think to use, but not the actual method I need.
If the makeConnection
(maybe call it connect
?) method were statically available on RedisConnection
, I would find it immediately.
Potentially init(channel:logger:)
could consider being made internal, too. There's really no reason to use it outside of testing, where one could use @testable import
anyway.