Commit fa227b0e authored by Nathan Harris's avatar Nathan Harris

59 -- Use `RESPValueConvertible` as Generic Constraint

Motivation:

Johannes continues to provide great insight, and correctly pointed out that `RESPValueConvertible` was being used as an "existential" in all cases.

This can cause unexpected type-erasure and introduce unnecessary cost overhead with dynamic dispatch when in most cases we know the exact value we want for `RESPValue` to execute commands.

Modifications:

- Add new extensions to `Array where Element == RESPValue` for appending and adding elements into them
- Change `RedisClient.send(command:with:)` to require `[RESPValue]` instead of `[RESPValueConvertible]` as the `with` argument type
- Change all instances of `RESPValueConvertible` being an "existential" type for method arguments to instead be a generic constraint

Result:

The library should be safeguarded from a class of bugs, with the use of `send` being a bit more straight forward, with some new convenience methods for `[RESPValue]` types.
parent e964ba04
Pipeline #69350304 passed with stages
in 10 minutes and 55 seconds
......@@ -22,7 +22,8 @@ extension RedisClient {
/// - Returns: The message sent with the command.
@inlinable
public func echo(_ message: String) -> EventLoopFuture<String> {
return send(command: "ECHO", with: [message])
let args = [RESPValue(bulk: message)]
return send(command: "ECHO", with: args)
.convertFromRESPValue()
}
......@@ -33,8 +34,10 @@ extension RedisClient {
/// - Returns: The provided message or Redis' default response of `"PONG"`.
@inlinable
public func ping(with message: String? = nil) -> EventLoopFuture<String> {
let arg = message != nil ? [message] : []
return send(command: "PING", with: arg)
let args: [RESPValue] = message != nil
? [.init(bulk: message!)] // safe because we did a nil pre-check
: []
return send(command: "PING", with: args)
.convertFromRESPValue()
}
......@@ -46,7 +49,8 @@ extension RedisClient {
/// - Returns: An `EventLoopFuture` that resolves when the operation has succeeded, or fails with a `RedisError`.
@inlinable
public func select(database index: Int) -> EventLoopFuture<Void> {
return send(command: "SELECT", with: [index])
let args = [RESPValue(bulk: index)]
return send(command: "SELECT", with: args)
.map { _ in return () }
}
......@@ -59,8 +63,11 @@ extension RedisClient {
/// - Returns: `true` if the swap was successful.
@inlinable
public func swapDatabase(_ first: Int, with second: Int) -> EventLoopFuture<Bool> {
/// connection.swapDatabase(index: 0, withIndex: 10)
return send(command: "SWAPDB", with: [first, second])
let args: [RESPValue] = [
.init(bulk: first),
.init(bulk: second)
]
return send(command: "SWAPDB", with: args)
.convertFromRESPValue(to: String.self)
.map { return $0 == "OK" }
}
......@@ -74,7 +81,8 @@ extension RedisClient {
public func delete(_ keys: [String]) -> EventLoopFuture<Int> {
guard keys.count > 0 else { return self.eventLoop.makeSucceededFuture(0) }
return send(command: "DEL", with: keys)
let args = keys.map(RESPValue.init)
return send(command: "DEL", with: args)
.convertFromRESPValue()
}
......@@ -89,7 +97,11 @@ extension RedisClient {
@inlinable
public func expire(_ key: String, after timeout: TimeAmount) -> EventLoopFuture<Bool> {
let amount = timeout.nanoseconds / 1_000_000_000
return send(command: "EXPIRE", with: [key, amount])
let args: [RESPValue] = [
.init(bulk: key),
.init(bulk: amount.description)
]
return send(command: "EXPIRE", with: args)
.convertFromRESPValue(to: Int.self)
.map { return $0 == 1 }
}
......@@ -116,7 +128,7 @@ extension RedisClient {
}
@usableFromInline
func _scan<T>(
internal func _scan<T>(
command: String,
resultType: T.Type = T.self,
_ key: String?,
......@@ -127,19 +139,19 @@ extension RedisClient {
where
T: RESPValueConvertible
{
var args: [RESPValueConvertible] = [pos]
var args: [RESPValue] = [.init(bulk: pos)]
if let k = key {
args.insert(k, at: 0)
args.insert(.init(bulk: k), at: 0)
}
if let m = match {
args.append("match")
args.append(m)
args.append(.init(bulk: "match"))
args.append(.init(bulk: m))
}
if let c = count {
args.append("count")
args.append(c)
args.append(.init(bulk: "count"))
args.append(.init(bulk: c))
}
let response = send(command: command, with: args).convertFromRESPValue(to: [RESPValue].self)
......
......@@ -18,7 +18,7 @@ import NIO
extension RedisClient {
@usableFromInline
static func _mapHashResponse(_ values: [String]) -> [String: String] {
internal static func _mapHashResponse(_ values: [String]) -> [String: String] {
guard values.count > 0 else { return [:] }
var result: [String: String] = [:]
......@@ -48,8 +48,11 @@ extension RedisClient {
@inlinable
public func hdel(_ fields: [String], from key: String) -> EventLoopFuture<Int> {
guard fields.count > 0 else { return self.eventLoop.makeSucceededFuture(0) }
var args: [RESPValue] = [.init(bulk: key)]
args.append(convertingContentsOf: fields)
return send(command: "HDEL", with: [key] + fields)
return send(command: "HDEL", with: args)
.convertFromRESPValue()
}
......@@ -62,7 +65,11 @@ extension RedisClient {
/// - Returns: `true` if the hash contains the field, `false` if either the key or field do not exist.
@inlinable
public func hexists(_ field: String, in key: String) -> EventLoopFuture<Bool> {
return send(command: "HEXISTS", with: [key, field])
let args: [RESPValue] = [
.init(bulk: key),
.init(bulk: field)
]
return send(command: "HEXISTS", with: args)
.convertFromRESPValue(to: Int.self)
.map { return $0 == 1 }
}
......@@ -74,7 +81,8 @@ extension RedisClient {
/// - Returns: The number of fields in the hash, or `0` if the key doesn't exist.
@inlinable
public func hlen(of key: String) -> EventLoopFuture<Int> {
return send(command: "HLEN", with: [key])
let args = [RESPValue(bulk: key)]
return send(command: "HLEN", with: args)
.convertFromRESPValue()
}
......@@ -87,7 +95,11 @@ extension RedisClient {
/// - Returns: The string length of the hash field's value, or `0` if the field or hash do not exist.
@inlinable
public func hstrlen(of field: String, in key: String) -> EventLoopFuture<Int> {
return send(command: "HSTRLEN", with: [key, field])
let args: [RESPValue] = [
.init(bulk: key),
.init(bulk: field)
]
return send(command: "HSTRLEN", with: args)
.convertFromRESPValue()
}
......@@ -98,7 +110,8 @@ extension RedisClient {
/// - Returns: A list of field names stored within the hash.
@inlinable
public func hkeys(in key: String) -> EventLoopFuture<[String]> {
return send(command: "HKEYS", with: [key])
let args = [RESPValue(bulk: key)]
return send(command: "HKEYS", with: args)
.convertFromRESPValue()
}
......@@ -109,7 +122,8 @@ extension RedisClient {
/// - Returns: A list of all values stored in a hash.
@inlinable
public func hvals(in key: String) -> EventLoopFuture<[RESPValue]> {
return send(command: "HVALS", with: [key])
let args = [RESPValue(bulk: key)]
return send(command: "HVALS", with: args)
.convertFromRESPValue()
}
......@@ -150,12 +164,17 @@ extension RedisClient {
/// - key: The key that holds the hash.
/// - Returns: `true` if the hash was created, `false` if it was updated.
@inlinable
public func hset(
public func hset<Value: RESPValueConvertible>(
_ field: String,
to value: RESPValueConvertible,
to value: Value,
in key: String
) -> EventLoopFuture<Bool> {
return send(command: "HSET", with: [key, field, value])
let args: [RESPValue] = [
.init(bulk: key),
.init(bulk: field),
value.convertedToRESPValue()
]
return send(command: "HSET", with: args)
.convertFromRESPValue(to: Int.self)
.map { return $0 == 1 }
}
......@@ -170,12 +189,17 @@ extension RedisClient {
/// - key: The key that holds the hash.
/// - Returns: `true` if the hash was created.
@inlinable
public func hsetnx(
public func hsetnx<Value: RESPValueConvertible>(
_ field: String,
to value: RESPValueConvertible,
to value: Value,
in key: String
) -> EventLoopFuture<Bool> {
return send(command: "HSETNX", with: [key, field, value])
let args: [RESPValue] = [
.init(bulk: key),
.init(bulk: field),
value.convertedToRESPValue()
]
return send(command: "HSETNX", with: args)
.convertFromRESPValue(to: Int.self)
.map { return $0 == 1 }
}
......@@ -188,18 +212,19 @@ extension RedisClient {
/// - key: The key that holds the hash.
/// - Returns: An `EventLoopFuture` that resolves when the operation has succeeded, or fails with a `RedisError`.
@inlinable
public func hmset(
_ fields: [String: RESPValueConvertible],
public func hmset<Value: RESPValueConvertible>(
_ fields: [String: Value],
in key: String
) -> EventLoopFuture<Void> {
assert(fields.count > 0, "At least 1 key-value pair should be specified")
let args: [RESPValueConvertible] = fields.reduce(into: [], { (result, element) in
result.append(element.key)
result.append(element.value)
})
var args: [RESPValue] = [.init(bulk: key)]
args.add(contentsOf: fields, overestimatedCountBeingAdded: fields.count * 2) { (array, element) in
array.append(.init(bulk: element.key))
array.append(element.value.convertedToRESPValue())
}
return send(command: "HMSET", with: [key] + args)
return send(command: "HMSET", with: args)
.map { _ in () }
}
}
......@@ -216,7 +241,11 @@ extension RedisClient {
/// - Returns: The value of the hash field, or `nil` if either the key or field does not exist.
@inlinable
public func hget(_ field: String, from key: String) -> EventLoopFuture<String?> {
return send(command: "HGET", with: [key, field])
let args: [RESPValue] = [
.init(bulk: key),
.init(bulk: field)
]
return send(command: "HGET", with: args)
.map { return String(fromRESP: $0) }
}
......@@ -230,8 +259,11 @@ extension RedisClient {
@inlinable
public func hmget(_ fields: [String], from key: String) -> EventLoopFuture<[String?]> {
guard fields.count > 0 else { return self.eventLoop.makeSucceededFuture([]) }
var args: [RESPValue] = [.init(bulk: key)]
args.append(convertingContentsOf: fields)
return send(command: "HMGET", with: [key] + fields)
return send(command: "HMGET", with: args)
.convertFromRESPValue(to: [RESPValue].self)
.map { return $0.map(String.init) }
}
......@@ -243,7 +275,8 @@ extension RedisClient {
/// - Returns: A key-value pair list of fields and their values.
@inlinable
public func hgetall(from key: String) -> EventLoopFuture<[String: String]> {
return send(command: "HGETALL", with: [key])
let args = [RESPValue(bulk: key)]
return send(command: "HGETALL", with: args)
.convertFromRESPValue(to: [String].self)
.map(Self._mapHashResponse)
}
......@@ -262,9 +295,7 @@ extension RedisClient {
/// - Returns: The new value of the hash field.
@inlinable
public func hincrby(_ amount: Int, field: String, in key: String) -> EventLoopFuture<Int> {
/// connection.hincrby(20, field: "foo", in: "key")
return send(command: "HINCRBY", with: [key, field, amount])
.convertFromRESPValue()
return _hincr(command: "HINCRBY", amount, field, key)
}
/// Increments a hash field's value and returns the new value.
......@@ -276,12 +307,27 @@ extension RedisClient {
/// - key: The key of the hash the field is stored in.
/// - Returns: The new value of the hash field.
@inlinable
public func hincrbyfloat<T>(_ amount: T, field: String, in key: String) -> EventLoopFuture<T>
public func hincrbyfloat<Value>(_ amount: Value, field: String, in key: String) -> EventLoopFuture<Value>
where
T: BinaryFloatingPoint,
T: RESPValueConvertible
Value: BinaryFloatingPoint,
Value: RESPValueConvertible
{
return send(command: "HINCRBYFLOAT", with: [key, field, amount])
return _hincr(command: "HINCRBYFLOAT", amount, field, key)
}
@usableFromInline
internal func _hincr<Value: RESPValueConvertible>(
command: String,
_ amount: Value,
_ field: String,
_ key: String
) -> EventLoopFuture<Value> {
let args: [RESPValue] = [
.init(bulk: key),
.init(bulk: field),
amount.convertedToRESPValue()
]
return send(command: command, with: args)
.convertFromRESPValue()
}
}
......@@ -27,7 +27,8 @@ extension RedisClient {
/// - Returns: A list of elements found within the set.
@inlinable
public func smembers(of key: String) -> EventLoopFuture<[RESPValue]> {
return send(command: "SMEMBERS", with: [key])
let args = [RESPValue(bulk: key)]
return send(command: "SMEMBERS", with: args)
.convertFromRESPValue()
}
......@@ -39,8 +40,12 @@ extension RedisClient {
/// - key: The key of the set to look in.
/// - Returns: `true` if the element is in the set.
@inlinable
public func sismember(_ element: RESPValueConvertible, of key: String) -> EventLoopFuture<Bool> {
return send(command: "SISMEMBER", with: [key, element])
public func sismember<Value: RESPValueConvertible>(_ element: Value, of key: String) -> EventLoopFuture<Bool> {
let args: [RESPValue] = [
.init(bulk: key),
element.convertedToRESPValue()
]
return send(command: "SISMEMBER", with: args)
.convertFromRESPValue(to: Int.self)
.map { return $0 == 1 }
}
......@@ -52,7 +57,8 @@ extension RedisClient {
/// - Returns: The total count of elements in the set.
@inlinable
public func scard(of key: String) -> EventLoopFuture<Int> {
return send(command: "SCARD", with: [key])
let args = [RESPValue(bulk: key)]
return send(command: "SCARD", with: args)
.convertFromRESPValue()
}
......@@ -64,10 +70,13 @@ extension RedisClient {
/// - key: The key of the set to insert into.
/// - Returns: The number of elements that were added to the set.
@inlinable
public func sadd(_ elements: [RESPValueConvertible], to key: String) -> EventLoopFuture<Int> {
public func sadd<Value: RESPValueConvertible>(_ elements: [Value], to key: String) -> EventLoopFuture<Int> {
guard elements.count > 0 else { return self.eventLoop.makeSucceededFuture(0) }
var args: [RESPValue] = [.init(bulk: key)]
args.append(convertingContentsOf: elements)
return send(command: "SADD", with: [key] + elements)
return send(command: "SADD", with: args)
.convertFromRESPValue()
}
......@@ -79,10 +88,13 @@ extension RedisClient {
/// - key: The key of the set to remove from.
/// - Returns: The number of elements that were removed from the set.
@inlinable
public func srem(_ elements: [RESPValueConvertible], from key: String) -> EventLoopFuture<Int> {
public func srem<Value: RESPValueConvertible>(_ elements: [Value], from key: String) -> EventLoopFuture<Int> {
guard elements.count > 0 else { return self.eventLoop.makeSucceededFuture(0) }
return send(command: "SREM", with: [key] + elements)
var args: [RESPValue] = [.init(bulk: key)]
args.append(convertingContentsOf: elements)
return send(command: "SREM", with: args)
.convertFromRESPValue()
}
......@@ -98,8 +110,12 @@ extension RedisClient {
assert(count >= 0, "A negative max count is nonsense.")
guard count > 0 else { return self.eventLoop.makeSucceededFuture([]) }
return send(command: "SPOP", with: [key, count])
let args: [RESPValue] = [
.init(bulk: key),
.init(bulk: count)
]
return send(command: "SPOP", with: args)
.convertFromRESPValue()
}
......@@ -118,7 +134,11 @@ extension RedisClient {
public func srandmember(from key: String, max count: Int = 1) -> EventLoopFuture<[RESPValue]> {
guard count != 0 else { return self.eventLoop.makeSucceededFuture([]) }
return send(command: "SRANDMEMBER", with: [key, count])
let args: [RESPValue] = [
.init(bulk: key),
.init(bulk: count)
]
return send(command: "SRANDMEMBER", with: args)
.convertFromRESPValue()
}
......@@ -131,14 +151,19 @@ extension RedisClient {
/// - destKey: The key of the destination set.
/// - Returns: `true` if the element was successfully removed from the source set.
@inlinable
public func smove(
_ element: RESPValueConvertible,
public func smove<Value: RESPValueConvertible>(
_ element: Value,
from sourceKey: String,
to destKey: String
) -> EventLoopFuture<Bool> {
guard sourceKey != destKey else { return self.eventLoop.makeSucceededFuture(true) }
return send(command: "SMOVE", with: [sourceKey, destKey, element])
let args: [RESPValue] = [
.init(bulk: sourceKey),
.init(bulk: destKey),
element.convertedToRESPValue()
]
return send(command: "SMOVE", with: args)
.convertFromRESPValue()
.map { return $0 == 1 }
}
......@@ -175,7 +200,8 @@ extension RedisClient {
public func sdiff(of keys: [String]) -> EventLoopFuture<[RESPValue]> {
guard keys.count > 0 else { return self.eventLoop.makeSucceededFuture([]) }
return send(command: "SDIFF", with: keys)
let args = keys.map(RESPValue.init)
return send(command: "SDIFF", with: args)
.convertFromRESPValue()
}
......@@ -191,7 +217,10 @@ extension RedisClient {
public func sdiffstore(as destination: String, sources keys: [String]) -> EventLoopFuture<Int> {
assert(keys.count > 0, "At least 1 key should be provided.")
return send(command: "SDIFFSTORE", with: [destination] + keys)
var args: [RESPValue] = [.init(bulk: destination)]
args.append(convertingContentsOf: keys)
return send(command: "SDIFFSTORE", with: args)
.convertFromRESPValue()
}
}
......@@ -208,7 +237,8 @@ extension RedisClient {
public func sinter(of keys: [String]) -> EventLoopFuture<[RESPValue]> {
guard keys.count > 0 else { return self.eventLoop.makeSucceededFuture([]) }
return send(command: "SINTER", with: keys)
let args = keys.map(RESPValue.init)
return send(command: "SINTER", with: args)
.convertFromRESPValue()
}
......@@ -224,7 +254,10 @@ extension RedisClient {
public func sinterstore(as destination: String, sources keys: [String]) -> EventLoopFuture<Int> {
assert(keys.count > 0, "At least 1 key should be provided.")
return send(command: "SINTERSTORE", with: [destination] + keys)
var args: [RESPValue] = [.init(bulk: destination)]
args.append(convertingContentsOf: keys)
return send(command: "SINTERSTORE", with: args)
.convertFromRESPValue()
}
}
......@@ -241,7 +274,8 @@ extension RedisClient {
public func sunion(of keys: [String]) -> EventLoopFuture<[RESPValue]> {
guard keys.count > 0 else { return self.eventLoop.makeSucceededFuture([]) }
return send(command: "SUNION", with: keys)
let args = keys.map(RESPValue.init)
return send(command: "SUNION", with: args)
.convertFromRESPValue()
}
......@@ -257,7 +291,10 @@ extension RedisClient {
public func sunionstore(as destination: String, sources keys: [String]) -> EventLoopFuture<Int> {
assert(keys.count > 0, "At least 1 key should be provided.")
return send(command: "SUNIONSTORE", with: [destination] + keys)
var args: [RESPValue] = [.init(bulk: destination)]
args.append(convertingContentsOf: keys)
return send(command: "SUNIONSTORE", with: args)
.convertFromRESPValue()
}
}
......@@ -26,7 +26,8 @@ extension RedisClient {
/// - Returns: The string value stored at the key provided, otherwise `nil` if the key does not exist.
@inlinable
public func get(_ key: String) -> EventLoopFuture<String?> {
return send(command: "GET", with: [key])
let args = [RESPValue(bulk: key)]
return send(command: "GET", with: args)
.map { return $0.string }
}
......@@ -39,7 +40,8 @@ extension RedisClient {
public func mget(_ keys: [String]) -> EventLoopFuture<[RESPValue]> {
guard keys.count > 0 else { return self.eventLoop.makeSucceededFuture([]) }
return send(command: "MGET", with: keys)
let args = keys.map(RESPValue.init)
return send(command: "MGET", with: args)
.convertFromRESPValue()
}
}
......@@ -47,6 +49,24 @@ extension RedisClient {
// MARK: Set
extension RedisClient {
/// Append a value to the end of an existing entry.
/// - Note: If the key does not exist, it is created and set as an empty string, so `APPEND` will be similar to `SET` in this special case.
///
/// See [https://redis.io/commands/append](https://redis.io/commands/append)
/// - Parameters:
/// - value: The value to append onto the value stored at the key.
/// - key: The key to use to uniquely identify this value.
/// - Returns: The length of the key's value after appending the additional value.
@inlinable
public func append<Value: RESPValueConvertible>(_ value: Value, to key: String) -> EventLoopFuture<Int> {
let args: [RESPValue] = [
.init(bulk: key),
value.convertedToRESPValue()
]
return send(command: "APPEND", with: args)
.convertFromRESPValue()
}
/// Sets the value stored in the key provided, overwriting the previous value.
///
/// Any previous expiration set on the key is discarded if the SET operation was successful.
......@@ -59,8 +79,12 @@ extension RedisClient {
/// - value: The value to set the key to.
/// - Returns: An `EventLoopFuture` that resolves if the operation was successful.
@inlinable
public func set(_ key: String, to value: RESPValueConvertible) -> EventLoopFuture<Void> {
return send(command: "SET", with: [key, value])
public func set<Value: RESPValueConvertible>(_ key: String, to value: Value) -> EventLoopFuture<Void> {
let args: [RESPValue] = [
.init(bulk: key),
value.convertedToRESPValue()
]
return send(command: "SET", with: args)
.map { _ in () }
}
......@@ -71,7 +95,7 @@ extension RedisClient {
/// - Parameter operations: The key-value list of SET operations to execute.
/// - Returns: An `EventLoopFuture` that resolves if the operation was successful.
@inlinable
public func mset(_ operations: [String: RESPValueConvertible]) -> EventLoopFuture<Void> {
public func mset<Value: RESPValueConvertible>(_ operations: [String: Value]) -> EventLoopFuture<Void> {
return _mset(command: "MSET", operations)
.map { _ in () }
}
......@@ -83,37 +107,26 @@ extension RedisClient {
/// - Parameter operations: The key-value list of SET operations to execute.
/// - Returns: `true` if the operation successfully completed.
@inlinable
public func msetnx(_ operations: [String: RESPValueConvertible]) -> EventLoopFuture<Bool> {
public func msetnx<Value: RESPValueConvertible>(_ operations: [String: Value]) -> EventLoopFuture<Bool> {
return _mset(command: "MSETNX", operations)
.convertFromRESPValue(to: Int.self)
.map { return $0 == 1 }
}
/// Append a value to the end of an existing entry.
/// - Note: If the key does not exist, it is created and set as an empty string, so `APPEND` will be similar to `SET` in this special case.
///
/// See [https://redis.io/commands/append](https://redis.io/commands/append)
/// - Parameters:
/// - value: The value to append onto the value stored at the key.
/// - key: The key to use to uniquely identify this value.
/// - Returns: The length of the key's value after appending the additional value.
@inlinable
public func append(_ value: RESPValueConvertible, to key: String) -> EventLoopFuture<Int> {
return send(command: "APPEND", with: [key, value])
.convertFromRESPValue()
}
@usableFromInline
func _mset(
func _mset<Value: RESPValueConvertible>(
command: String,
_ operations: [String: RESPValueConvertible]
_ operations: [String: Value]
) -> EventLoopFuture<RESPValue> {
assert(operations.count > 0, "At least 1 key-value pair should be provided.")
let args: [RESPValueConvertible] = operations.reduce(into: [], { (result, element) in
result.append(element.key)
result.append(element.value)
})
let args: [RESPValue] = operations.reduce(
into: .init(initialCapacity: operations.count * 2),
{ (array, element) in
array.append(.init(bulk: element.key))
array.append(element.value.convertedToRESPValue())
}
)
return send(command: command, with: args)
}
......@@ -129,7 +142,8 @@ extension RedisClient {
/// - Returns: The new value after the operation.
@inlinable
public func increment(_ key: String) -> EventLoopFuture<Int> {
return send(command: "INCR", with: [key])
let args = [RESPValue(bulk: key)]
return send(command: "INCR", with: args)
.convertFromRESPValue()
}
......@@ -142,7 +156,11 @@ extension RedisClient {
/// - Returns: The new value after the operation.
@inlinable
public func increment(_ key: String, by count: Int) -> EventLoopFuture<Int> {
return send(command: "INCRBY", with: [key, count])
let args: [RESPValue] = [
.init(bulk: key),
.init(bulk: count)
]
return send(command: "INCRBY", with: args)
.convertFromRESPValue()
}
......@@ -154,10 +172,16 @@ extension RedisClient {
/// - count: The amount that this value should be incremented, supporting both positive and negative values.
/// - Returns: The new value after the operation.
@inlinable
public func increment<T: BinaryFloatingPoint>(_ key: String, by count: T) -> EventLoopFuture<T>
where T: RESPValueConvertible
public func increment<Value>(_ key: String, by count: Value) -> EventLoopFuture<Value>
where
Value: BinaryFloatingPoint,
Value: RESPValueConvertible
{
return send(command: "INCRBYFLOAT", with: [key, count])
let args: [RESPValue] = [
.init(bulk: key),
count.convertedToRESPValue()
]
return send(command: "INCRBYFLOAT", with: args)
.convertFromRESPValue()
}
}
......@@ -172,7 +196,8 @@ extension RedisClient {
/// - Returns: The new value after the operation.
@inlinable
public func decrement(_ key: String) -> EventLoopFuture<Int> {
return send(command: "DECR", with: [key])
let args = [RESPValue(bulk: key)]
return send(command: "DECR", with: args)
.convertFromRESPValue()
}
......@@ -184,7 +209,11 @@ extension RedisClient {
/// - count: The amount that this value should be decremented, supporting both positive and negative values.
/// - Returns: The new value after the operation.
public func decrement(_ key: String, by count: Int) -> EventLoopFuture<Int> {
return send(command: "DECRBY", with: [key, count])
let args: [RESPValue] = [
.init(bulk: key),
.init(bulk: count)
]
return send(command: "DECRBY", with: args)
.convertFromRESPValue()
}
}
//===----------------------------------------------------------------------===//
//