Skip to content

Commit 526c67d

Browse files
authored
Implement Delete Operation Support (#24)
* init delete support * delete signature refactor * NF error added
1 parent 0b17329 commit 526c67d

File tree

6 files changed

+93
-0
lines changed

6 files changed

+93
-0
lines changed

Sources/SwiftMemcache/Extensions/UInt8+Characters.swift

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ extension UInt8 {
1919
static var m: UInt8 = .init(ascii: "m")
2020
static var s: UInt8 = .init(ascii: "s")
2121
static var g: UInt8 = .init(ascii: "g")
22+
static var d: UInt8 = .init(ascii: "d")
2223
static var v: UInt8 = .init(ascii: "v")
2324
static var T: UInt8 = .init(ascii: "T")
2425
static var zero: UInt8 = .init(ascii: "0")

Sources/SwiftMemcache/MemcachedConnection.swift

+33
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ public actor MemcachedConnection {
5959
case connectionShutdown
6060
/// Indicates that a nil response was received from the server.
6161
case unexpectedNilResponse
62+
/// Indicates that the key was not found.
63+
case keyNotFound
6264
}
6365

6466
private var state: State
@@ -247,4 +249,35 @@ public actor MemcachedConnection {
247249
throw MemcachedConnectionError.connectionShutdown
248250
}
249251
}
252+
253+
// MARK: - Deleting a Value
254+
255+
/// Delete the value for a key from the Memcache server.
256+
///
257+
/// - Parameter key: The key of the item to be deleted.
258+
/// - Throws: A `MemcachedConnectionError.connectionShutdown` error if the connection to the Memcache server is shut down.
259+
/// - Throws: A `MemcachedConnectionError.unexpectedNilResponse` error if the key was not found or if an unexpected response code was returned.
260+
public func delete(_ key: String) async throws {
261+
switch self.state {
262+
case .initial(_, _, _, _),
263+
.running:
264+
265+
let command = MemcachedRequest.DeleteCommand(key: key)
266+
let request = MemcachedRequest.delete(command)
267+
268+
let response = try await sendRequest(request)
269+
270+
switch response.returnCode {
271+
case .HD:
272+
return
273+
case .NF:
274+
throw MemcachedConnectionError.keyNotFound
275+
default:
276+
throw MemcachedConnectionError.unexpectedNilResponse
277+
}
278+
279+
case .finished:
280+
throw MemcachedConnectionError.connectionShutdown
281+
}
282+
}
250283
}

Sources/SwiftMemcache/MemcachedRequest.swift

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ enum MemcachedRequest {
2525
var flags: MemcachedFlags
2626
}
2727

28+
struct DeleteCommand {
29+
let key: String
30+
}
31+
2832
case set(SetCommand)
2933
case get(GetCommand)
34+
case delete(DeleteCommand)
3035
}

Sources/SwiftMemcache/MemcachedRequestEncoder.swift

+13
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,19 @@ struct MemcachedRequestEncoder: MessageToByteEncoder {
6060
// write flags if there are any
6161
out.writeMemcachedFlags(flags: command.flags)
6262

63+
// write separator
64+
out.writeInteger(UInt8.carriageReturn)
65+
out.writeInteger(UInt8.newline)
66+
67+
case .delete(let command):
68+
precondition(!command.key.isEmpty, "Key must not be empty")
69+
70+
// write command and key
71+
out.writeInteger(UInt8.m)
72+
out.writeInteger(UInt8.d)
73+
out.writeInteger(UInt8.whitespace)
74+
out.writeBytes(command.key.utf8)
75+
6376
// write separator
6477
out.writeInteger(UInt8.carriageReturn)
6578
out.writeInteger(UInt8.newline)

Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift

+24
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,30 @@ final class MemcachedIntegrationTest: XCTestCase {
230230
}
231231
}
232232

233+
func testDeleteValue() async throws {
234+
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
235+
defer {
236+
XCTAssertNoThrow(try! group.syncShutdownGracefully())
237+
}
238+
let memcachedConnection = MemcachedConnection(host: "memcached", port: 11211, eventLoopGroup: group)
239+
240+
try await withThrowingTaskGroup(of: Void.self) { group in
241+
group.addTask { try await memcachedConnection.run() }
242+
243+
// Set key and value
244+
let setValue = "foo"
245+
try await memcachedConnection.set("bar", value: setValue)
246+
247+
// Delete the key
248+
do {
249+
try await memcachedConnection.delete("bar")
250+
} catch {
251+
XCTFail("Deletion attempt should be successful, but threw: \(error)")
252+
}
253+
group.cancelAll()
254+
}
255+
}
256+
233257
func testMemcachedConnectionWithUInt() async throws {
234258
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
235259
defer {

Tests/SwiftMemcacheTests/UnitTest/MemcachedRequestEncoderTests.swift

+17
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,21 @@ final class MemcachedRequestEncoderTests: XCTestCase {
139139
let expectedEncodedData = "mg foo v\r\n"
140140
XCTAssertEqual(outBuffer.getString(at: 0, length: outBuffer.readableBytes), expectedEncodedData)
141141
}
142+
143+
func testEncodeDeleteRequest() {
144+
// Prepare a MemcachedRequest
145+
let command = MemcachedRequest.DeleteCommand(key: "foo")
146+
let request = MemcachedRequest.delete(command)
147+
148+
// Pass our request through the encoder
149+
var outBuffer = ByteBufferAllocator().buffer(capacity: 0)
150+
do {
151+
try self.encoder.encode(data: request, out: &outBuffer)
152+
} catch {
153+
XCTFail("Encoding failed with error: \(error)")
154+
}
155+
156+
let expectedEncodedData = "md foo\r\n"
157+
XCTAssertEqual(outBuffer.getString(at: 0, length: outBuffer.readableBytes), expectedEncodedData)
158+
}
142159
}

0 commit comments

Comments
 (0)