use std::collections::{BTreeMap, BTreeSet};
use std::convert::From;
use std::default::Default;
use std::error;
use std::ffi::{CString, NulError};
use std::fmt;
use std::hash::{BuildHasher, Hash};
use std::io;
use std::str::{from_utf8, Utf8Error};
use std::string::FromUtf8Error;
#[cfg(feature = "ahash")]
pub(crate) use ahash::{AHashMap as HashMap, AHashSet as HashSet};
#[cfg(not(feature = "ahash"))]
pub(crate) use std::collections::{HashMap, HashSet};
use std::ops::Deref;
macro_rules! invalid_type_error {
($v:expr, $det:expr) => {{
fail!(invalid_type_error_inner!($v, $det))
}};
}
macro_rules! invalid_type_error_inner {
($v:expr, $det:expr) => {
RedisError::from((
ErrorKind::TypeError,
"Response was of incompatible type",
format!("{:?} (response was {:?})", $det, $v),
))
};
}
pub enum Expiry {
EX(usize),
PX(usize),
EXAT(usize),
PXAT(usize),
PERSIST,
}
pub enum SetExpiry {
EX(usize),
PX(usize),
EXAT(usize),
PXAT(usize),
KEEPTTL,
}
pub enum ExistenceCheck {
NX,
XX,
}
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
pub enum NumericBehavior {
NonNumeric,
NumberIsInteger,
NumberIsFloat,
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[non_exhaustive]
pub enum ErrorKind {
ResponseError,
AuthenticationFailed,
TypeError,
ExecAbortError,
BusyLoadingError,
NoScriptError,
InvalidClientConfig,
Moved,
Ask,
TryAgain,
ClusterDown,
CrossSlot,
MasterDown,
IoError,
ClientError,
ExtensionError,
ReadOnly,
MasterNameNotFoundBySentinel,
NoValidReplicasFoundBySentinel,
EmptySentinelList,
NotBusy,
#[cfg(feature = "json")]
Serialize,
}
#[derive(PartialEq, Eq, Clone)]
pub enum Value {
Nil,
Int(i64),
Data(Vec<u8>),
Bulk(Vec<Value>),
Status(String),
Okay,
}
pub struct MapIter<'a>(std::slice::Iter<'a, Value>);
impl<'a> Iterator for MapIter<'a> {
type Item = (&'a Value, &'a Value);
fn next(&mut self) -> Option<Self::Item> {
Some((self.0.next()?, self.0.next()?))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (low, high) = self.0.size_hint();
(low / 2, high.map(|h| h / 2))
}
}
impl Value {
pub fn looks_like_cursor(&self) -> bool {
match *self {
Value::Bulk(ref items) => {
if items.len() != 2 {
return false;
}
match items[0] {
Value::Data(_) => {}
_ => {
return false;
}
};
match items[1] {
Value::Bulk(_) => {}
_ => {
return false;
}
}
true
}
_ => false,
}
}
pub fn as_sequence(&self) -> Option<&[Value]> {
match self {
Value::Bulk(items) => Some(&items[..]),
Value::Nil => Some(&[]),
_ => None,
}
}
pub fn as_map_iter(&self) -> Option<MapIter<'_>> {
match self {
Value::Bulk(items) => Some(MapIter(items.iter())),
_ => None,
}
}
}
impl fmt::Debug for Value {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Value::Nil => write!(fmt, "nil"),
Value::Int(val) => write!(fmt, "int({val:?})"),
Value::Data(ref val) => match from_utf8(val) {
Ok(x) => write!(fmt, "string-data('{x:?}')"),
Err(_) => write!(fmt, "binary-data({val:?})"),
},
Value::Bulk(ref values) => {
write!(fmt, "bulk(")?;
let mut is_first = true;
for val in values.iter() {
if !is_first {
write!(fmt, ", ")?;
}
write!(fmt, "{val:?}")?;
is_first = false;
}
write!(fmt, ")")
}
Value::Okay => write!(fmt, "ok"),
Value::Status(ref s) => write!(fmt, "status({s:?})"),
}
}
}
pub struct RedisError {
repr: ErrorRepr,
}
#[cfg(feature = "json")]
impl From<serde_json::Error> for RedisError {
fn from(serde_err: serde_json::Error) -> RedisError {
RedisError::from((
ErrorKind::Serialize,
"Serialization Error",
format!("{serde_err}"),
))
}
}
#[derive(Debug)]
enum ErrorRepr {
WithDescription(ErrorKind, &'static str),
WithDescriptionAndDetail(ErrorKind, &'static str, String),
ExtensionError(String, String),
IoError(io::Error),
}
impl PartialEq for RedisError {
fn eq(&self, other: &RedisError) -> bool {
match (&self.repr, &other.repr) {
(&ErrorRepr::WithDescription(kind_a, _), &ErrorRepr::WithDescription(kind_b, _)) => {
kind_a == kind_b
}
(
&ErrorRepr::WithDescriptionAndDetail(kind_a, _, _),
&ErrorRepr::WithDescriptionAndDetail(kind_b, _, _),
) => kind_a == kind_b,
(ErrorRepr::ExtensionError(a, _), ErrorRepr::ExtensionError(b, _)) => *a == *b,
_ => false,
}
}
}
impl From<io::Error> for RedisError {
fn from(err: io::Error) -> RedisError {
RedisError {
repr: ErrorRepr::IoError(err),
}
}
}
impl From<Utf8Error> for RedisError {
fn from(_: Utf8Error) -> RedisError {
RedisError {
repr: ErrorRepr::WithDescription(ErrorKind::TypeError, "Invalid UTF-8"),
}
}
}
impl From<NulError> for RedisError {
fn from(err: NulError) -> RedisError {
RedisError {
repr: ErrorRepr::WithDescriptionAndDetail(
ErrorKind::TypeError,
"Value contains interior nul terminator",
err.to_string(),
),
}
}
}
#[cfg(feature = "tls-native-tls")]
impl From<native_tls::Error> for RedisError {
fn from(err: native_tls::Error) -> RedisError {
RedisError {
repr: ErrorRepr::WithDescriptionAndDetail(
ErrorKind::IoError,
"TLS error",
err.to_string(),
),
}
}
}
#[cfg(feature = "tls-rustls")]
impl From<rustls::Error> for RedisError {
fn from(err: rustls::Error) -> RedisError {
RedisError {
repr: ErrorRepr::WithDescriptionAndDetail(
ErrorKind::IoError,
"TLS error",
err.to_string(),
),
}
}
}
#[cfg(feature = "tls-rustls")]
impl From<rustls::client::InvalidDnsNameError> for RedisError {
fn from(err: rustls::client::InvalidDnsNameError) -> RedisError {
RedisError {
repr: ErrorRepr::WithDescriptionAndDetail(
ErrorKind::IoError,
"TLS Error",
err.to_string(),
),
}
}
}
impl From<FromUtf8Error> for RedisError {
fn from(_: FromUtf8Error) -> RedisError {
RedisError {
repr: ErrorRepr::WithDescription(ErrorKind::TypeError, "Cannot convert from UTF-8"),
}
}
}
impl From<(ErrorKind, &'static str)> for RedisError {
fn from((kind, desc): (ErrorKind, &'static str)) -> RedisError {
RedisError {
repr: ErrorRepr::WithDescription(kind, desc),
}
}
}
impl From<(ErrorKind, &'static str, String)> for RedisError {
fn from((kind, desc, detail): (ErrorKind, &'static str, String)) -> RedisError {
RedisError {
repr: ErrorRepr::WithDescriptionAndDetail(kind, desc, detail),
}
}
}
impl error::Error for RedisError {
#[allow(deprecated)]
fn description(&self) -> &str {
match self.repr {
ErrorRepr::WithDescription(_, desc) => desc,
ErrorRepr::WithDescriptionAndDetail(_, desc, _) => desc,
ErrorRepr::ExtensionError(_, _) => "extension error",
ErrorRepr::IoError(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&dyn error::Error> {
match self.repr {
ErrorRepr::IoError(ref err) => Some(err as &dyn error::Error),
_ => None,
}
}
}
impl fmt::Display for RedisError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self.repr {
ErrorRepr::WithDescription(kind, desc) => {
desc.fmt(f)?;
f.write_str("- ")?;
fmt::Debug::fmt(&kind, f)
}
ErrorRepr::WithDescriptionAndDetail(kind, desc, ref detail) => {
desc.fmt(f)?;
f.write_str(" - ")?;
fmt::Debug::fmt(&kind, f)?;
f.write_str(": ")?;
detail.fmt(f)
}
ErrorRepr::ExtensionError(ref code, ref detail) => {
code.fmt(f)?;
f.write_str(": ")?;
detail.fmt(f)
}
ErrorRepr::IoError(ref err) => err.fmt(f),
}
}
}
impl fmt::Debug for RedisError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Display::fmt(self, f)
}
}
impl RedisError {
pub fn kind(&self) -> ErrorKind {
match self.repr {
ErrorRepr::WithDescription(kind, _)
| ErrorRepr::WithDescriptionAndDetail(kind, _, _) => kind,
ErrorRepr::ExtensionError(_, _) => ErrorKind::ExtensionError,
ErrorRepr::IoError(_) => ErrorKind::IoError,
}
}
pub fn detail(&self) -> Option<&str> {
match self.repr {
ErrorRepr::WithDescriptionAndDetail(_, _, ref detail)
| ErrorRepr::ExtensionError(_, ref detail) => Some(detail.as_str()),
_ => None,
}
}
pub fn code(&self) -> Option<&str> {
match self.kind() {
ErrorKind::ResponseError => Some("ERR"),
ErrorKind::ExecAbortError => Some("EXECABORT"),
ErrorKind::BusyLoadingError => Some("LOADING"),
ErrorKind::NoScriptError => Some("NOSCRIPT"),
ErrorKind::Moved => Some("MOVED"),
ErrorKind::Ask => Some("ASK"),
ErrorKind::TryAgain => Some("TRYAGAIN"),
ErrorKind::ClusterDown => Some("CLUSTERDOWN"),
ErrorKind::CrossSlot => Some("CROSSSLOT"),
ErrorKind::MasterDown => Some("MASTERDOWN"),
ErrorKind::ReadOnly => Some("READONLY"),
ErrorKind::NotBusy => Some("NOTBUSY"),
_ => match self.repr {
ErrorRepr::ExtensionError(ref code, _) => Some(code),
_ => None,
},
}
}
pub fn category(&self) -> &str {
match self.kind() {
ErrorKind::ResponseError => "response error",
ErrorKind::AuthenticationFailed => "authentication failed",
ErrorKind::TypeError => "type error",
ErrorKind::ExecAbortError => "script execution aborted",
ErrorKind::BusyLoadingError => "busy loading",
ErrorKind::NoScriptError => "no script",
ErrorKind::InvalidClientConfig => "invalid client config",
ErrorKind::Moved => "key moved",
ErrorKind::Ask => "key moved (ask)",
ErrorKind::TryAgain => "try again",
ErrorKind::ClusterDown => "cluster down",
ErrorKind::CrossSlot => "cross-slot",
ErrorKind::MasterDown => "master down",
ErrorKind::IoError => "I/O error",
ErrorKind::ExtensionError => "extension error",
ErrorKind::ClientError => "client error",
ErrorKind::ReadOnly => "read-only",
ErrorKind::MasterNameNotFoundBySentinel => "master name not found by sentinel",
ErrorKind::NoValidReplicasFoundBySentinel => "no valid replicas found by sentinel",
ErrorKind::EmptySentinelList => "empty sentinel list",
ErrorKind::NotBusy => "not busy",
#[cfg(feature = "json")]
ErrorKind::Serialize => "serializing",
}
}
pub fn is_io_error(&self) -> bool {
self.as_io_error().is_some()
}
pub(crate) fn as_io_error(&self) -> Option<&io::Error> {
match &self.repr {
ErrorRepr::IoError(e) => Some(e),
_ => None,
}
}
pub fn is_cluster_error(&self) -> bool {
matches!(
self.kind(),
ErrorKind::Moved | ErrorKind::Ask | ErrorKind::TryAgain | ErrorKind::ClusterDown
)
}
pub fn is_connection_refusal(&self) -> bool {
match self.repr {
ErrorRepr::IoError(ref err) => {
#[allow(clippy::match_like_matches_macro)]
match err.kind() {
io::ErrorKind::ConnectionRefused => true,
io::ErrorKind::NotFound => cfg!(unix),
_ => false,
}
}
_ => false,
}
}
pub fn is_timeout(&self) -> bool {
match self.repr {
ErrorRepr::IoError(ref err) => matches!(
err.kind(),
io::ErrorKind::TimedOut | io::ErrorKind::WouldBlock
),
_ => false,
}
}
pub fn is_connection_dropped(&self) -> bool {
match self.repr {
ErrorRepr::IoError(ref err) => matches!(
err.kind(),
io::ErrorKind::BrokenPipe
| io::ErrorKind::ConnectionReset
| io::ErrorKind::UnexpectedEof
),
_ => false,
}
}
pub fn redirect_node(&self) -> Option<(&str, u16)> {
match self.kind() {
ErrorKind::Ask | ErrorKind::Moved => (),
_ => return None,
}
let mut iter = self.detail()?.split_ascii_whitespace();
let slot_id: u16 = iter.next()?.parse().ok()?;
let addr = iter.next()?;
Some((addr, slot_id))
}
#[deprecated(note = "use code() instead")]
pub fn extension_error_code(&self) -> Option<&str> {
match self.repr {
ErrorRepr::ExtensionError(ref code, _) => Some(code),
_ => None,
}
}
#[cfg(feature = "connection-manager")] pub(crate) fn clone_mostly(&self, ioerror_description: &'static str) -> Self {
let repr = match self.repr {
ErrorRepr::WithDescription(kind, desc) => ErrorRepr::WithDescription(kind, desc),
ErrorRepr::WithDescriptionAndDetail(kind, desc, ref detail) => {
ErrorRepr::WithDescriptionAndDetail(kind, desc, detail.clone())
}
ErrorRepr::ExtensionError(ref code, ref detail) => {
ErrorRepr::ExtensionError(code.clone(), detail.clone())
}
ErrorRepr::IoError(ref e) => ErrorRepr::IoError(io::Error::new(
e.kind(),
format!("{ioerror_description}: {e}"),
)),
};
Self { repr }
}
#[cfg(feature = "cluster")] pub(crate) fn is_retryable(&self) -> bool {
match self.kind() {
ErrorKind::BusyLoadingError => true,
ErrorKind::Moved => true,
ErrorKind::Ask => true,
ErrorKind::TryAgain => true,
ErrorKind::MasterDown => true,
ErrorKind::IoError => true,
ErrorKind::ReadOnly => true,
ErrorKind::ClusterDown => true,
ErrorKind::MasterNameNotFoundBySentinel => true,
ErrorKind::NoValidReplicasFoundBySentinel => true,
ErrorKind::ExtensionError => false,
ErrorKind::ExecAbortError => false,
ErrorKind::ResponseError => false,
ErrorKind::AuthenticationFailed => false,
ErrorKind::TypeError => false,
ErrorKind::NoScriptError => false,
ErrorKind::InvalidClientConfig => false,
ErrorKind::CrossSlot => false,
ErrorKind::ClientError => false,
ErrorKind::EmptySentinelList => false,
ErrorKind::NotBusy => false,
#[cfg(feature = "json")]
ErrorKind::Serialize => false,
}
}
}
pub fn make_extension_error(code: &str, detail: Option<&str>) -> RedisError {
RedisError {
repr: ErrorRepr::ExtensionError(
code.to_string(),
match detail {
Some(x) => x.to_string(),
None => "Unknown extension error encountered".to_string(),
},
),
}
}
pub type RedisResult<T> = Result<T, RedisError>;
#[cfg(feature = "aio")]
pub type RedisFuture<'a, T> = futures_util::future::BoxFuture<'a, RedisResult<T>>;
#[derive(Debug, Clone)]
pub struct InfoDict {
map: HashMap<String, Value>,
}
impl InfoDict {
pub fn new(kvpairs: &str) -> InfoDict {
let mut map = HashMap::new();
for line in kvpairs.lines() {
if line.is_empty() || line.starts_with('#') {
continue;
}
let mut p = line.splitn(2, ':');
let k = unwrap_or!(p.next(), continue).to_string();
let v = unwrap_or!(p.next(), continue).to_string();
map.insert(k, Value::Status(v));
}
InfoDict { map }
}
pub fn get<T: FromRedisValue>(&self, key: &str) -> Option<T> {
match self.find(&key) {
Some(x) => from_redis_value(x).ok(),
None => None,
}
}
pub fn find(&self, key: &&str) -> Option<&Value> {
self.map.get(*key)
}
pub fn contains_key(&self, key: &&str) -> bool {
self.find(key).is_some()
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
}
impl Deref for InfoDict {
type Target = HashMap<String, Value>;
fn deref(&self) -> &Self::Target {
&self.map
}
}
pub trait RedisWrite {
fn write_arg(&mut self, arg: &[u8]);
fn write_arg_fmt(&mut self, arg: impl fmt::Display) {
self.write_arg(arg.to_string().as_bytes())
}
}
impl RedisWrite for Vec<Vec<u8>> {
fn write_arg(&mut self, arg: &[u8]) {
self.push(arg.to_owned());
}
fn write_arg_fmt(&mut self, arg: impl fmt::Display) {
self.push(arg.to_string().into_bytes())
}
}
pub trait ToRedisArgs: Sized {
fn to_redis_args(&self) -> Vec<Vec<u8>> {
let mut out = Vec::new();
self.write_redis_args(&mut out);
out
}
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite;
fn describe_numeric_behavior(&self) -> NumericBehavior {
NumericBehavior::NonNumeric
}
fn is_single_arg(&self) -> bool {
true
}
#[doc(hidden)]
fn make_arg_vec<W>(items: &[Self], out: &mut W)
where
W: ?Sized + RedisWrite,
{
for item in items.iter() {
item.write_redis_args(out);
}
}
#[doc(hidden)]
fn make_arg_iter_ref<'a, I, W>(items: I, out: &mut W)
where
W: ?Sized + RedisWrite,
I: Iterator<Item = &'a Self>,
Self: 'a,
{
for item in items {
item.write_redis_args(out);
}
}
#[doc(hidden)]
fn is_single_vec_arg(items: &[Self]) -> bool {
items.len() == 1 && items[0].is_single_arg()
}
}
macro_rules! itoa_based_to_redis_impl {
($t:ty, $numeric:expr) => {
impl ToRedisArgs for $t {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
let mut buf = ::itoa::Buffer::new();
let s = buf.format(*self);
out.write_arg(s.as_bytes())
}
fn describe_numeric_behavior(&self) -> NumericBehavior {
$numeric
}
}
};
}
macro_rules! non_zero_itoa_based_to_redis_impl {
($t:ty, $numeric:expr) => {
impl ToRedisArgs for $t {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
let mut buf = ::itoa::Buffer::new();
let s = buf.format(self.get());
out.write_arg(s.as_bytes())
}
fn describe_numeric_behavior(&self) -> NumericBehavior {
$numeric
}
}
};
}
macro_rules! ryu_based_to_redis_impl {
($t:ty, $numeric:expr) => {
impl ToRedisArgs for $t {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
let mut buf = ::ryu::Buffer::new();
let s = buf.format(*self);
out.write_arg(s.as_bytes())
}
fn describe_numeric_behavior(&self) -> NumericBehavior {
$numeric
}
}
};
}
impl ToRedisArgs for u8 {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
let mut buf = ::itoa::Buffer::new();
let s = buf.format(*self);
out.write_arg(s.as_bytes())
}
fn make_arg_vec<W>(items: &[u8], out: &mut W)
where
W: ?Sized + RedisWrite,
{
out.write_arg(items);
}
fn is_single_vec_arg(_items: &[u8]) -> bool {
true
}
}
itoa_based_to_redis_impl!(i8, NumericBehavior::NumberIsInteger);
itoa_based_to_redis_impl!(i16, NumericBehavior::NumberIsInteger);
itoa_based_to_redis_impl!(u16, NumericBehavior::NumberIsInteger);
itoa_based_to_redis_impl!(i32, NumericBehavior::NumberIsInteger);
itoa_based_to_redis_impl!(u32, NumericBehavior::NumberIsInteger);
itoa_based_to_redis_impl!(i64, NumericBehavior::NumberIsInteger);
itoa_based_to_redis_impl!(u64, NumericBehavior::NumberIsInteger);
itoa_based_to_redis_impl!(isize, NumericBehavior::NumberIsInteger);
itoa_based_to_redis_impl!(usize, NumericBehavior::NumberIsInteger);
non_zero_itoa_based_to_redis_impl!(core::num::NonZeroU8, NumericBehavior::NumberIsInteger);
non_zero_itoa_based_to_redis_impl!(core::num::NonZeroI8, NumericBehavior::NumberIsInteger);
non_zero_itoa_based_to_redis_impl!(core::num::NonZeroU16, NumericBehavior::NumberIsInteger);
non_zero_itoa_based_to_redis_impl!(core::num::NonZeroI16, NumericBehavior::NumberIsInteger);
non_zero_itoa_based_to_redis_impl!(core::num::NonZeroU32, NumericBehavior::NumberIsInteger);
non_zero_itoa_based_to_redis_impl!(core::num::NonZeroI32, NumericBehavior::NumberIsInteger);
non_zero_itoa_based_to_redis_impl!(core::num::NonZeroU64, NumericBehavior::NumberIsInteger);
non_zero_itoa_based_to_redis_impl!(core::num::NonZeroI64, NumericBehavior::NumberIsInteger);
non_zero_itoa_based_to_redis_impl!(core::num::NonZeroUsize, NumericBehavior::NumberIsInteger);
non_zero_itoa_based_to_redis_impl!(core::num::NonZeroIsize, NumericBehavior::NumberIsInteger);
ryu_based_to_redis_impl!(f32, NumericBehavior::NumberIsFloat);
ryu_based_to_redis_impl!(f64, NumericBehavior::NumberIsFloat);
impl ToRedisArgs for bool {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
out.write_arg(if *self { b"1" } else { b"0" })
}
}
impl ToRedisArgs for String {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
out.write_arg(self.as_bytes())
}
}
impl<'a> ToRedisArgs for &'a str {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
out.write_arg(self.as_bytes())
}
}
impl<T: ToRedisArgs> ToRedisArgs for Vec<T> {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
ToRedisArgs::make_arg_vec(self, out)
}
fn is_single_arg(&self) -> bool {
ToRedisArgs::is_single_vec_arg(&self[..])
}
}
impl<'a, T: ToRedisArgs> ToRedisArgs for &'a [T] {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
ToRedisArgs::make_arg_vec(self, out)
}
fn is_single_arg(&self) -> bool {
ToRedisArgs::is_single_vec_arg(self)
}
}
impl<T: ToRedisArgs> ToRedisArgs for Option<T> {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
if let Some(ref x) = *self {
x.write_redis_args(out);
}
}
fn describe_numeric_behavior(&self) -> NumericBehavior {
match *self {
Some(ref x) => x.describe_numeric_behavior(),
None => NumericBehavior::NonNumeric,
}
}
fn is_single_arg(&self) -> bool {
match *self {
Some(ref x) => x.is_single_arg(),
None => false,
}
}
}
impl<T: ToRedisArgs> ToRedisArgs for &T {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
(*self).write_redis_args(out)
}
fn is_single_arg(&self) -> bool {
(*self).is_single_arg()
}
}
impl<T: ToRedisArgs + Hash + Eq, S: BuildHasher + Default> ToRedisArgs
for std::collections::HashSet<T, S>
{
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
ToRedisArgs::make_arg_iter_ref(self.iter(), out)
}
fn is_single_arg(&self) -> bool {
self.len() <= 1
}
}
#[cfg(feature = "ahash")]
impl<T: ToRedisArgs + Hash + Eq, S: BuildHasher + Default> ToRedisArgs for ahash::AHashSet<T, S> {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
ToRedisArgs::make_arg_iter_ref(self.iter(), out)
}
fn is_single_arg(&self) -> bool {
self.len() <= 1
}
}
impl<T: ToRedisArgs + Hash + Eq + Ord> ToRedisArgs for BTreeSet<T> {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
ToRedisArgs::make_arg_iter_ref(self.iter(), out)
}
fn is_single_arg(&self) -> bool {
self.len() <= 1
}
}
impl<T: ToRedisArgs + Hash + Eq + Ord, V: ToRedisArgs> ToRedisArgs for BTreeMap<T, V> {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
for (key, value) in self {
assert!(key.is_single_arg() && value.is_single_arg());
key.write_redis_args(out);
value.write_redis_args(out);
}
}
fn is_single_arg(&self) -> bool {
self.len() <= 1
}
}
impl<T: ToRedisArgs + Hash + Eq + Ord, V: ToRedisArgs> ToRedisArgs
for std::collections::HashMap<T, V>
{
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
for (key, value) in self {
assert!(key.is_single_arg() && value.is_single_arg());
key.write_redis_args(out);
value.write_redis_args(out);
}
}
fn is_single_arg(&self) -> bool {
self.len() <= 1
}
}
macro_rules! to_redis_args_for_tuple {
() => ();
($($name:ident,)+) => (
#[doc(hidden)]
impl<$($name: ToRedisArgs),*> ToRedisArgs for ($($name,)*) {
#[allow(non_snake_case, unused_variables)]
fn write_redis_args<W>(&self, out: &mut W) where W: ?Sized + RedisWrite {
let ($(ref $name,)*) = *self;
$($name.write_redis_args(out);)*
}
#[allow(non_snake_case, unused_variables)]
fn is_single_arg(&self) -> bool {
let mut n = 0u32;
$(let $name = (); n += 1;)*
n == 1
}
}
to_redis_args_for_tuple_peel!($($name,)*);
)
}
macro_rules! to_redis_args_for_tuple_peel {
($name:ident, $($other:ident,)*) => (to_redis_args_for_tuple!($($other,)*);)
}
to_redis_args_for_tuple! { T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, }
macro_rules! to_redis_args_for_array {
($($N:expr)+) => {
$(
impl<'a, T: ToRedisArgs> ToRedisArgs for &'a [T; $N] {
fn write_redis_args<W>(&self, out: &mut W) where W: ?Sized + RedisWrite {
ToRedisArgs::make_arg_vec(*self, out)
}
fn is_single_arg(&self) -> bool {
ToRedisArgs::is_single_vec_arg(*self)
}
}
)+
}
}
to_redis_args_for_array! {
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32
}
pub trait FromRedisValue: Sized {
fn from_redis_value(v: &Value) -> RedisResult<Self>;
fn from_redis_values(items: &[Value]) -> RedisResult<Vec<Self>> {
items.iter().map(FromRedisValue::from_redis_value).collect()
}
fn from_byte_vec(_vec: &[u8]) -> Option<Vec<Self>> {
Self::from_redis_value(&Value::Data(_vec.into()))
.map(|rv| vec![rv])
.ok()
}
}
macro_rules! from_redis_value_for_num_internal {
($t:ty, $v:expr) => {{
let v = $v;
match *v {
Value::Int(val) => Ok(val as $t),
Value::Status(ref s) => match s.parse::<$t>() {
Ok(rv) => Ok(rv),
Err(_) => invalid_type_error!(v, "Could not convert from string."),
},
Value::Data(ref bytes) => match from_utf8(bytes)?.parse::<$t>() {
Ok(rv) => Ok(rv),
Err(_) => invalid_type_error!(v, "Could not convert from string."),
},
_ => invalid_type_error!(v, "Response type not convertible to numeric."),
}
}};
}
macro_rules! from_redis_value_for_num {
($t:ty) => {
impl FromRedisValue for $t {
fn from_redis_value(v: &Value) -> RedisResult<$t> {
from_redis_value_for_num_internal!($t, v)
}
}
};
}
impl FromRedisValue for u8 {
fn from_redis_value(v: &Value) -> RedisResult<u8> {
from_redis_value_for_num_internal!(u8, v)
}
fn from_byte_vec(vec: &[u8]) -> Option<Vec<u8>> {
Some(vec.to_vec())
}
}
from_redis_value_for_num!(i8);
from_redis_value_for_num!(i16);
from_redis_value_for_num!(u16);
from_redis_value_for_num!(i32);
from_redis_value_for_num!(u32);
from_redis_value_for_num!(i64);
from_redis_value_for_num!(u64);
from_redis_value_for_num!(i128);
from_redis_value_for_num!(u128);
from_redis_value_for_num!(f32);
from_redis_value_for_num!(f64);
from_redis_value_for_num!(isize);
from_redis_value_for_num!(usize);
impl FromRedisValue for bool {
fn from_redis_value(v: &Value) -> RedisResult<bool> {
match *v {
Value::Nil => Ok(false),
Value::Int(val) => Ok(val != 0),
Value::Status(ref s) => {
if &s[..] == "1" {
Ok(true)
} else if &s[..] == "0" {
Ok(false)
} else {
invalid_type_error!(v, "Response status not valid boolean");
}
}
Value::Data(ref bytes) => {
if bytes == b"1" {
Ok(true)
} else if bytes == b"0" {
Ok(false)
} else {
invalid_type_error!(v, "Response type not bool compatible.");
}
}
Value::Okay => Ok(true),
_ => invalid_type_error!(v, "Response type not bool compatible."),
}
}
}
impl FromRedisValue for CString {
fn from_redis_value(v: &Value) -> RedisResult<CString> {
match *v {
Value::Data(ref bytes) => Ok(CString::new(bytes.as_slice())?),
Value::Okay => Ok(CString::new("OK")?),
Value::Status(ref val) => Ok(CString::new(val.as_bytes())?),
_ => invalid_type_error!(v, "Response type not CString compatible."),
}
}
}
impl FromRedisValue for String {
fn from_redis_value(v: &Value) -> RedisResult<String> {
match *v {
Value::Data(ref bytes) => Ok(from_utf8(bytes)?.to_string()),
Value::Okay => Ok("OK".to_string()),
Value::Status(ref val) => Ok(val.to_string()),
_ => invalid_type_error!(v, "Response type not string compatible."),
}
}
}
impl<T: FromRedisValue> FromRedisValue for Vec<T> {
fn from_redis_value(v: &Value) -> RedisResult<Vec<T>> {
match *v {
Value::Data(ref bytes) => match FromRedisValue::from_byte_vec(bytes) {
Some(x) => Ok(x),
None => invalid_type_error!(
v,
format!("Conversion to Vec<{}> failed.", std::any::type_name::<T>())
),
},
Value::Bulk(ref items) => FromRedisValue::from_redis_values(items),
Value::Nil => Ok(vec![]),
_ => invalid_type_error!(v, "Response type not vector compatible."),
}
}
}
impl<K: FromRedisValue + Eq + Hash, V: FromRedisValue, S: BuildHasher + Default> FromRedisValue
for std::collections::HashMap<K, V, S>
{
fn from_redis_value(v: &Value) -> RedisResult<std::collections::HashMap<K, V, S>> {
match *v {
Value::Nil => Ok(Default::default()),
_ => v
.as_map_iter()
.ok_or_else(|| {
invalid_type_error_inner!(v, "Response type not hashmap compatible")
})?
.map(|(k, v)| Ok((from_redis_value(k)?, from_redis_value(v)?)))
.collect(),
}
}
}
#[cfg(feature = "ahash")]
impl<K: FromRedisValue + Eq + Hash, V: FromRedisValue, S: BuildHasher + Default> FromRedisValue
for ahash::AHashMap<K, V, S>
{
fn from_redis_value(v: &Value) -> RedisResult<ahash::AHashMap<K, V, S>> {
match *v {
Value::Nil => Ok(ahash::AHashMap::with_hasher(Default::default())),
_ => v
.as_map_iter()
.ok_or_else(|| {
invalid_type_error_inner!(v, "Response type not hashmap compatible")
})?
.map(|(k, v)| Ok((from_redis_value(k)?, from_redis_value(v)?)))
.collect(),
}
}
}
impl<K: FromRedisValue + Eq + Hash, V: FromRedisValue> FromRedisValue for BTreeMap<K, V>
where
K: Ord,
{
fn from_redis_value(v: &Value) -> RedisResult<BTreeMap<K, V>> {
v.as_map_iter()
.ok_or_else(|| invalid_type_error_inner!(v, "Response type not btreemap compatible"))?
.map(|(k, v)| Ok((from_redis_value(k)?, from_redis_value(v)?)))
.collect()
}
}
impl<T: FromRedisValue + Eq + Hash, S: BuildHasher + Default> FromRedisValue
for std::collections::HashSet<T, S>
{
fn from_redis_value(v: &Value) -> RedisResult<std::collections::HashSet<T, S>> {
let items = v
.as_sequence()
.ok_or_else(|| invalid_type_error_inner!(v, "Response type not hashset compatible"))?;
items.iter().map(|item| from_redis_value(item)).collect()
}
}
#[cfg(feature = "ahash")]
impl<T: FromRedisValue + Eq + Hash, S: BuildHasher + Default> FromRedisValue
for ahash::AHashSet<T, S>
{
fn from_redis_value(v: &Value) -> RedisResult<ahash::AHashSet<T, S>> {
let items = v
.as_sequence()
.ok_or_else(|| invalid_type_error_inner!(v, "Response type not hashset compatible"))?;
items.iter().map(|item| from_redis_value(item)).collect()
}
}
impl<T: FromRedisValue + Eq + Hash> FromRedisValue for BTreeSet<T>
where
T: Ord,
{
fn from_redis_value(v: &Value) -> RedisResult<BTreeSet<T>> {
let items = v
.as_sequence()
.ok_or_else(|| invalid_type_error_inner!(v, "Response type not btreeset compatible"))?;
items.iter().map(|item| from_redis_value(item)).collect()
}
}
impl FromRedisValue for Value {
fn from_redis_value(v: &Value) -> RedisResult<Value> {
Ok(v.clone())
}
}
impl FromRedisValue for () {
fn from_redis_value(_v: &Value) -> RedisResult<()> {
Ok(())
}
}
macro_rules! from_redis_value_for_tuple {
() => ();
($($name:ident,)+) => (
#[doc(hidden)]
impl<$($name: FromRedisValue),*> FromRedisValue for ($($name,)*) {
#[allow(non_snake_case, unused_variables)]
fn from_redis_value(v: &Value) -> RedisResult<($($name,)*)> {
match *v {
Value::Bulk(ref items) => {
let mut n = 0;
$(let $name = (); n += 1;)*
if items.len() != n {
invalid_type_error!(v, "Bulk response of wrong dimension")
}
let mut i = 0;
Ok(($({let $name = (); from_redis_value(
&items[{ i += 1; i - 1 }])?},)*))
}
_ => invalid_type_error!(v, "Not a bulk response")
}
}
#[allow(non_snake_case, unused_variables)]
fn from_redis_values(items: &[Value]) -> RedisResult<Vec<($($name,)*)>> {
let mut n = 0;
$(let $name = (); n += 1;)*
if items.len() % n != 0 {
invalid_type_error!(items, "Bulk response of wrong dimension")
}
let mut rv = vec![];
if items.len() == 0 {
return Ok(rv)
}
for chunk in items.chunks_exact(n) {
match chunk {
[$($name),*] => rv.push(($(from_redis_value($name)?),*),),
_ => unreachable!(),
}
}
Ok(rv)
}
}
from_redis_value_for_tuple_peel!($($name,)*);
)
}
macro_rules! from_redis_value_for_tuple_peel {
($name:ident, $($other:ident,)*) => (from_redis_value_for_tuple!($($other,)*);)
}
from_redis_value_for_tuple! { T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, }
impl FromRedisValue for InfoDict {
fn from_redis_value(v: &Value) -> RedisResult<InfoDict> {
let s: String = from_redis_value(v)?;
Ok(InfoDict::new(&s))
}
}
impl<T: FromRedisValue> FromRedisValue for Option<T> {
fn from_redis_value(v: &Value) -> RedisResult<Option<T>> {
if *v == Value::Nil {
return Ok(None);
}
Ok(Some(from_redis_value(v)?))
}
}
#[cfg(feature = "bytes")]
impl FromRedisValue for bytes::Bytes {
fn from_redis_value(v: &Value) -> RedisResult<Self> {
match v {
Value::Data(bytes_vec) => Ok(bytes::Bytes::copy_from_slice(bytes_vec.as_ref())),
_ => invalid_type_error!(v, "Not binary data"),
}
}
}
pub fn from_redis_value<T: FromRedisValue>(v: &Value) -> RedisResult<T> {
FromRedisValue::from_redis_value(v)
}