mirror of
https://github.com/swift-server/RediStack.git
synced 2026-06-02 07:37:33 +00:00
162 lines
6.0 KiB
Swift
162 lines
6.0 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the RedisNIO open source project
|
|
//
|
|
// Copyright (c) 2019 RedisNIO project authors
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of RedisNIO project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import NIO
|
|
|
|
extension RedisClient {
|
|
/// Echos the provided message through the Redis instance.
|
|
///
|
|
/// See [https://redis.io/commands/echo](https://redis.io/commands/echo)
|
|
/// - Parameter message: The message to echo.
|
|
/// - Returns: The message sent with the command.
|
|
@inlinable
|
|
public func echo(_ message: String) -> EventLoopFuture<String> {
|
|
return send(command: "ECHO", with: [message])
|
|
.convertFromRESPValue()
|
|
}
|
|
|
|
/// Pings the server, which will respond with a message.
|
|
///
|
|
/// See [https://redis.io/commands/ping](https://redis.io/commands/ping)
|
|
/// - Parameter message: The optional message that the server should respond with.
|
|
/// - Returns: The provided message or Redis' default response of `"PONG"`.
|
|
@inlinable
|
|
public func ping(with message: String? = nil) -> EventLoopFuture<String> {
|
|
let arg = message != nil ? [message] : []
|
|
return send(command: "PING", with: arg)
|
|
.convertFromRESPValue()
|
|
}
|
|
|
|
/// Select the Redis logical database having the specified zero-based numeric index.
|
|
/// - Note: New connections always use the database `0`.
|
|
///
|
|
/// [https://redis.io/commands/select](https://redis.io/commands/select)
|
|
/// - Parameter index: The 0-based index of the database that will receive later commands.
|
|
/// - Returns: An `EventLoopFuture` that resolves when the operation has succeeded, or fails with a `RedisError`.
|
|
@inlinable
|
|
public func select(database index: Int) -> EventLoopFuture<Void> {
|
|
return send(command: "SELECT", with: [index])
|
|
.map { _ in return () }
|
|
}
|
|
|
|
/// Swaps the data of two Redis databases by their index IDs.
|
|
///
|
|
/// See [https://redis.io/commands/swapdb](https://redis.io/commands/swapdb)
|
|
/// - Parameters:
|
|
/// - first: The index of the first database.
|
|
/// - second: The index of the second database.
|
|
/// - Returns: `true` if the swap was successful.
|
|
@inlinable
|
|
public func swapDatabase(_ first: Int, with second: Int) -> EventLoopFuture<Bool> {
|
|
/// connection.swapDatabase(index: 0, withIndex: 10)
|
|
return send(command: "SWAPDB", with: [first, second])
|
|
.convertFromRESPValue(to: String.self)
|
|
.map { return $0 == "OK" }
|
|
}
|
|
|
|
/// Removes the specified keys. A key is ignored if it does not exist.
|
|
///
|
|
/// [https://redis.io/commands/del](https://redis.io/commands/del)
|
|
/// - Parameter keys: A list of keys to delete from the database.
|
|
/// - Returns: The number of keys deleted from the database.
|
|
@inlinable
|
|
public func delete(_ keys: [String]) -> EventLoopFuture<Int> {
|
|
guard keys.count > 0 else { return self.eventLoop.makeSucceededFuture(0) }
|
|
|
|
return send(command: "DEL", with: keys)
|
|
.convertFromRESPValue()
|
|
}
|
|
|
|
/// Sets a timeout on key. After the timeout has expired, the key will automatically be deleted.
|
|
/// - Note: A key with an associated timeout is often said to be "volatile" in Redis terminology.
|
|
///
|
|
/// [https://redis.io/commands/expire](https://redis.io/commands/expire)
|
|
/// - Parameters:
|
|
/// - key: The key to set the expiration on.
|
|
/// - deadline: The time from now the key will expire at.
|
|
/// - Returns: `true` if the expiration was set.
|
|
@inlinable
|
|
public func expire(_ key: String, after deadline: TimeAmount) -> EventLoopFuture<Bool> {
|
|
let amount = deadline.nanoseconds / 1_000_000_000
|
|
return send(command: "EXPIRE", with: [key, amount])
|
|
.convertFromRESPValue(to: Int.self)
|
|
.map { return $0 == 1 }
|
|
}
|
|
}
|
|
|
|
// MARK: Scan
|
|
|
|
extension RedisClient {
|
|
/// Incrementally iterates over all keys in the currently selected database.
|
|
///
|
|
/// [https://redis.io/commands/scan](https://redis.io/commands/scan)
|
|
/// - Parameters:
|
|
/// - position: The cursor position to start from.
|
|
/// - count: The number of elements to advance by. Redis default is 10.
|
|
/// - match: A glob-style pattern to filter values to be selected from the result set.
|
|
/// - Returns: A cursor position for additional invocations with a limited collection of keys found in the database.
|
|
@inlinable
|
|
public func scan(
|
|
startingFrom position: Int = 0,
|
|
count: Int? = nil,
|
|
matching match: String? = nil
|
|
) -> EventLoopFuture<(Int, [String])> {
|
|
return _scan(command: "SCAN", nil, position, count, match)
|
|
}
|
|
|
|
@usableFromInline
|
|
func _scan<T>(
|
|
command: String,
|
|
resultType: T.Type = T.self,
|
|
_ key: String?,
|
|
_ pos: Int,
|
|
_ count: Int?,
|
|
_ match: String?
|
|
) -> EventLoopFuture<(Int, T)>
|
|
where
|
|
T: RESPValueConvertible
|
|
{
|
|
var args: [RESPValueConvertible] = [pos]
|
|
|
|
if let k = key {
|
|
args.insert(k, at: 0)
|
|
}
|
|
|
|
if let m = match {
|
|
args.append("match")
|
|
args.append(m)
|
|
}
|
|
if let c = count {
|
|
args.append("count")
|
|
args.append(c)
|
|
}
|
|
|
|
let response = send(command: command, with: args).convertFromRESPValue(to: [RESPValue].self)
|
|
let position = response.flatMapThrowing { result -> Int in
|
|
guard
|
|
let value = result[0].string,
|
|
let position = Int(value)
|
|
else {
|
|
throw RedisNIOError.assertionFailure(message: "Unexpected value in response: \(result[0])")
|
|
}
|
|
return position
|
|
}
|
|
let elements = response
|
|
.map { return $0[1] }
|
|
.convertFromRESPValue(to: resultType)
|
|
|
|
return position.and(elements)
|
|
}
|
|
}
|