From 785b46d514524fcf31175f0e52752ea80fa64e8e Mon Sep 17 00:00:00 2001 From: Natan Rolnik Date: Fri, 10 Oct 2025 15:30:56 +0300 Subject: [PATCH 1/2] Add response types for KEYS and SCAN commands Signed-off-by: Natan Rolnik --- .../Custom/GenericCustomCommands.swift | 25 +++++++++++++++++++ Sources/Valkey/Commands/GenericCommands.swift | 8 ++---- .../Documentation.docc/getting-started.md | 6 ++--- .../ValkeyCommandsRender.swift | 6 ++++- 4 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 Sources/Valkey/Commands/Custom/GenericCustomCommands.swift diff --git a/Sources/Valkey/Commands/Custom/GenericCustomCommands.swift b/Sources/Valkey/Commands/Custom/GenericCustomCommands.swift new file mode 100644 index 00000000..9dbd674f --- /dev/null +++ b/Sources/Valkey/Commands/Custom/GenericCustomCommands.swift @@ -0,0 +1,25 @@ +// +// This source file is part of the valkey-swift project +// Copyright (c) 2025 the valkey-swift project authors +// +// See LICENSE.txt for license information +// SPDX-License-Identifier: Apache-2.0 +// +import NIOCore + +extension SCAN { + public struct Response: RESPTokenDecodable, Sendable { + public let cursor: Int + public let keys: [ValkeyKey] + + public init(fromRESP token: RESPToken) throws { + let (cursor, keys) = try token.decodeArrayElements(as: (Int, [ValkeyKey]).self) + self.cursor = cursor + self.keys = keys + } + } +} + +extension KEYS { + public typealias Response = [ValkeyKey] +} diff --git a/Sources/Valkey/Commands/GenericCommands.swift b/Sources/Valkey/Commands/GenericCommands.swift index 0ac9fd2b..4ca12b35 100644 --- a/Sources/Valkey/Commands/GenericCommands.swift +++ b/Sources/Valkey/Commands/GenericCommands.swift @@ -324,8 +324,6 @@ public struct EXPIRETIME: ValkeyCommand { /// Returns all key names that match a pattern. @_documentation(visibility: internal) public struct KEYS: ValkeyCommand { - public typealias Response = RESPToken.Array - @inlinable public static var name: String { "KEYS" } public var pattern: String @@ -740,8 +738,6 @@ public struct RESTORE: ValkeyCommand { /// Iterates over the key names in the database. @_documentation(visibility: internal) public struct SCAN: ValkeyCommand { - public typealias Response = RESPToken.Array - @inlinable public static var name: String { "SCAN" } public var cursor: Int @@ -1168,7 +1164,7 @@ extension ValkeyClientProtocol { /// - Complexity: O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length. /// - Response: [Array]: List of keys matching pattern. @inlinable - public func keys(pattern: String) async throws -> RESPToken.Array { + public func keys(pattern: String) async throws -> KEYS.Response { try await execute(KEYS(pattern: pattern)) } @@ -1433,7 +1429,7 @@ extension ValkeyClientProtocol { /// - Complexity: O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection. /// - Response: [Array]: Cursor and scan response in array form. @inlinable - public func scan(cursor: Int, pattern: String? = nil, count: Int? = nil, type: String? = nil) async throws -> RESPToken.Array { + public func scan(cursor: Int, pattern: String? = nil, count: Int? = nil, type: String? = nil) async throws -> SCAN.Response { try await execute(SCAN(cursor: cursor, pattern: pattern, count: count, type: type)) } diff --git a/Sources/Valkey/Documentation.docc/getting-started.md b/Sources/Valkey/Documentation.docc/getting-started.md index 0c5f740f..cf57e6f6 100644 --- a/Sources/Valkey/Documentation.docc/getting-started.md +++ b/Sources/Valkey/Documentation.docc/getting-started.md @@ -12,14 +12,14 @@ You can use the `add-dependency` command: ```bash swift package add-dependency \ - https://github.com/valkey-io/valkey-swift --from: 0.1.0 + https://github.com/valkey-io/valkey-swift --from: 0.3.0 ``` or edit Package.swift directly: ```swift dependencies: [ .package(url: "https://github.com/valkey-io/valkey-swift", - from: "0.1.0"), + from: "0.3.0"), ] ``` @@ -58,7 +58,7 @@ You can either run them using a Task group, for example: ```swift let valkeyClient = ValkeyClient(.hostname("localhost", port: 6379), logger: logger) -try await withThrowingTaskgroup(of: Void.self) { group in +try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { // run connection pool in the background await valkeyClient.run() diff --git a/Sources/_ValkeyCommandsBuilder/ValkeyCommandsRender.swift b/Sources/_ValkeyCommandsBuilder/ValkeyCommandsRender.swift index 1ff1665f..10a3c5f6 100644 --- a/Sources/_ValkeyCommandsBuilder/ValkeyCommandsRender.swift +++ b/Sources/_ValkeyCommandsBuilder/ValkeyCommandsRender.swift @@ -21,9 +21,11 @@ private let disableResponseCalculationCommands: Set = [ "GEODIST", "GEOPOS", "GEOSEARCH", - "ROLE", + "KEYS", "LMOVE", "LMPOP", + "ROLE", + "SCAN", "SSCAN", "XAUTOCLAIM", "XCLAIM", @@ -36,6 +38,7 @@ private let disableResponseCalculationCommands: Set = [ "ZPOPMAX", "ZPOPMIN", ] + /// List of subscribe commands, which have their own implementation in code let subscribeFunctions: Set = [ "SUBSCRIBE", @@ -45,6 +48,7 @@ let subscribeFunctions: Set = [ "PUNSUBSCRIBE", "SUNSUBSCRIBE", ] + /// List of commands that should only be available from ValkeyConnection let connectionOnlyFunctionNames: Set = [ "MULTI", From 069b4f8471a354d3d6ba22d03f062db045da60e5 Mon Sep 17 00:00:00 2001 From: Natan Rolnik Date: Sun, 12 Oct 2025 01:12:34 +0300 Subject: [PATCH 2/2] Address PR comments Signed-off-by: Natan Rolnik --- .../Valkey/Commands/Custom/GenericCustomCommands.swift | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Sources/Valkey/Commands/Custom/GenericCustomCommands.swift b/Sources/Valkey/Commands/Custom/GenericCustomCommands.swift index 9dbd674f..9a5e27e8 100644 --- a/Sources/Valkey/Commands/Custom/GenericCustomCommands.swift +++ b/Sources/Valkey/Commands/Custom/GenericCustomCommands.swift @@ -10,16 +10,10 @@ import NIOCore extension SCAN { public struct Response: RESPTokenDecodable, Sendable { public let cursor: Int - public let keys: [ValkeyKey] + public let keys: RESPToken.Array public init(fromRESP token: RESPToken) throws { - let (cursor, keys) = try token.decodeArrayElements(as: (Int, [ValkeyKey]).self) - self.cursor = cursor - self.keys = keys + (self.cursor, self.keys) = try token.decodeArrayElements(as: (Int, RESPToken.Array).self) } } } - -extension KEYS { - public typealias Response = [ValkeyKey] -}