// Errors the client raises, and the helpers that pull a redis error reply apart into the
// redirect or state a caller acts on. A redis error reply is one value among the others a
// command returns, so the client turns it into `RedisError::Reply`; transport failures and a
// closed connection are `Connection` and `Closed`; a reply the client cannot make sense of is
// `Protocol`.
///|
/// Why a redis call failed: a redis error reply, a transport failure, a closed
/// connection, a reply the client could not interpret as the command promised, or a
/// transaction a watched key changed under.
pub(all) suberror RedisError {
Reply(String)
Connection(String)
Protocol(String)
Closed
Aborted
} derive(Eq, Debug)
///|
pub extend RedisError with Debug::{to_repr}
///|
pub extend RedisError with Eq::{not_equal, equal}
///|
/// The client's reply when a command returns a redis error value, or `Protocol` when the
/// reply is not an error value at all but also not the shape the command expected.
pub fn reply_error(reply : @resp.RespValue) -> RedisError {
match reply {
@resp.Error(msg) => Reply(msg)
@resp.BulkError(msg) => Reply(msg)
other => Protocol("unexpected reply: " + @resp.resp_kind(other))
}
}
///|
/// Where a cluster node sent a command.
pub(all) enum Redirect {
/// The slot lives there now, so route it there from here on.
Moved(slot~ : Int32, node~ : String)
/// The slot is migrating and this key has gone ahead: ask there this once, after `ASKING`.
Ask(slot~ : Int32, node~ : String)
} derive(Eq, Debug)
///|
pub extend Redirect with Debug::{to_repr}
///|
pub extend Redirect with Eq::{not_equal, equal}
///|
/// What a `MOVED` or `ASK` error reply points at; `None` when the message is neither.
pub fn redirect(msg : String) -> Redirect? {
let space = msg.find(" ")
guard space is Some(at) else { return None }
let tag = msg[0:at].to_owned()
if tag != "MOVED" && tag != "ASK" {
return None
}
let rest = msg[at + 1:].to_owned()
let second = rest.find(" ")
guard second is Some(at2) else { return None }
let slot_text = rest[0:at2].to_owned()
let node = rest[at2 + 1:].to_owned()
let slot = @string.parse_int(slot_text) catch { _ => return None }
Some(if tag == "MOVED" { Moved(slot~, node~) } else { Ask(slot~, node~) })
}
///|
/// Whether a redis error reply is the `NOSCRIPT` a forgotten script digest draws, so a
/// `Script` reloads and retries.
pub fn is_noscript(msg : String) -> Bool {
msg.has_prefix("NOSCRIPT")
}
///|
/// Whether a redis error reply is a `CLUSTERDOWN`, the cluster unable to serve.
pub fn is_cluster_down(msg : String) -> Bool {
msg.has_prefix("CLUSTERDOWN")
}