Add variadic overloads for several commands

Motivation:

For ergonomics, users sometimes want to provide arguments as a variadic list rather than an array.

Modifications:

- Add variadic overloads for almost all methods that accept lists of homogenous types

Result:

Users should have more flexibility in the way arguments are passed to command methods
parent 8ce1dd82
Pipeline #91774434 passed with stage
in 3 minutes and 11 seconds
......@@ -85,6 +85,16 @@ extension RedisClient {
return send(command: "DEL", with: args)
.convertFromRESPValue()
}
/// Removes the specified keys. A key is ignored if it does not exist.
///
/// [https://redis.io/commands/del](https://redis.io/commands/del)
/// - Parameter keys: A list of keys to delete from the database.
/// - Returns: The number of keys deleted from the database.
@inlinable
public func delete(_ keys: String...) -> EventLoopFuture<Int> {
return self.delete(keys)
}
/// Sets a timeout on key. After the timeout has expired, the key will automatically be deleted.
/// - Note: A key with an associated timeout is often said to be "volatile" in Redis terminology.
......
......@@ -55,6 +55,18 @@ extension RedisClient {
return send(command: "HDEL", with: args)
.convertFromRESPValue()
}
/// Removes the specified fields from a hash.
///
/// See [https://redis.io/commands/hdel](https://redis.io/commands/hdel)
/// - Parameters:
/// - fields: The list of field names that should be removed from the hash.
/// - key: The key of the hash to delete from.
/// - Returns: The number of fields that were deleted.
@inlinable
public func hdel(_ fields: String..., from key: String) -> EventLoopFuture<Int> {
return self.hdel(fields, from: key)
}
/// Checks if a hash contains the field specified.
///
......@@ -267,6 +279,18 @@ extension RedisClient {
.convertFromRESPValue(to: [RESPValue].self)
.map { return $0.map(String.init) }
}
/// Gets the values of a hash for the fields specified.
///
/// See [https://redis.io/commands/hmget](https://redis.io/commands/hmget)
/// - Parameters:
/// - fields: A list of field names to get values for.
/// - key: The key of the hash being accessed.
/// - Returns: A list of values in the same order as the `fields` argument. Non-existent fields return `nil` values.
@inlinable
public func hmget(_ fields: String..., from key: String) -> EventLoopFuture<[String?]> {
return self.hmget(fields, from: key)
}
/// Returns all the fields and values stored in a hash.
///
......
......@@ -267,6 +267,19 @@ extension RedisClient {
return send(command: "LPUSH", with: args)
.convertFromRESPValue()
}
/// Pushes all of the provided elements into a list.
/// - Note: This inserts the elements at the head of the list; for the tail see `rpush(_:into:)`.
///
/// See [https://redis.io/commands/lpush](https://redis.io/commands/lpush)
/// - Parameters:
/// - elements: The values to push into the list.
/// - key: The key of the list.
/// - Returns: The length of the list after adding the new elements.
@inlinable
public func lpush<Value: RESPValueConvertible>(_ elements: Value..., into key: String) -> EventLoopFuture<Int> {
return self.lpush(elements, into: key)
}
/// Pushes an element into a list, but only if the key exists and holds a list.
/// - Note: This inserts the element at the head of the list, for the tail see `rpushx(_:into:)`.
......@@ -318,6 +331,18 @@ extension RedisClient {
return send(command: "RPUSH", with: args)
.convertFromRESPValue()
}
/// Pushes all of the provided elements into a list.
/// - Note: This inserts the elements at the tail of the list; for the head see `lpush(_:into:)`.
///
/// See [https://redis.io/commands/rpush](https://redis.io/commands/rpush)
/// - elements: The values to push into the list.
/// - key: The key of the list.
/// - Returns: The length of the list after adding the new elements.
@inlinable
public func rpush<Value: RESPValueConvertible>(_ elements: Value..., into key: String) -> EventLoopFuture<Int> {
return self.rpush(elements, into: key)
}
/// Pushes an element into a list, but only if the key exists and holds a list.
/// - Note: This inserts the element at the tail of the list; for the head see `lpushx(_:into:)`.
......@@ -378,12 +403,31 @@ extension RedisClient {
///
/// Otherwise, the key of the list the element was removed from and the popped element.
@inlinable
public func blpop(
from keys: [String],
timeout: Int = 0
) -> EventLoopFuture<(String, RESPValue)?> {
public func blpop(from keys: [String], timeout: Int = 0) -> EventLoopFuture<(String, RESPValue)?> {
return _bpop(command: "BLPOP", keys, timeout)
}
/// Removes the first element of a list, blocking until an element is available.
///
/// - Important:
/// This will block the connection from completing further commands until an element
/// is available to pop from the group of lists.
///
/// It is **highly** recommended to set a reasonable `timeout`
/// or to use the non-blocking `lpop` method where possible.
///
/// See [https://redis.io/commands/blpop](https://redis.io/commands/blpop)
/// - Parameters:
/// - keys: The keys of lists in Redis that should be popped from.
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
/// - Returns:
/// If timeout was reached, `nil`.
///
/// Otherwise, the key of the list the element was removed from and the popped element.
@inlinable
public func blpop(from keys: String..., timeout: Int = 0) -> EventLoopFuture<(String, RESPValue)?> {
return self.blpop(from: keys, timeout: timeout)
}
/// Removes the last element of a list, blocking until an element is available.
///
......@@ -422,13 +466,32 @@ extension RedisClient {
///
/// Otherwise, the key of the list the element was removed from and the popped element.
@inlinable
public func brpop(
from keys: [String],
timeout: Int = 0
) -> EventLoopFuture<(String, RESPValue)?> {
public func brpop(from keys: [String], timeout: Int = 0) -> EventLoopFuture<(String, RESPValue)?> {
return _bpop(command: "BRPOP", keys, timeout)
}
/// Removes the last element of a list, blocking until an element is available.
///
/// - Important:
/// This will block the connection from completing further commands until an element
/// is available to pop from the group of lists.
///
/// It is **highly** recommended to set a reasonable `timeout`
/// or to use the non-blocking `rpop` method where possible.
///
/// See [https://redis.io/commands/brpop](https://redis.io/commands/brpop)
/// - Parameters:
/// - keys: The keys of lists in Redis that should be popped from.
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
/// - Returns:
/// If timeout was reached, `nil`.
///
/// Otherwise, the key of the list the element was removed from and the popped element.
@inlinable
public func brpop(from keys: String..., timeout: Int = 0) -> EventLoopFuture<(String, RESPValue)?> {
return self.brpop(from: keys, timeout: timeout)
}
@usableFromInline
func _bpop(
command: String,
......
......@@ -79,6 +79,18 @@ extension RedisClient {
return send(command: "SADD", with: args)
.convertFromRESPValue()
}
/// Adds elements to a set.
///
/// See [https://redis.io/commands/sadd](https://redis.io/commands/sadd)
/// - Parameters:
/// - elements: The values to add to the set.
/// - key: The key of the set to insert into.
/// - Returns: The number of elements that were added to the set.
@inlinable
public func sadd<Value: RESPValueConvertible>(_ elements: Value..., to key: String) -> EventLoopFuture<Int> {
return self.sadd(elements, to: key)
}
/// Removes elements from a set.
///
......@@ -97,6 +109,18 @@ extension RedisClient {
return send(command: "SREM", with: args)
.convertFromRESPValue()
}
/// Removes elements from a set.
///
/// See [https://redis.io/commands/srem](https://redis.io/commands/srem)
/// - Parameters:
/// - elements: The values to remove from the set.
/// - key: The key of the set to remove from.
/// - Returns: The number of elements that were removed from the set.
@inlinable
public func srem<Value: RESPValueConvertible>(_ elements: Value..., from key: String) -> EventLoopFuture<Int> {
return self.srem(elements, from: key)
}
/// Randomly selects and removes one or more elements in a set.
///
......@@ -204,6 +228,16 @@ extension RedisClient {
return send(command: "SDIFF", with: args)
.convertFromRESPValue()
}
/// Calculates the difference between two or more sets.
///
/// See [https://redis.io/commands/sdiff](https://redis.io/commands/sdiff)
/// - Parameter keys: The source sets to calculate the difference of.
/// - Returns: A list of elements resulting from the difference.
@inlinable
public func sdiff(of keys: String...) -> EventLoopFuture<[RESPValue]> {
return self.sdiff(of: keys)
}
/// Calculates the difference between two or more sets and stores the result.
/// - Important: If the destination key already exists, it is overwritten.
......@@ -241,6 +275,16 @@ extension RedisClient {
return send(command: "SINTER", with: args)
.convertFromRESPValue()
}
/// Calculates the intersection of two or more sets.
///
/// See [https://redis.io/commands/sinter](https://redis.io/commands/sinter)
/// - Parameter keys: The source sets to calculate the intersection of.
/// - Returns: A list of elements resulting from the intersection.
@inlinable
public func sinter(of keys: String...) -> EventLoopFuture<[RESPValue]> {
return self.sinter(of: keys)
}
/// Calculates the intersetion of two or more sets and stores the result.
/// - Important: If the destination key already exists, it is overwritten.
......@@ -278,6 +322,16 @@ extension RedisClient {
return send(command: "SUNION", with: args)
.convertFromRESPValue()
}
/// Calculates the union of two or more sets.
///
/// See [https://redis.io/commands/sunion](https://redis.io/commands/sunion)
/// - Parameter keys: The source sets to calculate the union of.
/// - Returns: A list of elements resulting from the union.
@inlinable
public func sunion(of keys: String...) -> EventLoopFuture<[RESPValue]> {
return self.sunion(of: keys)
}
/// Calculates the union of two or more sets and stores the result.
/// - Important: If the destination key already exists, it is overwritten.
......
......@@ -93,6 +93,29 @@ extension RedisClient {
return send(command: "ZADD", with: args)
.convertFromRESPValue()
}
/// Adds elements to a sorted set, assigning their score to the values provided.
/// - Note: `INCR` is not supported by this library in `zadd`. Use the `zincrby(:element:in:)` method instead.
///
/// See [https://redis.io/commands/zadd](https://redis.io/commands/zadd)
/// - Parameters:
/// - elements: A list of elements and their score to add to the sorted set.
/// - key: The key of the sorted set.
/// - option: An option for modifying the behavior of the command.
/// - returnChangedCount: `zadd` normally returns the number of new elements added to the set,
/// but setting this to `true` will instead have the command return the number of elements changed.
///
/// "Changed" in this context are new elements added, and elements that had their score updated.
/// - Returns: The number of elements added to the sorted set, unless `returnChangedCount` was set to `true`.
@inlinable
public func zadd<Value: RESPValueConvertible>(
_ elements: (element: Value, score: Double)...,
to key: String,
option: RedisSortedSetAddOption? = nil,
returnChangedCount: Bool = false
) -> EventLoopFuture<Int> {
return self.zadd(elements, to: key, option: option, returnChangedCount: returnChangedCount)
}
/// Adds an element to a sorted set, assigning their score to the value provided.
///
......@@ -818,6 +841,18 @@ extension RedisClient {
return send(command: "ZREM", with: args)
.convertFromRESPValue()
}
/// Removes the specified elements from a sorted set.
///
/// See [https://redis.io/commands/zrem](https://redis.io/commands/zrem)
/// - Parameters:
/// - elements: The values to remove from the sorted set.
/// - key: The key of the sorted set.
/// - Returns: The number of elements removed from the set.
@inlinable
public func zrem<Value: RESPValueConvertible>(_ elements: Value..., from key: String) -> EventLoopFuture<Int> {
return self.zrem(elements, from: key)
}
/// Removes elements from a sorted set whose lexiographical values are between the range specified.
/// - Important: This assumes all elements in the sorted set have the same score. If not, the elements selected are unspecified.
......
......@@ -57,6 +57,16 @@ extension RedisClient {
return send(command: "MGET", with: args)
.convertFromRESPValue()
}
/// Gets the values of all specified keys, using `.null` to represent non-existant values.
///
/// See [https://redis.io/commands/mget](https://redis.io/commands/mget)
/// - Parameter keys: The list of keys to fetch the values from.
/// - Returns: The values stored at the keys provided, matching the same order.
@inlinable
public func mget(_ keys: String...) -> EventLoopFuture<[RESPValue]> {
return self.mget(keys)
}
}
// MARK: Set
......
......@@ -33,7 +33,7 @@ final class BasicCommandsTests: RediStackIntegrationTestCase {
let second = try connection.delete([keys[0]]).wait()
XCTAssertEqual(second, 0)
let third = try connection.delete([keys[1], keys[2]]).wait()
let third = try connection.delete(keys[1], keys[2]).wait()
XCTAssertEqual(third, 2)
}
......
......@@ -48,7 +48,7 @@ final class HashCommandsTests: RediStackIntegrationTestCase {
func test_hmget() throws {
_ = try connection.hmset(["first": "foo", "second": "bar"], in: #function).wait()
let values = try connection.hmget(["first", "second", "fake"], from: #function).wait()
let values = try connection.hmget("first", "second", "fake", from: #function).wait()
XCTAssertEqual(values[0], "foo")
XCTAssertEqual(values[1], "bar")
XCTAssertNil(values[2])
......@@ -63,7 +63,7 @@ final class HashCommandsTests: RediStackIntegrationTestCase {
func test_hdel() throws {
_ = try connection.hmset(["first": "foo", "second": "bar"], in: #function).wait()
let count = try connection.hdel(["first", "second", "fake"], from: #function).wait()
let count = try connection.hdel("first", "second", "fake", from: #function).wait()
XCTAssertEqual(count, 2)
}
......
......@@ -140,7 +140,7 @@ final class ListCommandsTests: RediStackIntegrationTestCase {
let pop1 = try connection.blpop(from: "first").wait() ?? .null
XCTAssertEqual(Int(fromRESP: pop1), 30)
let pop2 = try connection.blpop(from: ["fake", "first"]).wait()
let pop2 = try connection.blpop(from: "fake", "first").wait()
XCTAssertEqual(pop2?.0, "first")
let blockingConnection = try self.makeNewConnection()
......@@ -158,7 +158,7 @@ final class ListCommandsTests: RediStackIntegrationTestCase {
func test_lpush() throws {
_ = try connection.rpush([10, 20, 30], into: #function).wait()
let size = try connection.lpush([100], into: #function).wait()
let size = try connection.lpush(100, into: #function).wait()
let element = try connection.lindex(0, from: #function).wait()
XCTAssertEqual(size, 4)
XCTAssertEqual(Int(fromRESP: element), 100)
......@@ -199,7 +199,7 @@ final class ListCommandsTests: RediStackIntegrationTestCase {
let pop1 = try connection.brpop(from: "first").wait() ?? .null
XCTAssertEqual(Int(fromRESP: pop1), 10)
let pop2 = try connection.brpop(from: ["fake", "first"]).wait()
let pop2 = try connection.brpop(from: "fake", "first").wait()
XCTAssertEqual(pop2?.0, "first")
let blockingConnection = try self.makeNewConnection()
......@@ -217,7 +217,7 @@ final class ListCommandsTests: RediStackIntegrationTestCase {
func test_rpush() throws {
_ = try connection.lpush([10, 20, 30], into: #function).wait()
let size = try connection.rpush([100], into: #function).wait()
let size = try connection.rpush(100, into: #function).wait()
let element = try connection.lindex(3, from: #function).wait()
XCTAssertEqual(size, 4)
XCTAssertEqual(Int(fromRESP: element), 100)
......
......@@ -18,7 +18,7 @@ import XCTest
final class SetCommandsTests: RediStackIntegrationTestCase {
func test_sadd() throws {
var insertCount = try connection.sadd([1, 2, 3], to: #function).wait()
var insertCount = try connection.sadd(1, 2, 3, to: #function).wait()
XCTAssertEqual(insertCount, 3)
insertCount = try connection.sadd([3, 4, 5], to: #function).wait()
XCTAssertEqual(insertCount, 2)
......@@ -57,7 +57,7 @@ final class SetCommandsTests: RediStackIntegrationTestCase {
}
func test_srem() throws {
var removedCount = try connection.srem([1], from: #function).wait()
var removedCount = try connection.srem(1, from: #function).wait()
XCTAssertEqual(removedCount, 0)
_ = try connection.sadd([1], to: #function).wait()
......@@ -96,10 +96,10 @@ final class SetCommandsTests: RediStackIntegrationTestCase {
_ = try connection.sadd([3, 4, 5], to: key2).wait()
_ = try connection.sadd([2, 4], to: key3).wait()
let diff1 = try connection.sdiff(of: [key1, key2]).wait()
let diff1 = try connection.sdiff(of: key1, key2).wait()
XCTAssertEqual(diff1.count, 2)
let diff2 = try connection.sdiff(of: [key1, key3]).wait()
let diff2 = try connection.sdiff(of: key1, key3).wait()
XCTAssertEqual(diff2.count, 2)
let diff3 = try connection.sdiff(of: [key1, key2, key3]).wait()
......@@ -133,10 +133,10 @@ final class SetCommandsTests: RediStackIntegrationTestCase {
_ = try connection.sadd([3, 4, 5], to: key2).wait()
_ = try connection.sadd([2, 4], to: key3).wait()
let diff1 = try connection.sinter(of: [key1, key2]).wait()
let diff1 = try connection.sinter(of: key1, key2).wait()
XCTAssertEqual(diff1.count, 1)
let diff2 = try connection.sinter(of: [key1, key3]).wait()
let diff2 = try connection.sinter(of: key1, key3).wait()
XCTAssertEqual(diff2.count, 1)
let diff3 = try connection.sinter(of: [key1, key2, key3]).wait()
......@@ -186,7 +186,7 @@ final class SetCommandsTests: RediStackIntegrationTestCase {
_ = try connection.sadd([3, 4, 5], to: key2).wait()
_ = try connection.sadd([2, 4], to: key3).wait()
let union1 = try connection.sunion(of: [key1, key2]).wait()
let union1 = try connection.sunion(of: key1, key2).wait()
XCTAssertEqual(union1.count, 5)
let union2 = try connection.sunion(of: [key2, key3]).wait()
......
......@@ -43,7 +43,7 @@ final class SortedSetCommandsTests: RediStackIntegrationTestCase {
XCTAssertEqual(count, 1)
count = try connection.zadd([(30, 5)], to: #function).wait()
XCTAssertEqual(count, 0)
count = try connection.zadd([(30, 6), (31, 0), (32, 1)], to: #function, option: .onlyAddNewElements).wait()
count = try connection.zadd((30, 6), (31, 0), (32, 1), to: #function, option: .onlyAddNewElements).wait()
XCTAssertEqual(count, 2)
count = try connection.zadd(
[(32, 2), (33, 3)],
......@@ -342,12 +342,12 @@ final class SortedSetCommandsTests: RediStackIntegrationTestCase {
}
func test_zrem() throws {
var count = try connection.zrem([1], from: key).wait()
var count = try connection.zrem(1, from: key).wait()
XCTAssertEqual(count, 1)
count = try connection.zrem([1], from: key).wait()
XCTAssertEqual(count, 0)
count = try connection.zrem([2, 3, 4, 5], from: key).wait()
count = try connection.zrem(2, 3, 4, 5, from: key).wait()
XCTAssertEqual(count, 4)
count = try connection.zrem([5, 6, 7], from: key).wait()
XCTAssertEqual(count, 2)
......
......@@ -39,7 +39,7 @@ final class StringCommandsTests: RediStackIntegrationTestCase {
XCTAssertEqual(values[1].string, "two")
XCTAssertEqual(values[2].isNull, true)
XCTAssertEqual(try connection.mget(["empty", #function]).wait().count, 2)
XCTAssertEqual(try connection.mget("empty", #function).wait().count, 2)
}
func test_set() throws {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment