// Valkey native client
///|
/// Client is a native Valkey/Redis client supporting asynchronous operations.
///|
/// Client is a native Valkey/Redis client supporting asynchronous operations.
#cfg(target="native")
pub struct Client {
conn : @socket.Tcp
}
///|
/// ValkeyError represents the error types handled by the Valkey driver.
pub suberror ValkeyError {
/// ConnectionError indicates a TCP communication failure.
ConnectionError(String)
/// ProtocolError indicates a RESP protocol parsing failure.
ProtocolError(String)
} derive(Debug, ToJson)
///|
/// connect opens an asynchronous connection to the specified host and port.
#cfg(target="native")
pub async fn Client::connect(host : String, port : Int) -> Client raise Error {
try {
let addr = @socket.Addr::resolve(host, port~)
let conn = @socket.Tcp::connect(addr)
{ conn, }
} catch {
e => raise ConnectionError(e.to_string())
}
}
///|
/// send_command encodes and sends a RESP command to the server.
#cfg(target="native")
pub async fn Client::send_command(
self : Client,
args : Array[String],
) -> Unit raise Error {
let data = encode_command(args)
@io.Writer::write(self.conn, data) catch {
e => raise ConnectionError("Write failed: \{e}")
}
}
///|
/// read_response reads a RESP response from the server.
#cfg(target="native")
pub async fn Client::read_response(self : Client) -> RespValue raise Error {
let parser = RespParser::new(self.conn)
parser.parse_value() catch {
err => raise ProtocolError("RESP parse failed: \{err}")
}
}
///|
/// request sends a command and returns the response.
#cfg(target="native")
pub async fn Client::request(
self : Client,
args : Array[String],
) -> RespValue raise Error {
self.send_command(args)
self.read_response()
}
///|
/// ping sends a PING command to the server and expects a PONG response.
#cfg(target="native")
pub async fn Client::ping(self : Client) -> Unit raise Error {
match self.request(["PING"]) {
SimpleString("PONG") => ()
_ => raise ProtocolError("Expected PONG")
}
}
///|
/// get returns a string value by key, or None when the key does not exist.
#cfg(target="native")
pub async fn Client::get(
self : Client,
key : String,
) -> String? raise Error {
match self.request(["GET", key]) {
BulkString(Some(value)) => Some(value)
BulkString(None) => None
_ => raise ProtocolError("Unexpected GET response")
}
}
///|
/// set stores a string value by key.
#cfg(target="native")
pub async fn Client::set(
self : Client,
key : String,
value : String,
) -> Unit raise Error {
match self.request(["SET", key, value]) {
SimpleString("OK") => ()
_ => raise ProtocolError("Expected OK")
}
}
///|
/// del removes a key and returns the number of deleted keys.
#cfg(target="native")
pub async fn Client::del(
self : Client,
key : String,
) -> Int64 raise Error {
match self.request(["DEL", key]) {
Integer(n) => n
_ => raise ProtocolError("Expected integer DEL response")
}
}
///|
/// exists returns true when the key exists.
#cfg(target="native")
pub async fn Client::exists(
self : Client,
key : String,
) -> Bool raise Error {
match self.request(["EXISTS", key]) {
Integer(0L) => false
Integer(_) => true
_ => raise ProtocolError("Expected integer EXISTS response")
}
}
///|
/// encode_command encodes command arguments as a RESP array of bulk strings.
fn encode_command(args : Array[String]) -> Bytes {
let mut cmd = "*" + args.length().to_string() + "\r\n"
for arg in args {
let bytes = @utf8.encode(arg)
cmd = cmd + "$" + bytes.length().to_string() + "\r\n" + arg + "\r\n"
}
@utf8.encode(cmd)
}
///|
#cfg(target="native")
fn _dummy_use_native() -> Unit {
let _ = @socket.IpProtocolPreference::OnlyV4
let _ : @async.RetryMethod? = None
()
}
///|
#cfg(not(target="native"))
fn _dummy_use_non_native() -> Unit {
let _ : @async.RetryMethod? = None
()
}