Skip to content
Open
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
115 changes: 115 additions & 0 deletions Sources/Valkey/Commands/Custom/ScriptingCustomCommands.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//
// 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
//
// This file is autogenerated by ValkeyCommandsBuilder

import NIOCore

extension FUNCTION {
public typealias LOADResponse = String
}

extension FUNCTION.LIST {
public typealias Response = [ResponseElement]
public struct ResponseElement: RESPTokenDecodable, Sendable {
public struct Script: RESPTokenDecodable, Sendable {
public let name: String
public let description: String?
public let flags: [String]

public init(fromRESP token: RESPToken) throws {
let map = try [String: RESPToken](fromRESP: token)
guard let name = map["name"] else { throw RESPParsingError(code: .unexpectedType, buffer: token.base) }
guard let description = map["description"] else { throw RESPParsingError(code: .unexpectedType, buffer: token.base) }
guard let flags = map["flags"] else { throw RESPParsingError(code: .unexpectedType, buffer: token.base) }
self.name = try String(fromRESP: name)
self.description = try String?(fromRESP: description)
self.flags = try [String](fromRESP: flags)
}
}
public let libraryName: String
public let engine: String
public let functions: [Script]
public let libraryCode: String?

public init(fromRESP token: RESPToken) throws {
let map = try [String: RESPToken](fromRESP: token)
guard let libraryName = map["library_name"] else { throw RESPParsingError(code: .unexpectedType, buffer: token.base) }
guard let engine = map["engine"] else { throw RESPParsingError(code: .unexpectedType, buffer: token.base) }
guard let functions = map["functions"] else { throw RESPParsingError(code: .unexpectedType, buffer: token.base) }
let libraryCode = map["library_code"]
self.libraryName = try String(fromRESP: libraryName)
self.engine = try String(fromRESP: engine)
self.functions = try [Script](fromRESP: functions)
self.libraryCode = try libraryCode.map { try String(fromRESP: $0) }
}
}
}

extension FUNCTION.LOAD {
public typealias Response = FUNCTION.LOADResponse
}

extension FUNCTION.STATS {
public struct Response: RESPTokenDecodable, Sendable {

public struct Script: RESPTokenDecodable, Sendable {
public let name: String
public let command: [ByteBuffer]
public let duration: Duration

public init(fromRESP token: RESPToken) throws {
let map = try [String: RESPToken](fromRESP: token)
guard let name = map["name"] else { throw RESPParsingError(code: .unexpectedType, buffer: token.base) }
guard let command = map["command"] else { throw RESPParsingError(code: .unexpectedType, buffer: token.base) }
guard let duration = map["duration_ms"] else { throw RESPParsingError(code: .unexpectedType, buffer: token.base) }
self.name = try .init(fromRESP: name)
self.command = try .init(fromRESP: command)
self.duration = try .milliseconds(Double(fromRESP: duration))
}
}
public struct Engine: RESPTokenDecodable, Sendable {
public let libraryCount: Int
public let functionCount: Int

public init(fromRESP token: RESPToken) throws {
let map = try [String: RESPToken](fromRESP: token)
guard let libraryCount = map["libraries_count"] else { throw RESPParsingError(code: .unexpectedType, buffer: token.base) }
guard let functionCount = map["functions_count"] else { throw RESPParsingError(code: .unexpectedType, buffer: token.base) }
self.libraryCount = try .init(fromRESP: libraryCount)
self.functionCount = try .init(fromRESP: functionCount)
}
}
public let runningScript: Script
public let engines: [String: Engine]
public init(fromRESP token: RESPToken) throws {
let map = try [String: RESPToken](fromRESP: token)
guard let runningScript = map["running_script"] else { throw RESPParsingError(code: .unexpectedType, buffer: token.base) }
guard let engines = map["engines"] else { throw RESPParsingError(code: .unexpectedType, buffer: token.base) }
self.runningScript = try .init(fromRESP: runningScript)
self.engines = try .init(fromRESP: engines)
}
}
}

extension SCRIPT {
public typealias LOADResponse = String
public typealias EXISTSResponse = [Int]
public typealias SHOWResponse = String
}

extension SCRIPT.LOAD {
public typealias Response = SCRIPT.LOADResponse
}

extension SCRIPT.EXISTS {
public typealias Response = SCRIPT.EXISTSResponse
}

extension SCRIPT.SHOW {
public typealias Response = SCRIPT.SHOWResponse
}
27 changes: 9 additions & 18 deletions Sources/Valkey/Commands/ScriptingCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,6 @@ public enum FUNCTION {
/// Returns information about all libraries.
@_documentation(visibility: internal)
public struct LIST: ValkeyCommand {
public typealias Response = RESPToken.Array

@inlinable public static var name: String { "FUNCTION LIST" }

public var libraryNamePattern: String?
Expand All @@ -131,8 +129,6 @@ public enum FUNCTION {
/// Creates a library.
@_documentation(visibility: internal)
public struct LOAD<FunctionCode: RESPStringRenderable>: ValkeyCommand {
public typealias Response = ByteBuffer

@inlinable public static var name: String { "FUNCTION LOAD" }

public var replace: Bool
Expand Down Expand Up @@ -186,8 +182,6 @@ public enum FUNCTION {
/// Returns information about a function during execution.
@_documentation(visibility: internal)
public struct STATS: ValkeyCommand {
public typealias Response = RESPToken.Map

@inlinable public static var name: String { "FUNCTION STATS" }

@inlinable public init() {
Expand Down Expand Up @@ -239,8 +233,6 @@ public enum SCRIPT {
/// Determines whether server-side Lua scripts exist in the script cache.
@_documentation(visibility: internal)
public struct EXISTS<Sha1: RESPStringRenderable>: ValkeyCommand {
public typealias Response = RESPToken.Array

@inlinable public static var name: String { "SCRIPT EXISTS" }

public var sha1s: [Sha1]
Expand Down Expand Up @@ -316,8 +308,6 @@ public enum SCRIPT {
/// Loads a server-side Lua script to the script cache.
@_documentation(visibility: internal)
public struct LOAD<Script: RESPStringRenderable>: ValkeyCommand {
public typealias Response = ByteBuffer

@inlinable public static var name: String { "SCRIPT LOAD" }

public var script: Script
Expand All @@ -334,8 +324,6 @@ public enum SCRIPT {
/// Show server-side Lua script in the script cache.
@_documentation(visibility: internal)
public struct SHOW<Sha1: RESPStringRenderable>: ValkeyCommand {
public typealias Response = ByteBuffer

@inlinable public static var name: String { "SCRIPT SHOW" }

public var sha1: Sha1
Expand Down Expand Up @@ -621,7 +609,7 @@ extension ValkeyClientProtocol {
/// - Complexity: O(N) where N is the number of functions
@inlinable
@discardableResult
public func functionList(libraryNamePattern: String? = nil, withcode: Bool = false) async throws -> RESPToken.Array {
public func functionList(libraryNamePattern: String? = nil, withcode: Bool = false) async throws -> FUNCTION.LIST.Response {
try await execute(FUNCTION.LIST(libraryNamePattern: libraryNamePattern, withcode: withcode))
}

Expand All @@ -633,7 +621,10 @@ extension ValkeyClientProtocol {
/// - Response: [String]: The library name that was loaded
@inlinable
@discardableResult
public func functionLoad<FunctionCode: RESPStringRenderable>(replace: Bool = false, functionCode: FunctionCode) async throws -> ByteBuffer {
public func functionLoad<FunctionCode: RESPStringRenderable>(
replace: Bool = false,
functionCode: FunctionCode
) async throws -> FUNCTION.LOADResponse {
try await execute(FUNCTION.LOAD(replace: replace, functionCode: functionCode))
}

Expand All @@ -657,7 +648,7 @@ extension ValkeyClientProtocol {
/// - Complexity: O(1)
@inlinable
@discardableResult
public func functionStats() async throws -> RESPToken.Map {
public func functionStats() async throws -> FUNCTION.STATS.Response {
try await execute(FUNCTION.STATS())
}

Expand All @@ -679,7 +670,7 @@ extension ValkeyClientProtocol {
/// - Response: [Array]: An array of integers that correspond to the specified SHA1 digest arguments.
@inlinable
@discardableResult
public func scriptExists<Sha1: RESPStringRenderable>(sha1s: [Sha1]) async throws -> RESPToken.Array {
public func scriptExists<Sha1: RESPStringRenderable>(sha1s: [Sha1]) async throws -> SCRIPT.EXISTSResponse {
try await execute(SCRIPT.EXISTS(sha1s: sha1s))
}

Expand Down Expand Up @@ -725,7 +716,7 @@ extension ValkeyClientProtocol {
/// - Response: [String]: The SHA1 digest of the script added into the script cache
@inlinable
@discardableResult
public func scriptLoad<Script: RESPStringRenderable>(script: Script) async throws -> ByteBuffer {
public func scriptLoad<Script: RESPStringRenderable>(script: Script) async throws -> SCRIPT.LOADResponse {
try await execute(SCRIPT.LOAD(script: script))
}

Expand All @@ -737,7 +728,7 @@ extension ValkeyClientProtocol {
/// - Response: [String]: Lua script if sha1 hash exists in script cache.
@inlinable
@discardableResult
public func scriptShow<Sha1: RESPStringRenderable>(sha1: Sha1) async throws -> ByteBuffer {
public func scriptShow<Sha1: RESPStringRenderable>(sha1: Sha1) async throws -> SCRIPT.SHOWResponse {
try await execute(SCRIPT.SHOW(sha1: sha1))
}

Expand Down
6 changes: 6 additions & 0 deletions Sources/_ValkeyCommandsBuilder/ValkeyCommandsRender.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,19 @@ private let disableResponseCalculationCommands: Set<String> = [
"CLUSTER MYID",
"CLUSTER MYSHARDID",
"CLUSTER SHARDS",
"FUNCTION LIST",
"FUNCTION LOAD",
"FUNCTION STATS",
"GEODIST",
"GEOPOS",
"GEOSEARCH",
"ROLE",
"LMOVE",
"LMPOP",
"SSCAN",
"SCRIPT EXISTS",
"SCRIPT LOAD",
"SCRIPT SHOW",
"XAUTOCLAIM",
"XCLAIM",
"XPENDING",
Expand Down
105 changes: 0 additions & 105 deletions Tests/IntegrationTests/ClientIntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -316,83 +316,6 @@ struct ClientIntegratedTests {
}
}

@Test
@available(valkeySwift 1.0, *)
func testRole() async throws {
var logger = Logger(label: "Valkey")
logger.logLevel = .debug
try await withValkeyConnection(.hostname(valkeyHostname, port: 6379), logger: logger) { connection in
let role = try await connection.role()
switch role {
case .primary:
break
case .replica, .sentinel:
Issue.record()
}
}
}

@available(valkeySwift 1.0, *)
@Test("Array with count using LMPOP")
func testArrayWithCount() async throws {
var logger = Logger(label: "Valkey")
logger.logLevel = .trace
try await withValkeyConnection(.hostname(valkeyHostname, port: 6379), logger: logger) { connection in
try await withKey(connection: connection) { key in
try await withKey(connection: connection) { key2 in
try await connection.lpush(key, elements: ["a"])
try await connection.lpush(key2, elements: ["b"])
try await connection.lpush(key2, elements: ["c"])
try await connection.lpush(key2, elements: ["d"])
let rt1 = try await connection.lmpop(keys: [key, key2], where: .right)
let (element) = try rt1?.values.decodeElements(as: (String).self)
#expect(rt1?.key == key)
#expect(element == "a")
let rt2 = try await connection.lmpop(keys: [key, key2], where: .right)
let elements2 = try rt2?.values.decode(as: [String].self)
#expect(rt2?.key == key2)
#expect(elements2 == ["b"])
let rt3 = try await connection.lmpop(keys: [key, key2], where: .right, count: 2)
let elements3 = try rt3?.values.decode(as: [String].self)
#expect(rt3?.key == key2)
#expect(elements3 == ["c", "d"])
}
}
}
}

@available(valkeySwift 1.0, *)
@Test
func testLMOVE() async throws {
var logger = Logger(label: "Valkey")
logger.logLevel = .trace
try await withValkeyConnection(.hostname(valkeyHostname, port: 6379), logger: logger) { connection in
try await withKey(connection: connection) { key in
try await withKey(connection: connection) { key2 in
let rtEmpty = try await connection.lmove(source: key, destination: key2, wherefrom: .right, whereto: .left)
#expect(rtEmpty == nil)
try await connection.lpush(key, elements: ["a"])
try await connection.lpush(key, elements: ["b"])
try await connection.lpush(key, elements: ["c"])
try await connection.lpush(key, elements: ["d"])
let list1Before = try await connection.lrange(key, start: 0, stop: -1).decode(as: [String].self)
#expect(list1Before == ["d", "c", "b", "a"])
let list2Before = try await connection.lrange(key2, start: 0, stop: -1).decode(as: [String].self)
#expect(list2Before == [])
for expectedValue in ["a", "b", "c", "d"] {
var rt = try #require(try await connection.lmove(source: key, destination: key2, wherefrom: .right, whereto: .left))
let value = rt.readString(length: 1)
#expect(value == expectedValue)
}
let list1After = try await connection.lrange(key, start: 0, stop: -1).decode(as: [String].self)
#expect(list1After == [])
let list2After = try await connection.lrange(key2, start: 0, stop: -1).decode(as: [String].self)
#expect(list2After == ["d", "c", "b", "a"])
}
}
}
}

@available(valkeySwift 1.0, *)
@Test("Test command error is thrown")
func testCommandError() async throws {
Expand Down Expand Up @@ -542,34 +465,6 @@ struct ClientIntegratedTests {
}
}

@available(valkeySwift 1.0, *)
@Test
func testGEOPOS() async throws {
var logger = Logger(label: "Valkey")
logger.logLevel = .trace
try await withValkeyConnection(.hostname(valkeyHostname, port: 6379), logger: logger) { connection in
try await withKey(connection: connection) { key in
let count = try await connection.geoadd(
key,
data: [.init(longitude: 1.0, latitude: 53.0, member: "Edinburgh"), .init(longitude: 1.4, latitude: 53.5, member: "Glasgow")]
)
#expect(count == 2)
let search = try await connection.geosearch(
key,
from: .fromlonlat(.init(longitude: 0.0, latitude: 53.0)),
by: .circle(.init(radius: 10000, unit: .mi)),
withcoord: true,
withdist: true,
withhash: true
)
print(search.map { $0.member })
try print(search.map { try $0.attributes[0].decode(as: Double.self) })
try print(search.map { try $0.attributes[1].decode(as: String.self) })
try print(search.map { try $0.attributes[2].decode(as: GeoCoordinates.self) })
}
}
}

@available(valkeySwift 1.0, *)
@Test
func testClientInfo() async throws {
Expand Down
Loading
Loading