Skip to content

Commit a431ae8

Browse files
committed
[List] Properly map results when key has no values
Fixes #116
1 parent e0cab21 commit a431ae8

File tree

2 files changed

+76
-2
lines changed

2 files changed

+76
-2
lines changed

Sources/RediStack/Commands/ListCommands.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ extension RedisCommand {
2525
/// - Parameters:
2626
/// - key: The key of the list to pop from.
2727
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
28+
/// - Returns: The value popped from the list, otherwise `nil`.
2829
public static func blpop(from key: RedisKey, timeout: TimeAmount = .seconds(0)) -> RedisCommand<RESPValue?> {
2930
return ._bpop(keyword: "BLPOP", [key], timeout, { $0?.1 })
3031
}
@@ -37,6 +38,7 @@ extension RedisCommand {
3738
/// - Parameters:
3839
/// - keys: The list of keys to pop from.
3940
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
41+
/// - Returns: The popped value and the key of its source list, otherwise `nil`.
4042
public static func blpop(
4143
from keys: [RedisKey],
4244
timeout: TimeAmount = .seconds(0)
@@ -52,6 +54,7 @@ extension RedisCommand {
5254
/// - Parameters:
5355
/// - keys: The list of keys to pop from.
5456
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
57+
/// - Returns: The popped value and the key of its source list, otherwise `nil`.
5558
public static func blpop(
5659
from keys: RedisKey...,
5760
timeout: TimeAmount = .seconds(0)
@@ -65,6 +68,7 @@ extension RedisCommand {
6568
/// - Parameters:
6669
/// - key: The key of the list to pop from.
6770
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
71+
/// - Returns: The value popped from the list, otherwise `nil`.
6872
public static func brpop(from key: RedisKey, timeout: TimeAmount = .seconds(0)) -> RedisCommand<RESPValue?> {
6973
return ._bpop(keyword: "BRPOP", [key], timeout, { $0?.1 })
7074
}
@@ -77,6 +81,7 @@ extension RedisCommand {
7781
/// - Parameters:
7882
/// - key: The key of the list to pop from.
7983
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
84+
/// - Returns: The popped value and the key of its source list, otherwise `nil`.
8085
public static func brpop(from keys: [RedisKey], timeout: TimeAmount = .seconds(0)) -> RedisCommand<(RedisKey, RESPValue)?> {
8186
return ._bpop(keyword: "BRPOP", keys, timeout, { $0 })
8287
}
@@ -89,6 +94,7 @@ extension RedisCommand {
8994
/// - Parameters:
9095
/// - key: The key of the list to pop from.
9196
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
97+
/// - Returns: The popped value and the key of its source list, otherwise `nil`.
9298
public static func brpop(
9399
from keys: RedisKey...,
94100
timeout: TimeAmount = .seconds(0)
@@ -103,23 +109,26 @@ extension RedisCommand {
103109
/// - source: The key of the list to pop from.
104110
/// - dest: The key of the list to push to.
105111
/// - timeout: The max time to wait for a value to use. `0` seconds means to wait indefinitely.
112+
/// - Returns: The value removed from the `source`, otherwise `nil`.
106113
public static func brpoplpush(
107114
from source: RedisKey,
108115
to dest: RedisKey,
109116
timeout: TimeAmount = .seconds(0)
110117
) -> RedisCommand<RESPValue?> {
118+
assert(timeout >= .seconds(0), "anything smaller than a second will be treated as 0 seconds")
111119
let args: [RESPValue] = [
112120
.init(from: source),
113121
.init(from: dest),
114122
.init(from: timeout.seconds)
115123
]
116-
return .init(keyword: "BRPOPLPUSH", arguments: args)
124+
return .init(keyword: "BRPOPLPUSH", arguments: args, mapValueToResult: { try? $0.map() })
117125
}
118126

119127
/// [LINDEX](https://redis.io/commands/lindex)
120128
/// - Parameters:
121129
/// - index: The 0-based index of the element to get.
122130
/// - key: The key of the list.
131+
/// - Returns: The value stored at the index, otherwise `nil`.
123132
public static func lindex(_ index: Int, from key: RedisKey) -> RedisCommand<RESPValue?> {
124133
let args: [RESPValue] = [
125134
.init(from: key),
@@ -161,6 +170,7 @@ extension RedisCommand {
161170

162171
/// [LPOP](https://redis.io/commands/lpop)
163172
/// - Parameter key: The key of the list to pop from.
173+
/// - Returns: The value popped from the list, otherwise `nil`.
164174
public static func lpop(from key: RedisKey) -> RedisCommand<RESPValue?> {
165175
let args = [RESPValue(from: key)]
166176
return .init(keyword: "LPOP", arguments: args) { try? $0.map() }
@@ -472,6 +482,7 @@ extension RedisCommand {
472482

473483
/// [RPOP](https://redis.io/commands/rpop)
474484
/// - Parameter key: The key of the list to pop from.
485+
/// - Returns: The value popped from the list, otherwise `nil`.
475486
public static func rpop(from key: RedisKey) -> RedisCommand<RESPValue?> {
476487
let args = [RESPValue(from: key)]
477488
return .init(keyword: "RPOP", arguments: args) { try? $0.map() }
@@ -481,12 +492,13 @@ extension RedisCommand {
481492
/// - Parameters:
482493
/// - source: The key of the list to pop from.
483494
/// - dest: The key of the list to push to.
495+
/// - Returns: The value removed from the `source`, otherwise `nil`.
484496
public static func rpoplpush(from source: RedisKey, to dest: RedisKey) -> RedisCommand<RESPValue?> {
485497
let args: [RESPValue] = [
486498
.init(from: source),
487499
.init(from: dest)
488500
]
489-
return .init(keyword: "RPOPLPUSH", arguments: args)
501+
return .init(keyword: "RPOPLPUSH", arguments: args, mapValueToResult: { try? $0.map() })
490502
}
491503

492504
/// [RPUSH](https://redis.io/commands/rpush)
@@ -537,6 +549,8 @@ extension RedisCommand {
537549
_ timeout: TimeAmount,
538550
_ transform: @escaping ((RedisKey, RESPValue)?) throws -> ResultType?
539551
) -> RedisCommand<ResultType?> {
552+
assert(timeout >= .seconds(0), "anything smaller than a second will be treated as 0 seconds")
553+
540554
var args = keys.map(RESPValue.init(from:))
541555
args.append(.init(bulk: timeout.seconds))
542556

Tests/RediStackIntegrationTests/Commands/ListCommandsTests.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,63 @@ final class ListCommandsTests: RediStackIntegrationTestCase {
274274
XCTAssertEqual(elements.count, 3)
275275
}
276276
}
277+
278+
// MARK: #116 tests
279+
280+
extension ListCommandsTests {
281+
func test_rpoplpush_whenEmpty_succeeds_withNil() throws {
282+
let result = try self
283+
.connection
284+
.send(.rpoplpush(from: "list1", to: "\(#function)"))
285+
.wait()
286+
XCTAssertNil(result)
287+
}
288+
289+
func test_rpop_whenEmpty_succeeds_withNil() throws {
290+
let result = try self
291+
.connection
292+
.send(.rpop(from: "\(#function)"))
293+
.wait()
294+
XCTAssertNil(result)
295+
}
296+
297+
func test_lpop_whenEmpty_succeeds_withNil() throws {
298+
let result = try self
299+
.connection
300+
.send(.lpop(from: "\(#function)"))
301+
.wait()
302+
XCTAssertNil(result)
303+
}
304+
305+
func test_lindex_whenEmpty_succeeds_withNil() throws {
306+
let result = try self
307+
.connection
308+
.send(.lindex(3, from: "\(#function)"))
309+
.wait()
310+
XCTAssertNil(result)
311+
}
312+
313+
func test_lrange_whenEmpty_succeeds_withEmpty() throws {
314+
let result = try self
315+
.connection
316+
.send(.lrange(from: "\(#function)", firstIndex: 0, lastIndex: 3))
317+
.wait()
318+
XCTAssertTrue(result.isEmpty)
319+
}
320+
321+
func test_brpoplpush_whenEmpty_succeeds_withNil() throws {
322+
let result = try self
323+
.connection
324+
.send(.brpoplpush(from: "\(#function)", to: "list1", timeout: .seconds(1)))
325+
.wait()
326+
XCTAssertNil(result)
327+
}
328+
329+
func test_brpop_whenEmpty_succeeds_withNil() throws {
330+
let result = try self
331+
.connection
332+
.send(.brpop(from: "\(#function)", timeout: .seconds(1)))
333+
.wait()
334+
XCTAssertNil(result)
335+
}
336+
}

0 commit comments

Comments
 (0)