Skip to content

Commit 4b06ece

Browse files
committed
Add SETNX command
Motivation: The SETNX command is missing. Modifications: - Add SETNX command - Add integration test Result: Users can set a key only if it does not already exist
1 parent 06471a2 commit 4b06ece

File tree

3 files changed

+37
-10
lines changed

3 files changed

+37
-10
lines changed

Sources/RediStack/Commands/StringCommands.swift

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ extension RedisClient {
2626
let args = [RESPValue(from: key)]
2727
return self.send(command: "GET", with: args)
2828
}
29-
29+
3030
/// Get the value of a key, converting it to the desired type.
3131
///
3232
/// [https://redis.io/commands/get](https://redis.io/commands/get)
@@ -55,7 +55,7 @@ extension RedisClient {
5555
return send(command: "MGET", with: args)
5656
.map()
5757
}
58-
58+
5959
/// Gets the values of all specified keys, using `.null` to represent non-existant values.
6060
///
6161
/// See [https://redis.io/commands/mget](https://redis.io/commands/mget)
@@ -68,7 +68,7 @@ extension RedisClient {
6868
return self.mget(keys)
6969
.map { return $0.map(Value.init(fromRESP:)) }
7070
}
71-
71+
7272
/// Gets the values of all specified keys, using `.null` to represent non-existant values.
7373
///
7474
/// See [https://redis.io/commands/mget](https://redis.io/commands/mget)
@@ -77,7 +77,7 @@ extension RedisClient {
7777
public func mget(_ keys: RedisKey...) -> EventLoopFuture<[RESPValue]> {
7878
return self.mget(keys)
7979
}
80-
80+
8181
/// Gets the values of all specified keys, using `.null` to represent non-existant values.
8282
///
8383
/// See [https://redis.io/commands/mget](https://redis.io/commands/mget)
@@ -111,7 +111,7 @@ extension RedisClient {
111111
return send(command: "APPEND", with: args)
112112
.map()
113113
}
114-
114+
115115
/// Sets the value stored in the key provided, overwriting the previous value.
116116
///
117117
/// Any previous expiration set on the key is discarded if the SET operation was successful.
@@ -133,6 +133,27 @@ extension RedisClient {
133133
.map { _ in () }
134134
}
135135

136+
/// Sets the key to the provided value if the key does not exist.
137+
///
138+
/// [https://redis.io/commands/setnx](https://redis.io/commands/setnx)
139+
/// - Important: Regardless of the type of data stored at the key, it will be overwritten to a "string" data type.
140+
///
141+
/// ie. If the key is a reference to a Sorted Set, its value will be overwritten to be a "string" data type.
142+
/// - Parameters:
143+
/// - key: The key to use to uniquely identify this value.
144+
/// - value: The value to set the key to.
145+
/// - Returns: `true` if the operation successfully completed.
146+
@inlinable
147+
public func setnx<Value: RESPValueConvertible>(_ key: RedisKey, to value: Value) -> EventLoopFuture<Bool> {
148+
let args: [RESPValue] = [
149+
.init(from: key),
150+
value.convertedToRESPValue()
151+
]
152+
return self.send(command: "SETNX", with: args)
153+
.map(to: Int.self)
154+
.map { $0 == 1 }
155+
}
156+
136157
/// Sets each key to their respective new value, overwriting existing values.
137158
/// - Note: Use `msetnx(_:)` if you don't want to overwrite values.
138159
///
@@ -157,7 +178,7 @@ extension RedisClient {
157178
.map(to: Int.self)
158179
.map { return $0 == 1 }
159180
}
160-
181+
161182
@usableFromInline
162183
func _mset<Value: RESPValueConvertible>(
163184
command: String,

Tests/RediStackIntegrationTests/Commands/StringCommandsTests.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ final class StringCommandsTests: RediStackIntegrationTestCase {
2323
try connection.set(#function, to: "value").wait()
2424
let r1: String? = try connection.get(#function, as: String.self).wait()
2525
XCTAssertEqual(r1, "value")
26-
26+
2727
try connection.set(#function, to: 30).wait()
2828
let r2 = try connection.get(#function, as: Int.self).wait()
2929
XCTAssertEqual(r2, 30)
30-
30+
3131
_ = try connection.delete(#function).wait()
3232
let r3: RESPValue = try connection.get(#function).wait()
3333
XCTAssertEqual(r3, .null)
@@ -51,7 +51,12 @@ final class StringCommandsTests: RediStackIntegrationTestCase {
5151
let val = try connection.get(#function, as: String.self).wait()
5252
XCTAssertEqual(val, "value")
5353
}
54-
54+
55+
func test_setnx() throws {
56+
XCTAssertTrue(try connection.setnx(#function, to: "value").wait())
57+
XCTAssertFalse(try connection.setnx(#function, to: "value").wait())
58+
}
59+
5560
func test_append() throws {
5661
let result = "value appended"
5762
XCTAssertNoThrow(try connection.append("value", to: #function).wait())
@@ -60,7 +65,7 @@ final class StringCommandsTests: RediStackIntegrationTestCase {
6065
let val = try connection.get(#function, as: String.self).wait()
6166
XCTAssertEqual(val, result)
6267
}
63-
68+
6469
func test_mset() throws {
6570
let data: [RedisKey: Int] = [
6671
"first": 1,

Tests/RediStackIntegrationTests/XCTestManifests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ extension StringCommandsTests {
148148
("test_mset", test_mset),
149149
("test_msetnx", test_msetnx),
150150
("test_set", test_set),
151+
("test_setnx", test_setnx),
151152
]
152153
}
153154

0 commit comments

Comments
 (0)