@@ -93,6 +93,106 @@ extension RedisClient {
93
93
94
94
// MARK: Set
95
95
96
+ /// A condition which must hold true in order for a key to be set.
97
+ ///
98
+ /// See [https://redis.io/commands/set](https://redis.io/commands/set)
99
+ public struct RedisSetCommandCondition : Hashable {
100
+ private enum Condition : String , Hashable {
101
+ case keyExists = " XX "
102
+ case keyDoesNotExist = " NX "
103
+ }
104
+
105
+ private let condition : Condition ?
106
+ private init ( _ condition: Condition ? ) {
107
+ self . condition = condition
108
+ }
109
+
110
+ /// The `RESPValue` representation of the condition.
111
+ @usableFromInline
112
+ internal var commandArgument : RESPValue ? {
113
+ return self . condition. map { RESPValue ( from: $0. rawValue) }
114
+ }
115
+ }
116
+
117
+ extension RedisSetCommandCondition {
118
+ /// No condition is required to be met in order to set the key's value.
119
+ public static let none = RedisSetCommandCondition ( . none)
120
+
121
+ /// Only set the key if it already exists.
122
+ ///
123
+ /// Redis documentation refers to this as the option "XX".
124
+ public static let keyExists = RedisSetCommandCondition ( . keyExists)
125
+
126
+ /// Only set the key if it does not already exist.
127
+ ///
128
+ /// Redis documentation refers to this as the option "NX".
129
+ public static let keyDoesNotExist = RedisSetCommandCondition ( . keyDoesNotExist)
130
+ }
131
+
132
+ /// The expiration to apply when setting a key.
133
+ ///
134
+ /// See [https://redis.io/commands/set](https://redis.io/commands/set)
135
+ public struct RedisSetCommandExpiration : Hashable {
136
+ private enum Expiration : Hashable {
137
+ case keepExisting
138
+ case seconds( Int )
139
+ case milliseconds( Int )
140
+ }
141
+
142
+ private let expiration : Expiration
143
+ private init ( _ expiration: Expiration ) {
144
+ self . expiration = expiration
145
+ }
146
+
147
+ /// An array of `RESPValue`s representing this expiration.
148
+ @usableFromInline
149
+ internal func asCommandArguments( ) -> [ RESPValue ] {
150
+ switch self . expiration {
151
+ case . keepExisting:
152
+ return [ RESPValue ( from: " KEEPTTL " ) ]
153
+ case . seconds( let amount) :
154
+ return [ RESPValue ( from: " EX " ) , amount. convertedToRESPValue ( ) ]
155
+ case . milliseconds( let amount) :
156
+ return [ RESPValue ( from: " PX " ) , amount. convertedToRESPValue ( ) ]
157
+ }
158
+ }
159
+ }
160
+
161
+ extension RedisSetCommandExpiration {
162
+ /// Retain the existing expiration associated with the key, if one exists.
163
+ ///
164
+ /// Redis documentation refers to this as "KEEPTTL".
165
+ /// - Important: This is option is only available in Redis 6.0+. An error will be returned if this value is sent in lower versions of Redis.
166
+ public static let keepExisting = RedisSetCommandExpiration ( . keepExisting)
167
+
168
+ /// Expire the key after the given number of seconds.
169
+ ///
170
+ /// Redis documentation refers to this as the option "EX".
171
+ /// - Important: The actual amount used will be the specified value or `1`, whichever is larger.
172
+ public static func seconds( _ amount: Int ) -> RedisSetCommandExpiration {
173
+ return RedisSetCommandExpiration ( . seconds( max ( amount, 1 ) ) )
174
+ }
175
+
176
+ /// Expire the key after the given number of milliseconds.
177
+ ///
178
+ /// Redis documentation refers to this as the option "PX".
179
+ /// - Important: The actual amount used will be the specified value or `1`, whichever is larger.
180
+ public static func milliseconds( _ amount: Int ) -> RedisSetCommandExpiration {
181
+ return RedisSetCommandExpiration ( . milliseconds( max ( amount, 1 ) ) )
182
+ }
183
+ }
184
+
185
+ /// The result of a `SET` command.
186
+ public enum RedisSetCommandResult : Hashable {
187
+ /// The command completed successfully.
188
+ case ok
189
+
190
+ /// The command was not performed because a condition was not met.
191
+ ///
192
+ /// See `RedisSetCommandCondition`.
193
+ case conditionNotMet
194
+ }
195
+
96
196
extension RedisClient {
97
197
/// Append a value to the end of an existing entry.
98
198
/// - 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.
@@ -133,6 +233,45 @@ extension RedisClient {
133
233
. map { _ in ( ) }
134
234
}
135
235
236
+ /// Sets the key to the provided value with options to control how it is set.
237
+ ///
238
+ /// [https://redis.io/commands/set](https://redis.io/commands/set)
239
+ /// - Important: Regardless of the type of data stored at the key, it will be overwritten to a "string" data type.
240
+ ///
241
+ /// ie. If the key is a reference to a Sorted Set, its value will be overwritten to be a "string" data type.
242
+ ///
243
+ /// - Parameters:
244
+ /// - key: The key to use to uniquely identify this value.
245
+ /// - value: The value to set the key to.
246
+ /// - condition: The condition under which the key should be set.
247
+ /// - expiration: The expiration to use when setting the key. No expiration is set if `nil`.
248
+ /// - Returns: A `NIO.EventLoopFuture` indicating the result of the operation;
249
+ /// `.ok` if the operation was successful and `.conditionNotMet` if the specified `condition` was not met.
250
+ ///
251
+ /// If the condition `.none` was used, then the result value will always be `.ok`.
252
+ public func set< Value: RESPValueConvertible > (
253
+ _ key: RedisKey ,
254
+ to value: Value ,
255
+ onCondition condition: RedisSetCommandCondition ,
256
+ expiration: RedisSetCommandExpiration ? = nil
257
+ ) -> EventLoopFuture < RedisSetCommandResult > {
258
+ var args : [ RESPValue ] = [
259
+ . init( from: key) ,
260
+ value. convertedToRESPValue ( )
261
+ ]
262
+
263
+ if let conditionArgument = condition. commandArgument {
264
+ args. append ( conditionArgument)
265
+ }
266
+
267
+ if let expiration = expiration {
268
+ args. append ( contentsOf: expiration. asCommandArguments ( ) )
269
+ }
270
+
271
+ return self . send ( command: " SET " , with: args)
272
+ . map { return $0. isNull ? . conditionNotMet : . ok }
273
+ }
274
+
136
275
/// Sets the key to the provided value if the key does not exist.
137
276
///
138
277
/// [https://redis.io/commands/setnx](https://redis.io/commands/setnx)
0 commit comments