Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions Sources/Valkey/Commands/Custom/HashCustomCommands.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// 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

/// Sorted set entry
@_documentation(visibility: internal)
public struct HashEntry: RESPTokenDecodable, Sendable {
public let field: ByteBuffer
public let value: ByteBuffer

init(field: ByteBuffer, value: ByteBuffer) {
self.field = field
self.value = value
}

public init(fromRESP token: RESPToken) throws {
switch token.value {
case .array(let array):
(self.field, self.value) = try array.decodeElements()
default:
throw RESPDecodeError.tokenMismatch(expected: [.array], token: token)
}
}
}

extension HSCAN {
public struct Response: RESPTokenDecodable, Sendable {
public struct Members: RESPTokenDecodable, Sendable {
/// List of members and possibly scores.
public let elements: RESPToken.Array

public init(fromRESP token: RESPToken) throws {
self.elements = try token.decode(as: RESPToken.Array.self)
}

/// if HSCAN was called with the `NOVALUES` parameter use this
/// function to get an array of fields
public func withoutValues() throws -> [ByteBuffer] {
try self.elements.decode(as: [ByteBuffer].self)
}

/// if HSCAN was called without the `NOVALUES` parameter use this
/// function to get an array of fields and values
public func withValues() throws -> [HashEntry] {
var array: [HashEntry] = []
for respElement in try self.elements.asMap() {
let field = try ByteBuffer(fromRESP: respElement.key)
let value = try ByteBuffer(fromRESP: respElement.value)
array.append(.init(field: field, value: value))
}
return array
}
}
/// Cursor to use in next call to HSCAN
public let cursor: Int
/// Sorted set members
public let members: Members

public init(fromRESP token: RESPToken) throws {
(self.cursor, self.members) = try token.decodeArrayElements()
}
}
}
25 changes: 25 additions & 0 deletions Sources/Valkey/Commands/Custom/ListCustomCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@

import NIOCore

/// List entry
@_documentation(visibility: internal)
public struct ListEntry: RESPTokenDecodable, Sendable {
public let key: ValkeyKey
public let value: ByteBuffer

public init(fromRESP token: RESPToken) throws {
(self.key, self.value) = try token.decodeArrayElements()
}
}

extension LMOVE {
public typealias Response = ByteBuffer?
}
Expand Down Expand Up @@ -38,3 +49,17 @@ extension BLMPOP {
/// * [Array]: List key from which elements were popped.
public typealias Response = LMPOP.Response
}

extension BLPOP {
/// - Response: One of the following
/// * [Null]: No element could be popped and timeout expired
/// * [Array]: The key from which the element was popped and the value of the popped element
public typealias Response = ListEntry?
}

extension BRPOP {
/// - Response: One of the following
/// * [Null]: No element could be popped and the timeout expired.
/// * [Array]: The key from which the element was popped and the value of the popped element
public typealias Response = ListEntry?
}
44 changes: 44 additions & 0 deletions Sources/Valkey/Commands/Custom/SortedSetCustomCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public struct SortedSetEntry: RESPTokenDecodable, Sendable {
public let value: ByteBuffer
public let score: Double

init(value: ByteBuffer, score: Double) {
self.value = value
self.score = score
}

public init(fromRESP token: RESPToken) throws {
switch token.value {
case .array(let array):
Expand Down Expand Up @@ -76,3 +81,42 @@ extension ZPOPMIN {
/// * [Array]: List of popped elements and scores when 'COUNT' is specified.
public typealias Response = [SortedSetEntry]
}

extension ZSCAN {
public struct Response: RESPTokenDecodable, Sendable {
public struct Members: RESPTokenDecodable, Sendable {
/// List of members and possibly scores.
public let elements: RESPToken.Array

public init(fromRESP token: RESPToken) throws {
self.elements = try token.decode(as: RESPToken.Array.self)
}

/// if ZSCAN was called with the `NOSCORES` parameter use this
/// function to get an array of members
public func withoutScores() throws -> [ByteBuffer] {
try self.elements.decode(as: [ByteBuffer].self)
}

/// if ZSCAN was called without the `NOSCORES` parameter use this
/// function to get an array of members and scores
public func withScores() throws -> [SortedSetEntry] {
var array: [SortedSetEntry] = []
for respElement in try self.elements.asMap() {
let value = try ByteBuffer(fromRESP: respElement.key)
let score = try Double(fromRESP: respElement.value)
array.append(.init(value: value, score: score))
}
return array
}
}
/// Cursor to use in next call to ZSCAN
public let cursor: Int
/// Sorted set members
public let members: Members

public init(fromRESP token: RESPToken) throws {
(self.cursor, self.members) = try token.decodeArrayElements()
}
}
}
4 changes: 3 additions & 1 deletion Sources/Valkey/Commands/GenericCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@ 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
Expand Down Expand Up @@ -1164,7 +1166,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 -> KEYS.Response {
public func keys(pattern: String) async throws -> RESPToken.Array {
try await execute(KEYS(pattern: pattern))
}

Expand Down
11 changes: 2 additions & 9 deletions Sources/Valkey/Commands/HashCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -831,8 +831,6 @@ public struct HRANDFIELD: ValkeyCommand {
/// Iterates over fields and values of a hash.
@_documentation(visibility: internal)
public struct HSCAN: ValkeyCommand {
public typealias Response = RESPToken.Array

@inlinable public static var name: String { "HSCAN" }

public var key: ValkeyKey
Expand Down Expand Up @@ -1410,13 +1408,8 @@ 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 hscan(
_ key: ValkeyKey,
cursor: Int,
pattern: String? = nil,
count: Int? = nil,
novalues: Bool = false
) async throws -> RESPToken.Array {
public func hscan(_ key: ValkeyKey, cursor: Int, pattern: String? = nil, count: Int? = nil, novalues: Bool = false) async throws -> HSCAN.Response
{
try await execute(HSCAN(key, cursor: cursor, pattern: pattern, count: count, novalues: novalues))
}

Expand Down
8 changes: 2 additions & 6 deletions Sources/Valkey/Commands/ListCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@ public struct BLMPOP: ValkeyCommand {
/// Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.
@_documentation(visibility: internal)
public struct BLPOP: ValkeyCommand {
public typealias Response = RESPToken.Array?

@inlinable public static var name: String { "BLPOP" }

public var keys: [ValkeyKey]
Expand All @@ -143,8 +141,6 @@ public struct BLPOP: ValkeyCommand {
/// Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.
@_documentation(visibility: internal)
public struct BRPOP: ValkeyCommand {
public typealias Response = RESPToken.Array?

@inlinable public static var name: String { "BRPOP" }

public var keys: [ValkeyKey]
Expand Down Expand Up @@ -701,7 +697,7 @@ extension ValkeyClientProtocol {
/// * [Array]: The key from which the element was popped and the value of the popped element
@inlinable
@discardableResult
public func blpop(keys: [ValkeyKey], timeout: Double) async throws -> RESPToken.Array? {
public func blpop(keys: [ValkeyKey], timeout: Double) async throws -> BLPOP.Response {
try await execute(BLPOP(keys: keys, timeout: timeout))
}

Expand All @@ -717,7 +713,7 @@ extension ValkeyClientProtocol {
/// * [Array]: The name of the key where an element was popped
@inlinable
@discardableResult
public func brpop(keys: [ValkeyKey], timeout: Double) async throws -> RESPToken.Array? {
public func brpop(keys: [ValkeyKey], timeout: Double) async throws -> BRPOP.Response {
try await execute(BRPOP(keys: keys, timeout: timeout))
}

Expand Down
11 changes: 2 additions & 9 deletions Sources/Valkey/Commands/SortedSetCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1143,8 +1143,6 @@ public struct ZREVRANK<Member: RESPStringRenderable>: ValkeyCommand {
/// Iterates over members and scores of a sorted set.
@_documentation(visibility: internal)
public struct ZSCAN: ValkeyCommand {
public typealias Response = RESPToken.Array

@inlinable public static var name: String { "ZSCAN" }

public var key: ValkeyKey
Expand Down Expand Up @@ -1788,13 +1786,8 @@ 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 zscan(
_ key: ValkeyKey,
cursor: Int,
pattern: String? = nil,
count: Int? = nil,
noscores: Bool = false
) async throws -> RESPToken.Array {
public func zscan(_ key: ValkeyKey, cursor: Int, pattern: String? = nil, count: Int? = nil, noscores: Bool = false) async throws -> ZSCAN.Response
{
try await execute(ZSCAN(key, cursor: cursor, pattern: pattern, count: count, noscores: noscores))
}

Expand Down
6 changes: 5 additions & 1 deletion Sources/_ValkeyCommandsBuilder/ValkeyCommandsRender.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import Foundation
/// List of functions where the Response calculation has been disabled because we want
/// to override the response in the Valkey library
private let disableResponseCalculationCommands: Set<String> = [
"BLPOP",
"BLMPOP",
"BRPOP",
"BZMPOP",
"BZPOPMAX",
"BZPOPMIN",
Expand All @@ -24,7 +26,8 @@ private let disableResponseCalculationCommands: Set<String> = [
"GEODIST",
"GEOPOS",
"GEOSEARCH",
"KEYS",
"HSCAN",
"ROLE",
"LMOVE",
"LMPOP",
"ROLE",
Expand All @@ -43,6 +46,7 @@ private let disableResponseCalculationCommands: Set<String> = [
"ZMPOP",
"ZPOPMAX",
"ZPOPMIN",
"ZSCAN",
]

/// List of subscribe commands, which have their own implementation in code
Expand Down
Loading
Loading