use crate::errors::ParsingError;
#[cfg(feature = "ahash")]
pub(crate) use ahash::{AHashMap as HashMap, AHashSet as HashSet};
#[cfg(feature = "num-bigint")]
use num_bigint::BigInt;
use std::borrow::Cow;
#[cfg(not(feature = "ahash"))]
pub(crate) use std::collections::{HashMap, HashSet};
use std::default::Default;
use std::ffi::CString;
use std::fmt;
use std::hash::{BuildHasher, Hash};
use std::io;
use std::ops::Deref;
use std::str::from_utf8;
use crate::errors::{RedisError, ServerError};
#[non_exhaustive]
pub enum Expiry {
EX(u64),
PX(u64),
EXAT(u64),
PXAT(u64),
PERSIST,
}
#[derive(Clone, Copy)]
#[non_exhaustive]
pub enum SetExpiry {
EX(u64),
PX(u64),
EXAT(u64),
PXAT(u64),
KEEPTTL,
}
impl ToRedisArgs for SetExpiry {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
match self {
SetExpiry::EX(secs) => {
out.write_arg(b"EX");
out.write_arg(format!("{secs}").as_bytes());
}
SetExpiry::PX(millis) => {
out.write_arg(b"PX");
out.write_arg(format!("{millis}").as_bytes());
}
SetExpiry::EXAT(unix_time) => {
out.write_arg(b"EXAT");
out.write_arg(format!("{unix_time}").as_bytes());
}
SetExpiry::PXAT(unix_time) => {
out.write_arg(b"PXAT");
out.write_arg(format!("{unix_time}").as_bytes());
}
SetExpiry::KEEPTTL => {
out.write_arg(b"KEEPTTL");
}
}
}
}
#[derive(Clone, Copy)]
#[non_exhaustive]
pub enum ExistenceCheck {
NX,
XX,
}
impl ToRedisArgs for ExistenceCheck {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
match self {
ExistenceCheck::NX => {
out.write_arg(b"NX");
}
ExistenceCheck::XX => {
out.write_arg(b"XX");
}
}
}
}
#[derive(Clone, Copy)]
#[non_exhaustive]
pub enum FieldExistenceCheck {
FNX,
FXX,
}
impl ToRedisArgs for FieldExistenceCheck {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
match self {
FieldExistenceCheck::FNX => out.write_arg(b"FNX"),
FieldExistenceCheck::FXX => out.write_arg(b"FXX"),
}
}
}
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
#[non_exhaustive]
pub enum NumericBehavior {
NonNumeric,
NumberIsInteger,
NumberIsFloat,
}
#[derive(PartialEq, Clone, Default)]
#[non_exhaustive]
pub enum Value {
#[default]
Nil,
Int(i64),
BulkString(Vec<u8>),
Array(Vec<Value>),
SimpleString(String),
Okay,
Map(Vec<(Value, Value)>),
Attribute {
data: Box<Value>,
attributes: Vec<(Value, Value)>,
},
Set(Vec<Value>),
Double(f64),
Boolean(bool),
VerbatimString {
format: VerbatimFormat,
text: String,
},
#[cfg(feature = "num-bigint")]
BigNumber(BigInt),
#[cfg(not(feature = "num-bigint"))]
BigNumber(Vec<u8>),
Push {
kind: PushKind,
data: Vec<Value>,
},
ServerError(ServerError),
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum ValueComparison {
IFEQ(String),
IFNE(String),
IFDEQ(String),
IFDNE(String),
}
impl ValueComparison {
pub fn ifeq(value: impl ToSingleRedisArg) -> Self {
ValueComparison::IFEQ(Self::arg_to_string(value))
}
pub fn ifne(value: impl ToSingleRedisArg) -> Self {
ValueComparison::IFNE(Self::arg_to_string(value))
}
pub fn ifdeq(digest: impl ToSingleRedisArg) -> Self {
ValueComparison::IFDEQ(Self::arg_to_string(digest))
}
pub fn ifdne(digest: impl ToSingleRedisArg) -> Self {
ValueComparison::IFDNE(Self::arg_to_string(digest))
}
fn arg_to_string(value: impl ToSingleRedisArg) -> String {
let args = value.to_redis_args();
String::from_utf8_lossy(&args[0]).into_owned()
}
}
impl ToRedisArgs for ValueComparison {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
match self {
ValueComparison::IFEQ(value) => {
out.write_arg(b"IFEQ");
out.write_arg(value.as_bytes());
}
ValueComparison::IFNE(value) => {
out.write_arg(b"IFNE");
out.write_arg(value.as_bytes());
}
ValueComparison::IFDEQ(digest) => {
out.write_arg(b"IFDEQ");
out.write_arg(digest.as_bytes());
}
ValueComparison::IFDNE(digest) => {
out.write_arg(b"IFDNE");
out.write_arg(digest.as_bytes());
}
}
}
}
#[derive(PartialEq, Clone, Debug)]
#[non_exhaustive]
pub enum VerbatimFormat {
Unknown(String),
Markdown,
Text,
}
#[derive(PartialEq, Clone, Debug)]
#[non_exhaustive]
pub enum PushKind {
Disconnection,
Other(String),
Invalidate,
Message,
PMessage,
SMessage,
Unsubscribe,
PUnsubscribe,
SUnsubscribe,
Subscribe,
PSubscribe,
SSubscribe,
}
impl PushKind {
#[cfg(feature = "aio")]
pub(crate) fn has_reply(&self) -> bool {
matches!(
self,
&PushKind::Unsubscribe
| &PushKind::PUnsubscribe
| &PushKind::SUnsubscribe
| &PushKind::Subscribe
| &PushKind::PSubscribe
| &PushKind::SSubscribe
)
}
}
impl fmt::Display for VerbatimFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
VerbatimFormat::Markdown => write!(f, "mkd"),
VerbatimFormat::Unknown(val) => write!(f, "{val}"),
VerbatimFormat::Text => write!(f, "txt"),
}
}
}
impl fmt::Display for PushKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PushKind::Other(kind) => write!(f, "{kind}"),
PushKind::Invalidate => write!(f, "invalidate"),
PushKind::Message => write!(f, "message"),
PushKind::PMessage => write!(f, "pmessage"),
PushKind::SMessage => write!(f, "smessage"),
PushKind::Unsubscribe => write!(f, "unsubscribe"),
PushKind::PUnsubscribe => write!(f, "punsubscribe"),
PushKind::SUnsubscribe => write!(f, "sunsubscribe"),
PushKind::Subscribe => write!(f, "subscribe"),
PushKind::PSubscribe => write!(f, "psubscribe"),
PushKind::SSubscribe => write!(f, "ssubscribe"),
PushKind::Disconnection => write!(f, "disconnection"),
}
}
}
#[non_exhaustive]
pub enum MapIter<'a> {
Array(std::slice::Iter<'a, Value>),
Map(std::slice::Iter<'a, (Value, Value)>),
}
impl<'a> Iterator for MapIter<'a> {
type Item = (&'a Value, &'a Value);
fn next(&mut self) -> Option<Self::Item> {
match self {
MapIter::Array(iter) => Some((iter.next()?, iter.next()?)),
MapIter::Map(iter) => {
let (k, v) = iter.next()?;
Some((k, v))
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
MapIter::Array(iter) => iter.size_hint(),
MapIter::Map(iter) => iter.size_hint(),
}
}
}
#[non_exhaustive]
pub enum OwnedMapIter {
Array(std::vec::IntoIter<Value>),
Map(std::vec::IntoIter<(Value, Value)>),
}
impl Iterator for OwnedMapIter {
type Item = (Value, Value);
fn next(&mut self) -> Option<Self::Item> {
match self {
OwnedMapIter::Array(iter) => Some((iter.next()?, iter.next()?)),
OwnedMapIter::Map(iter) => iter.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
OwnedMapIter::Array(iter) => {
let (low, high) = iter.size_hint();
(low / 2, high.map(|h| h / 2))
}
OwnedMapIter::Map(iter) => iter.size_hint(),
}
}
}
impl Value {
pub fn looks_like_cursor(&self) -> bool {
match *self {
Value::Array(ref items) => {
if items.len() != 2 {
return false;
}
matches!(items[0], Value::BulkString(_)) && matches!(items[1], Value::Array(_))
}
_ => false,
}
}
pub fn as_sequence(&self) -> Option<&[Value]> {
match self {
Value::Array(items) => Some(&items[..]),
Value::Set(items) => Some(&items[..]),
Value::Nil => Some(&[]),
_ => None,
}
}
pub fn into_sequence(self) -> Result<Vec<Value>, Value> {
match self {
Value::Array(items) => Ok(items),
Value::Set(items) => Ok(items),
Value::Nil => Ok(vec![]),
_ => Err(self),
}
}
pub fn as_map_iter(&self) -> Option<MapIter<'_>> {
match self {
Value::Array(items) => {
if items.len() % 2 == 0 {
Some(MapIter::Array(items.iter()))
} else {
None
}
}
Value::Map(items) => Some(MapIter::Map(items.iter())),
_ => None,
}
}
pub fn into_map_iter(self) -> Result<OwnedMapIter, Value> {
match self {
Value::Array(items) => {
if items.len() % 2 == 0 {
Ok(OwnedMapIter::Array(items.into_iter()))
} else {
Err(Value::Array(items))
}
}
Value::Map(items) => Ok(OwnedMapIter::Map(items.into_iter())),
_ => Err(self),
}
}
pub fn extract_error(self) -> RedisResult<Self> {
match self {
Self::Array(val) => Ok(Self::Array(Self::extract_error_vec(val)?)),
Self::Map(map) => Ok(Self::Map(Self::extract_error_map(map)?)),
Self::Attribute { data, attributes } => {
let data = Box::new((*data).extract_error()?);
let attributes = Self::extract_error_map(attributes)?;
Ok(Value::Attribute { data, attributes })
}
Self::Set(set) => Ok(Self::Set(Self::extract_error_vec(set)?)),
Self::Push { kind, data } => Ok(Self::Push {
kind,
data: Self::extract_error_vec(data)?,
}),
Value::ServerError(err) => Err(err.into()),
_ => Ok(self),
}
}
pub(crate) fn extract_error_vec(vec: Vec<Self>) -> RedisResult<Vec<Self>> {
vec.into_iter()
.map(Self::extract_error)
.collect::<RedisResult<Vec<_>>>()
}
pub(crate) fn extract_error_map(map: Vec<(Self, Self)>) -> RedisResult<Vec<(Self, Self)>> {
let mut vec = Vec::with_capacity(map.len());
for (key, value) in map.into_iter() {
vec.push((key.extract_error()?, value.extract_error()?));
}
Ok(vec)
}
fn is_collection_of_len(&self, len: usize) -> bool {
match self {
Value::Array(values) => values.len() == len,
Value::Map(items) => items.len() * 2 == len,
Value::Set(values) => values.len() == len,
_ => false,
}
}
#[cfg(feature = "cluster-async")]
pub(crate) fn is_error_that_requires_action(&self) -> bool {
matches!(self, Self::ServerError(error) if error.requires_action())
}
}
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::BulkString(ref val) => match from_utf8(val) {
Ok(x) => write!(fmt, "bulk-string('{x:?}')"),
Err(_) => write!(fmt, "binary-data({val:?})"),
},
Value::Array(ref values) => write!(fmt, "array({values:?})"),
Value::Push { ref kind, ref data } => write!(fmt, "push({kind:?}, {data:?})"),
Value::Okay => write!(fmt, "ok"),
Value::SimpleString(ref s) => write!(fmt, "simple-string({s:?})"),
Value::Map(ref values) => write!(fmt, "map({values:?})"),
Value::Attribute {
ref data,
attributes: _,
} => write!(fmt, "attribute({data:?})"),
Value::Set(ref values) => write!(fmt, "set({values:?})"),
Value::Double(ref d) => write!(fmt, "double({d:?})"),
Value::Boolean(ref b) => write!(fmt, "boolean({b:?})"),
Value::VerbatimString {
ref format,
ref text,
} => {
write!(fmt, "verbatim-string({format:?},{text:?})")
}
Value::BigNumber(ref m) => write!(fmt, "big-number({m:?})"),
Value::ServerError(ref err) => match err.details() {
Some(details) => write!(fmt, "Server error: `{}: {details}`", err.code()),
None => write!(fmt, "Server error: `{}`", err.code()),
},
}
}
}
pub type RedisResult<T> = Result<T, RedisError>;
impl<T: FromRedisValue> FromRedisValue for RedisResult<T> {
fn from_redis_value_ref(value: &Value) -> Result<Self, ParsingError> {
match value {
Value::ServerError(err) => Ok(Err(err.clone().into())),
_ => from_redis_value_ref(value).map(|result| Ok(result)),
}
}
fn from_redis_value(value: Value) -> Result<Self, ParsingError> {
match value {
Value::ServerError(err) => Ok(Err(err.into())),
_ => from_redis_value(value).map(|result| Ok(result)),
}
}
}
#[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, v) = match (p.next(), p.next()) {
(Some(k), Some(v)) => (k.to_string(), v.to_string()),
_ => continue,
};
map.insert(k, Value::SimpleString(v));
}
InfoDict { map }
}
pub fn get<T: FromRedisValue>(&self, key: &str) -> Option<T> {
match self.find(&key) {
Some(x) => from_redis_value_ref(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
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[non_exhaustive]
pub enum Role {
Primary {
replication_offset: u64,
replicas: Vec<ReplicaInfo>,
},
Replica {
primary_ip: String,
primary_port: u16,
replication_state: String,
data_received: u64,
},
Sentinel {
primary_names: Vec<String>,
},
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ReplicaInfo {
pub ip: String,
pub port: u16,
pub replication_offset: i64,
}
impl FromRedisValue for ReplicaInfo {
fn from_redis_value_ref(v: &Value) -> Result<Self, ParsingError> {
Self::from_redis_value(v.clone())
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError> {
let v = match get_owned_inner_value(v).into_sequence() {
Ok(v) => v,
Err(v) => crate::errors::invalid_type_error!(v, "Replica response should be an array"),
};
if v.len() < 3 {
crate::errors::invalid_type_error!(v, "Replica array is too short, expected 3 elements")
}
let mut v = v.into_iter();
let ip = from_redis_value(v.next().expect("len was checked"))?;
let port = from_redis_value(v.next().expect("len was checked"))?;
let offset = from_redis_value(v.next().expect("len was checked"))?;
Ok(ReplicaInfo {
ip,
port,
replication_offset: offset,
})
}
}
impl FromRedisValue for Role {
fn from_redis_value_ref(v: &Value) -> Result<Self, ParsingError> {
Self::from_redis_value(v.clone())
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError> {
let v = match get_owned_inner_value(v).into_sequence() {
Ok(v) => v,
Err(v) => crate::errors::invalid_type_error!(v, "Role response should be an array"),
};
if v.len() < 2 {
crate::errors::invalid_type_error!(
v,
"Role array is too short, expected at least 2 elements"
)
}
match &v[0] {
Value::BulkString(role) => match role.as_slice() {
b"master" => Role::new_primary(v),
b"slave" => Role::new_replica(v),
b"sentinel" => Role::new_sentinel(v),
_ => crate::errors::invalid_type_error!(
v,
"Role type is not master, slave or sentinel"
),
},
_ => crate::errors::invalid_type_error!(v, "Role type is not a bulk string"),
}
}
}
impl Role {
fn new_primary(values: Vec<Value>) -> Result<Self, ParsingError> {
if values.len() < 3 {
crate::errors::invalid_type_error!(
values,
"Role primary response too short, expected 3 elements"
)
}
let mut values = values.into_iter();
_ = values.next();
let replication_offset = from_redis_value(values.next().expect("len was checked"))?;
let replicas = from_redis_value(values.next().expect("len was checked"))?;
Ok(Role::Primary {
replication_offset,
replicas,
})
}
fn new_replica(values: Vec<Value>) -> Result<Self, ParsingError> {
if values.len() < 5 {
crate::errors::invalid_type_error!(
values,
"Role replica response too short, expected 5 elements"
)
}
let mut values = values.into_iter();
_ = values.next();
let primary_ip = from_redis_value(values.next().expect("len was checked"))?;
let primary_port = from_redis_value(values.next().expect("len was checked"))?;
let replication_state = from_redis_value(values.next().expect("len was checked"))?;
let data_received = from_redis_value(values.next().expect("len was checked"))?;
Ok(Role::Replica {
primary_ip,
primary_port,
replication_state,
data_received,
})
}
fn new_sentinel(values: Vec<Value>) -> Result<Self, ParsingError> {
if values.len() < 2 {
crate::errors::invalid_type_error!(
values,
"Role sentinel response too short, expected at least 2 elements"
)
}
let second_val = values.into_iter().nth(1).expect("len was checked");
let primary_names = from_redis_value(second_val)?;
Ok(Role::Sentinel { primary_names })
}
}
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())
}
fn writer_for_next_arg(&mut self) -> impl io::Write + '_;
fn reserve_space_for_args(&mut self, additional: impl IntoIterator<Item = usize>) {
let _do_nothing = additional;
}
#[cfg(feature = "bytes")]
fn bufmut_for_next_arg(&mut self, capacity: usize) -> impl bytes::BufMut + '_ {
struct Wrapper<'a> {
buf: Vec<u8>,
writer: Box<dyn io::Write + 'a>,
}
unsafe impl bytes::BufMut for Wrapper<'_> {
fn remaining_mut(&self) -> usize {
self.buf.remaining_mut()
}
unsafe fn advance_mut(&mut self, cnt: usize) {
unsafe {
self.buf.advance_mut(cnt);
}
}
fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
self.buf.chunk_mut()
}
fn put<T: bytes::buf::Buf>(&mut self, src: T)
where
Self: Sized,
{
self.buf.put(src);
}
fn put_slice(&mut self, src: &[u8]) {
self.buf.put_slice(src);
}
fn put_bytes(&mut self, val: u8, cnt: usize) {
self.buf.put_bytes(val, cnt);
}
}
impl Drop for Wrapper<'_> {
fn drop(&mut self) {
self.writer.write_all(&self.buf).unwrap()
}
}
Wrapper {
buf: Vec::with_capacity(capacity),
writer: Box::new(self.writer_for_next_arg()),
}
}
}
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())
}
fn writer_for_next_arg(&mut self) -> impl io::Write + '_ {
self.push(Vec::new());
self.last_mut().unwrap()
}
fn reserve_space_for_args(&mut self, additional: impl IntoIterator<Item = usize>) {
self.reserve(additional.into_iter().count());
}
#[cfg(feature = "bytes")]
fn bufmut_for_next_arg(&mut self, capacity: usize) -> impl bytes::BufMut + '_ {
self.push(Vec::with_capacity(capacity));
self.last_mut().unwrap()
}
}
pub trait ToSingleRedisArg: ToRedisArgs {}
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 num_of_args(&self) -> usize {
1
}
#[doc(hidden)]
fn write_args_from_slice<W>(items: &[Self], out: &mut W)
where
W: ?Sized + RedisWrite,
{
Self::make_arg_iter_ref(items.iter(), 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].num_of_args() <= 1
}
}
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
}
}
impl ToSingleRedisArg for $t {}
};
}
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
}
}
impl ToSingleRedisArg for $t {}
};
}
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 ToSingleRedisArg for $t {}
};
}
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 write_args_from_slice<W>(items: &[u8], out: &mut W)
where
W: ?Sized + RedisWrite,
{
out.write_arg(items);
}
fn is_single_vec_arg(_items: &[u8]) -> bool {
true
}
}
impl ToSingleRedisArg for u8 {}
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!(i128, NumericBehavior::NumberIsInteger);
itoa_based_to_redis_impl!(u128, 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::NonZeroU128, NumericBehavior::NumberIsInteger);
non_zero_itoa_based_to_redis_impl!(core::num::NonZeroI128, 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);
#[cfg(any(
feature = "rust_decimal",
feature = "bigdecimal",
feature = "num-bigint"
))]
macro_rules! bignum_to_redis_impl {
($t:ty) => {
impl ToRedisArgs for $t {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
out.write_arg(&self.to_string().into_bytes())
}
}
impl ToSingleRedisArg for $t {}
};
}
#[cfg(feature = "rust_decimal")]
bignum_to_redis_impl!(rust_decimal::Decimal);
#[cfg(feature = "bigdecimal")]
bignum_to_redis_impl!(bigdecimal::BigDecimal);
#[cfg(feature = "num-bigint")]
bignum_to_redis_impl!(num_bigint::BigInt);
#[cfg(feature = "num-bigint")]
bignum_to_redis_impl!(num_bigint::BigUint);
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 ToSingleRedisArg for bool {}
impl ToRedisArgs for String {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
out.write_arg(self.as_bytes())
}
}
impl ToSingleRedisArg for String {}
impl ToRedisArgs for &str {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
out.write_arg(self.as_bytes())
}
}
impl ToSingleRedisArg for &str {}
impl<'a, T> ToRedisArgs for Cow<'a, T>
where
T: ToOwned + ?Sized,
&'a T: ToRedisArgs,
T::Owned: ToRedisArgs,
{
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
match self {
Cow::Borrowed(inner) => inner.write_redis_args(out),
Cow::Owned(inner) => inner.write_redis_args(out),
}
}
}
impl<'a, T> ToSingleRedisArg for Cow<'a, T>
where
T: ToOwned + ?Sized,
&'a T: ToSingleRedisArg,
T::Owned: ToSingleRedisArg,
{
}
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 num_of_args(&self) -> usize {
match *self {
Some(ref x) => x.num_of_args(),
None => 0,
}
}
}
macro_rules! impl_write_redis_args_for_collection {
($type:ty) => {
impl<'a, T> ToRedisArgs for $type
where
T: ToRedisArgs,
{
#[inline]
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
ToRedisArgs::write_args_from_slice(self, out)
}
fn num_of_args(&self) -> usize {
if ToRedisArgs::is_single_vec_arg(&self[..]) {
return 1;
}
if self.len() == 1 {
self[0].num_of_args()
} else {
self.len()
}
}
fn describe_numeric_behavior(&self) -> NumericBehavior {
NumericBehavior::NonNumeric
}
}
};
}
macro_rules! deref_to_write_redis_args_impl {
($type:ty) => {
impl<'a, T> ToRedisArgs for $type
where
T: ToRedisArgs,
{
#[inline]
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
(**self).write_redis_args(out)
}
fn num_of_args(&self) -> usize {
(**self).num_of_args()
}
fn describe_numeric_behavior(&self) -> NumericBehavior {
(**self).describe_numeric_behavior()
}
}
impl<'a, T> ToSingleRedisArg for $type where T: ToSingleRedisArg {}
};
}
deref_to_write_redis_args_impl! {&'a T}
deref_to_write_redis_args_impl! {&'a mut T}
deref_to_write_redis_args_impl! {Box<T>}
deref_to_write_redis_args_impl! {std::sync::Arc<T>}
deref_to_write_redis_args_impl! {std::rc::Rc<T>}
impl_write_redis_args_for_collection! {&'a [T]}
impl_write_redis_args_for_collection! {&'a mut [T]}
impl_write_redis_args_for_collection! {Box<[T]>}
impl_write_redis_args_for_collection! {std::sync::Arc<[T]>}
impl_write_redis_args_for_collection! {std::rc::Rc<[T]>}
impl_write_redis_args_for_collection! {Vec<T>}
impl ToSingleRedisArg for &[u8] {}
impl ToSingleRedisArg for &mut [u8] {}
impl ToSingleRedisArg for Vec<u8> {}
impl ToSingleRedisArg for Box<[u8]> {}
impl ToSingleRedisArg for std::rc::Rc<[u8]> {}
impl ToSingleRedisArg for std::sync::Arc<[u8]> {}
macro_rules! impl_to_redis_args_for_set {
(for <$($TypeParam:ident),+> $SetType:ty, where ($($WhereClause:tt)+) ) => {
impl< $($TypeParam),+ > ToRedisArgs for $SetType
where
$($WhereClause)+
{
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
ToRedisArgs::make_arg_iter_ref(self.iter(), out)
}
fn num_of_args(&self) -> usize {
self.len()
}
}
};
}
impl_to_redis_args_for_set!(
for <T, S> std::collections::HashSet<T, S>,
where (T: ToRedisArgs)
);
impl_to_redis_args_for_set!(
for <T> std::collections::BTreeSet<T>,
where (T: ToRedisArgs)
);
#[cfg(feature = "hashbrown")]
impl_to_redis_args_for_set!(
for <T, S> hashbrown::HashSet<T, S>,
where (T: ToRedisArgs)
);
#[cfg(feature = "ahash")]
impl_to_redis_args_for_set!(
for <T, S> ahash::AHashSet<T, S>,
where (T: ToRedisArgs)
);
macro_rules! impl_to_redis_args_for_map {
(
$(#[$meta:meta])*
for <$($TypeParam:ident),+> $MapType:ty,
where ($($WhereClause:tt)+)
) => {
$(#[$meta])*
impl< $($TypeParam),+ > ToRedisArgs for $MapType
where
$($WhereClause)+
{
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
for (key, value) in self {
assert!(key.num_of_args() <= 1 && value.num_of_args() <= 1);
key.write_redis_args(out);
value.write_redis_args(out);
}
}
fn num_of_args(&self) -> usize {
self.len()
}
}
};
}
impl_to_redis_args_for_map!(
for <K, V, S> std::collections::HashMap<K, V, S>,
where (K: ToRedisArgs, V: ToRedisArgs)
);
impl_to_redis_args_for_map!(
for <K, V> std::collections::BTreeMap<K, V>,
where (K: ToRedisArgs, V: ToRedisArgs)
);
#[cfg(feature = "hashbrown")]
impl_to_redis_args_for_map!(
for <K, V, S> hashbrown::HashMap<K, V, S>,
where (K: ToRedisArgs, V: ToRedisArgs)
);
#[cfg(feature = "ahash")]
impl_to_redis_args_for_map!(
for <K, V, S> ahash::AHashMap<K, V, S>,
where (K: ToRedisArgs, V: ToRedisArgs)
);
macro_rules! to_redis_args_for_tuple {
() => ();
($(#[$meta:meta],)*$($name:ident,)+) => (
$(#[$meta])*
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 num_of_args(&self) -> usize {
let mut n: usize = 0;
$(let $name = (); n += 1;)*
n
}
}
)
}
to_redis_args_for_tuple! { #[cfg_attr(docsrs, doc(fake_variadic))], #[doc = "This trait is implemented for tuples up to 12 items long."], T, }
to_redis_args_for_tuple! { #[doc(hidden)], T1, T2, }
to_redis_args_for_tuple! { #[doc(hidden)], T1, T2, T3, }
to_redis_args_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, }
to_redis_args_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, }
to_redis_args_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, T6, }
to_redis_args_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, T6, T7, }
to_redis_args_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, T6, T7, T8, }
to_redis_args_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, T6, T7, T8, T9, }
to_redis_args_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, }
to_redis_args_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, }
to_redis_args_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, }
impl<T: ToRedisArgs, const N: usize> ToRedisArgs for &[T; N] {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
ToRedisArgs::write_args_from_slice(self.as_slice(), out)
}
fn num_of_args(&self) -> usize {
if ToRedisArgs::is_single_vec_arg(&self[..]) {
return 1;
}
if self.len() == 1 {
self[0].num_of_args()
} else {
self.len()
}
}
}
impl<const N: usize> ToSingleRedisArg for &[u8; N] {}
fn vec_to_array<T, const N: usize>(
items: Vec<T>,
original_value: &Value,
) -> Result<[T; N], ParsingError> {
match items.try_into() {
Ok(array) => Ok(array),
Err(items) => {
let msg = format!(
"Response has wrong dimension, expected {N}, got {}",
items.len()
);
crate::errors::invalid_type_error!(original_value, msg)
}
}
}
impl<T: FromRedisValue, const N: usize> FromRedisValue for [T; N] {
fn from_redis_value_ref(value: &Value) -> Result<[T; N], ParsingError> {
match *value {
Value::BulkString(ref bytes) => match FromRedisValue::from_byte_slice(bytes) {
Some(items) => vec_to_array(items, value),
None => {
let msg = format!(
"Conversion to Array[{}; {N}] failed",
std::any::type_name::<T>()
);
crate::errors::invalid_type_error!(value, msg)
}
},
Value::Array(ref items) => {
let items = FromRedisValue::from_redis_value_refs(items)?;
vec_to_array(items, value)
}
Value::Nil => vec_to_array(vec![], value),
_ => crate::errors::invalid_type_error!(value, "Response type not array compatible"),
}
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError> {
Self::from_redis_value_ref(&v)
}
}
pub trait FromRedisValue: Sized {
fn from_redis_value_ref(v: &Value) -> Result<Self, ParsingError> {
Self::from_redis_value(v.clone())
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError>;
fn from_redis_value_refs(items: &[Value]) -> Result<Vec<Self>, ParsingError> {
items
.iter()
.map(FromRedisValue::from_redis_value_ref)
.collect()
}
fn from_redis_values(items: Vec<Value>) -> Result<Vec<Self>, ParsingError> {
items
.into_iter()
.map(FromRedisValue::from_redis_value)
.collect()
}
fn from_each_redis_values(items: Vec<Value>) -> Vec<Result<Self, ParsingError>> {
items
.into_iter()
.map(FromRedisValue::from_redis_value)
.collect()
}
fn from_byte_slice(_vec: &[u8]) -> Option<Vec<Self>> {
Self::from_redis_value(Value::BulkString(_vec.into()))
.map(|rv| vec![rv])
.ok()
}
fn from_byte_vec(_vec: Vec<u8>) -> Result<Vec<Self>, ParsingError> {
Self::from_redis_value(Value::BulkString(_vec)).map(|rv| vec![rv])
}
}
fn get_inner_value(v: &Value) -> &Value {
if let Value::Attribute {
data,
attributes: _,
} = v
{
data.as_ref()
} else {
v
}
}
fn get_owned_inner_value(v: Value) -> Value {
if let Value::Attribute {
data,
attributes: _,
} = v
{
*data
} else {
v
}
}
macro_rules! from_redis_value_for_num_internal {
($t:ty, $v:expr) => {{
let v = if let Value::Attribute {
data,
attributes: _,
} = $v
{
data
} else {
$v
};
match *v {
Value::Int(val) => Ok(val as $t),
Value::SimpleString(ref s) => match s.parse::<$t>() {
Ok(rv) => Ok(rv),
Err(_) => crate::errors::invalid_type_error!(v, "Could not convert from string."),
},
Value::BulkString(ref bytes) => match from_utf8(bytes)?.parse::<$t>() {
Ok(rv) => Ok(rv),
Err(_) => crate::errors::invalid_type_error!(v, "Could not convert from string."),
},
Value::Double(val) => Ok(val as $t),
_ => crate::errors::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_ref(v: &Value) -> Result<$t, ParsingError> {
from_redis_value_for_num_internal!($t, v)
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError> {
Self::from_redis_value_ref(&v)
}
}
};
}
impl FromRedisValue for u8 {
fn from_redis_value_ref(v: &Value) -> Result<u8, ParsingError> {
from_redis_value_for_num_internal!(u8, v)
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError> {
Self::from_redis_value_ref(&v)
}
fn from_byte_slice(vec: &[u8]) -> Option<Vec<u8>> {
Some(vec.to_vec())
}
fn from_byte_vec(vec: Vec<u8>) -> Result<Vec<u8>, ParsingError> {
Ok(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);
#[cfg(any(
feature = "rust_decimal",
feature = "bigdecimal",
feature = "num-bigint"
))]
macro_rules! from_redis_value_for_bignum_internal {
($t:ty, $v:expr) => {{
let v = $v;
match *v {
Value::Int(val) => <$t>::try_from(val).map_err(|_| {
crate::errors::invalid_type_error_inner!(v, "Could not convert from integer.")
}),
Value::SimpleString(ref s) => match s.parse::<$t>() {
Ok(rv) => Ok(rv),
Err(_) => crate::errors::invalid_type_error!(v, "Could not convert from string."),
},
Value::BulkString(ref bytes) => match from_utf8(bytes)?.parse::<$t>() {
Ok(rv) => Ok(rv),
Err(_) => crate::errors::invalid_type_error!(v, "Could not convert from string."),
},
_ => crate::errors::invalid_type_error!(v, "Response type not convertible to numeric."),
}
}};
}
#[cfg(any(
feature = "rust_decimal",
feature = "bigdecimal",
feature = "num-bigint"
))]
macro_rules! from_redis_value_for_bignum {
($t:ty) => {
impl FromRedisValue for $t {
fn from_redis_value_ref(v: &Value) -> Result<$t, ParsingError> {
from_redis_value_for_bignum_internal!($t, v)
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError> {
Self::from_redis_value_ref(&v)
}
}
};
}
#[cfg(feature = "rust_decimal")]
from_redis_value_for_bignum!(rust_decimal::Decimal);
#[cfg(feature = "bigdecimal")]
from_redis_value_for_bignum!(bigdecimal::BigDecimal);
#[cfg(feature = "num-bigint")]
from_redis_value_for_bignum!(num_bigint::BigInt);
#[cfg(feature = "num-bigint")]
from_redis_value_for_bignum!(num_bigint::BigUint);
impl FromRedisValue for bool {
fn from_redis_value_ref(v: &Value) -> Result<bool, ParsingError> {
let v = get_inner_value(v);
match *v {
Value::Nil => Ok(false),
Value::Int(val) => Ok(val != 0),
Value::SimpleString(ref s) => {
if &s[..] == "1" {
Ok(true)
} else if &s[..] == "0" {
Ok(false)
} else {
crate::errors::invalid_type_error!(v, "Response status not valid boolean");
}
}
Value::BulkString(ref bytes) => {
if bytes == b"1" {
Ok(true)
} else if bytes == b"0" {
Ok(false)
} else {
crate::errors::invalid_type_error!(v, "Response type not bool compatible.");
}
}
Value::Boolean(b) => Ok(b),
Value::Okay => Ok(true),
_ => crate::errors::invalid_type_error!(v, "Response type not bool compatible."),
}
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError> {
Self::from_redis_value_ref(&v)
}
}
impl FromRedisValue for CString {
fn from_redis_value_ref(v: &Value) -> Result<CString, ParsingError> {
let v = get_inner_value(v);
match *v {
Value::BulkString(ref bytes) => Ok(CString::new(bytes.as_slice())?),
Value::Okay => Ok(CString::new("OK")?),
Value::SimpleString(ref val) => Ok(CString::new(val.as_bytes())?),
_ => crate::errors::invalid_type_error!(v, "Response type not CString compatible."),
}
}
fn from_redis_value(v: Value) -> Result<CString, ParsingError> {
let v = get_owned_inner_value(v);
match v {
Value::BulkString(bytes) => Ok(CString::new(bytes)?),
Value::Okay => Ok(CString::new("OK")?),
Value::SimpleString(val) => Ok(CString::new(val)?),
_ => crate::errors::invalid_type_error!(v, "Response type not CString compatible."),
}
}
}
impl FromRedisValue for String {
fn from_redis_value_ref(v: &Value) -> Result<Self, ParsingError> {
let v = get_inner_value(v);
match *v {
Value::BulkString(ref bytes) => Ok(from_utf8(bytes)?.to_string()),
Value::Okay => Ok("OK".to_string()),
Value::SimpleString(ref val) => Ok(val.to_string()),
Value::VerbatimString {
format: _,
ref text,
} => Ok(text.to_string()),
Value::Double(ref val) => Ok(val.to_string()),
Value::Int(val) => Ok(val.to_string()),
_ => crate::errors::invalid_type_error!(v, "Response type not string compatible."),
}
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError> {
let v = get_owned_inner_value(v);
match v {
Value::BulkString(bytes) => Ok(Self::from_utf8(bytes)?),
Value::Okay => Ok("OK".to_string()),
Value::SimpleString(val) => Ok(val),
Value::VerbatimString { format: _, text } => Ok(text),
Value::Double(val) => Ok(val.to_string()),
Value::Int(val) => Ok(val.to_string()),
_ => crate::errors::invalid_type_error!(v, "Response type not string compatible."),
}
}
}
macro_rules! pointer_from_redis_value_impl {
(
$(#[$attr:meta])*
$id:ident, $ty:ty, $func:expr
) => {
$(#[$attr])*
impl<$id: FromRedisValue> FromRedisValue for $ty {
fn from_redis_value_ref(v: &Value) -> Result<Self, ParsingError>
{
FromRedisValue::from_redis_value_ref(v).map($func)
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError>{
FromRedisValue::from_redis_value(v).map($func)
}
}
}
}
pointer_from_redis_value_impl!(T, Box<T>, Box::new);
pointer_from_redis_value_impl!(T, std::sync::Arc<T>, std::sync::Arc::new);
pointer_from_redis_value_impl!(T, std::rc::Rc<T>, std::rc::Rc::new);
macro_rules! from_vec_from_redis_value {
(<$T:ident> $Type:ty) => {
from_vec_from_redis_value!(<$T> $Type; Into::into);
};
(<$T:ident> $Type:ty; $convert:expr) => {
impl<$T: FromRedisValue> FromRedisValue for $Type {
fn from_redis_value_ref(v: &Value) -> Result<$Type, ParsingError> {
match v {
Value::BulkString(bytes) => match FromRedisValue::from_byte_slice(bytes) {
Some(x) => Ok($convert(x)),
None => crate::errors::invalid_type_error!(
v,
format!("Conversion to {} failed.", std::any::type_name::<$Type>())
),
},
Value::Array(items) => FromRedisValue::from_redis_value_refs(items).map($convert),
Value::Set(items) => FromRedisValue::from_redis_value_refs(items).map($convert),
Value::Map(items) => {
let mut n: Vec<T> = vec![];
for item in items {
match FromRedisValue::from_redis_value_ref(&Value::Map(vec![item.clone()])) {
Ok(v) => {
n.push(v);
}
Err(e) => {
return Err(e);
}
}
}
Ok($convert(n))
}
Value::Nil => Ok($convert(Vec::new())),
_ => crate::errors::invalid_type_error!(v, "Response type not vector compatible."),
}
}
fn from_redis_value(v: Value) -> Result<$Type, ParsingError> {
match v {
Value::BulkString(bytes) => FromRedisValue::from_byte_vec(bytes).map($convert),
Value::Array(items) => FromRedisValue::from_redis_values(items).map($convert),
Value::Set(items) => FromRedisValue::from_redis_values(items).map($convert),
Value::Map(items) => {
let mut n: Vec<T> = vec![];
for item in items {
match FromRedisValue::from_redis_value(Value::Map(vec![item])) {
Ok(v) => {
n.push(v);
}
Err(e) => {
return Err(e);
}
}
}
Ok($convert(n))
}
Value::Nil => Ok($convert(Vec::new())),
_ => crate::errors::invalid_type_error!(v, "Response type not vector compatible."),
}
}
}
};
}
from_vec_from_redis_value!(<T> Vec<T>);
from_vec_from_redis_value!(<T> std::sync::Arc<[T]>);
from_vec_from_redis_value!(<T> Box<[T]>; Vec::into_boxed_slice);
macro_rules! impl_from_redis_value_for_map {
(for <$($TypeParam:ident),+> $MapType:ty, where ($($WhereClause:tt)+)) => {
impl< $($TypeParam),+ > FromRedisValue for $MapType
where
$($WhereClause)+
{
fn from_redis_value_ref(v: &Value) -> Result<$MapType, ParsingError> {
let v = get_inner_value(v);
match *v {
Value::Nil => Ok(Default::default()),
_ => v
.as_map_iter()
.ok_or_else(|| crate::errors::invalid_type_error_inner!(v, "Response type not map compatible"))?
.map(|(k, v)| {
Ok((from_redis_value_ref(k)?, from_redis_value_ref(v)?))
})
.collect(),
}
}
fn from_redis_value(v: Value) -> Result<$MapType, ParsingError> {
let v = get_owned_inner_value(v);
match v {
Value::Nil => Ok(Default::default()),
_ => v
.into_map_iter()
.map_err(|v| crate::errors::invalid_type_error_inner!(v, "Response type not map compatible"))?
.map(|(k, v)| {
Ok((from_redis_value(k)?, from_redis_value(v)?))
})
.collect(),
}
}
}
};
}
impl_from_redis_value_for_map!(
for <K, V, S> std::collections::HashMap<K, V, S>,
where (K: FromRedisValue + Eq + Hash, V: FromRedisValue, S: BuildHasher + Default)
);
impl_from_redis_value_for_map!(
for <K, V> std::collections::BTreeMap<K, V>,
where (K: FromRedisValue + Eq + Ord, V: FromRedisValue)
);
#[cfg(feature = "hashbrown")]
impl_from_redis_value_for_map!(
for <K, V, S> hashbrown::HashMap<K, V, S>,
where (K: FromRedisValue + Eq + Hash, V: FromRedisValue, S: BuildHasher + Default)
);
#[cfg(feature = "ahash")]
impl_from_redis_value_for_map!(
for <K, V> ahash::AHashMap<K, V>,
where (K: FromRedisValue + Eq + Hash, V: FromRedisValue)
);
macro_rules! impl_from_redis_value_for_set {
(for <$($TypeParam:ident),+> $SetType:ty, where ($($WhereClause:tt)+)) => {
impl< $($TypeParam),+ > FromRedisValue for $SetType
where
$($WhereClause)+
{
fn from_redis_value_ref(v: &Value) -> Result<$SetType, ParsingError> {
let v = get_inner_value(v);
let items = v
.as_sequence()
.ok_or_else(|| crate::errors::invalid_type_error_inner!(v, "Response type not map compatible"))?;
items.iter().map(|item| from_redis_value_ref(item)).collect()
}
fn from_redis_value(v: Value) -> Result<$SetType, ParsingError> {
let v = get_owned_inner_value(v);
let items = v
.into_sequence()
.map_err(|v| crate::errors::invalid_type_error_inner!(v, "Response type not map compatible"))?;
items
.into_iter()
.map(|item| from_redis_value(item))
.collect()
}
}
};
}
impl_from_redis_value_for_set!(
for <T, S> std::collections::HashSet<T, S>,
where (T: FromRedisValue + Eq + Hash, S: BuildHasher + Default)
);
impl_from_redis_value_for_set!(
for <T> std::collections::BTreeSet<T>,
where (T: FromRedisValue + Ord)
);
#[cfg(feature = "hashbrown")]
impl_from_redis_value_for_set!(
for <T, S> hashbrown::HashSet<T, S>,
where (T: FromRedisValue + Eq + Hash, S: BuildHasher + Default)
);
#[cfg(feature = "ahash")]
impl_from_redis_value_for_set!(
for <T> ahash::AHashSet<T>,
where (T: FromRedisValue + Eq + Hash)
);
impl FromRedisValue for Value {
fn from_redis_value_ref(v: &Value) -> Result<Value, ParsingError> {
Ok(v.clone())
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError> {
Ok(v)
}
}
impl FromRedisValue for () {
fn from_redis_value_ref(v: &Value) -> Result<(), ParsingError> {
match v {
Value::ServerError(err) => Err(ParsingError::from(err.to_string())),
_ => Ok(()),
}
}
fn from_redis_value(v: Value) -> Result<(), ParsingError> {
Self::from_redis_value_ref(&v)
}
}
macro_rules! from_redis_value_for_tuple {
() => ();
($(#[$meta:meta],)*$($name:ident,)+) => (
$(#[$meta])*
impl<$($name: FromRedisValue),*> FromRedisValue for ($($name,)*) {
#[allow(non_snake_case, unused_variables)]
fn from_redis_value_ref(v: &Value) -> Result<($($name,)*), ParsingError> {
let v = get_inner_value(v);
let mut n = 0;
$(let $name = (); n += 1;)*
match *v {
Value::Array(ref items) => {
if items.len() != n {
crate::errors::invalid_type_error!(v, "Array response of wrong dimension")
}
let mut i = 0;
Ok(($({let $name = (); from_redis_value_ref(
&items[{ i += 1; i - 1 }])?},)*))
}
Value::Set(ref items) => {
if items.len() != n {
crate::errors::invalid_type_error!(v, "Set response of wrong dimension")
}
let mut i = 0;
Ok(($({let $name = (); from_redis_value_ref(
&items[{ i += 1; i - 1 }])?},)*))
}
Value::Map(ref items) => {
if n != items.len() * 2 {
crate::errors::invalid_type_error!(v, "Map response of wrong dimension")
}
let mut flatten_items = items.iter().map(|(a,b)|[a,b]).flatten();
Ok(($({let $name = (); from_redis_value_ref(
&flatten_items.next().unwrap())?},)*))
}
_ => crate::errors::invalid_type_error!(v, "Not a Array response")
}
}
#[allow(non_snake_case, unused_variables)]
fn from_redis_value(v: Value) -> Result<($($name,)*), ParsingError> {
let v = get_owned_inner_value(v);
let mut n = 0;
$(let $name = (); n += 1;)*
match v {
Value::Array(mut items) => {
if items.len() != n {
crate::errors::invalid_type_error!(Value::Array(items), "Array response of wrong dimension")
}
let mut i = 0;
Ok(($({let $name = (); from_redis_value(
::std::mem::replace(&mut items[{ i += 1; i - 1 }], Value::Nil)
)?},)*))
}
Value::Set(mut items) => {
if items.len() != n {
crate::errors::invalid_type_error!(Value::Array(items), "Set response of wrong dimension")
}
let mut i = 0;
Ok(($({let $name = (); from_redis_value(
::std::mem::replace(&mut items[{ i += 1; i - 1 }], Value::Nil)
)?},)*))
}
Value::Map(items) => {
if n != items.len() * 2 {
crate::errors::invalid_type_error!(Value::Map(items), "Map response of wrong dimension")
}
let mut flatten_items = items.into_iter().map(|(a,b)|[a,b]).flatten();
Ok(($({let $name = (); from_redis_value(
::std::mem::replace(&mut flatten_items.next().unwrap(), Value::Nil)
)?},)*))
}
_ => crate::errors::invalid_type_error!(v, "Not a Array response")
}
}
#[allow(non_snake_case, unused_variables)]
fn from_redis_value_refs(items: &[Value]) -> Result<Vec<($($name,)*)>, ParsingError> {
let mut n = 0;
$(let $name = (); n += 1;)*
if items.len() == 0 {
return Ok(vec![]);
}
if items.iter().all(|item| item.is_collection_of_len(n)) {
return items.iter().map(|item| from_redis_value_ref(item)).collect();
}
let mut rv = Vec::with_capacity(items.len() / n);
if let [$($name),*] = items {
rv.push(($(from_redis_value_ref($name)?,)*));
return Ok(rv);
}
for chunk in items.chunks(n) {
match chunk {
[$($name),*] => rv.push(($(from_redis_value_ref($name)?,)*)),
_ => return Err(format!("Vector of length {} doesn't have arity of {n}", items.len()).into()),
}
}
Ok(rv)
}
#[allow(non_snake_case, unused_variables)]
fn from_each_redis_values(mut items: Vec<Value>) -> Vec<Result<($($name,)*), ParsingError>> {
#[allow(unused_parens)]
let extract = |val: ($(Result<$name, ParsingError>,)*)| -> Result<($($name,)*), ParsingError> {
let ($($name,)*) = val;
Ok(($($name?,)*))
};
let mut n = 0;
$(let $name = (); n += 1;)*
if items.len() == 0 {
return vec![];
}
if items.iter().all(|item| item.is_collection_of_len(n)) {
return items.into_iter().map(|item| from_redis_value(item).map_err(|err|err.into())).collect();
}
let mut rv = Vec::with_capacity(items.len() / n);
for chunk in items.chunks_mut(n) {
match chunk {
[$($name),*] => rv.push(extract(($(from_redis_value(std::mem::replace($name, Value::Nil)).into(),)*))),
_ => return vec![Err(format!("Vector of length {} doesn't have arity of {n}", items.len()).into())],
}
}
rv
}
#[allow(non_snake_case, unused_variables)]
fn from_redis_values(mut items: Vec<Value>) -> Result<Vec<($($name,)*)>, ParsingError> {
let mut n = 0;
$(let $name = (); n += 1;)*
if items.len() == 0 {
return Ok(vec![])
}
if items.iter().all(|item| item.is_collection_of_len(n)) {
return items.into_iter().map(|item| from_redis_value(item)).collect();
}
let mut rv = Vec::with_capacity(items.len() / n);
for chunk in items.chunks_mut(n) {
match chunk {
[$($name),*] => rv.push(($(from_redis_value(std::mem::replace($name, Value::Nil))?,)*)),
_ => return Err(format!("Vector of length {} doesn't have arity of {n}", items.len()).into()),
}
}
Ok(rv)
}
}
)
}
from_redis_value_for_tuple! { #[cfg_attr(docsrs, doc(fake_variadic))], #[doc = "This trait is implemented for tuples up to 12 items long."], T, }
from_redis_value_for_tuple! { #[doc(hidden)], T1, T2, }
from_redis_value_for_tuple! { #[doc(hidden)], T1, T2, T3, }
from_redis_value_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, }
from_redis_value_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, }
from_redis_value_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, T6, }
from_redis_value_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, T6, T7, }
from_redis_value_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, T6, T7, T8, }
from_redis_value_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, T6, T7, T8, T9, }
from_redis_value_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, }
from_redis_value_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, }
from_redis_value_for_tuple! { #[doc(hidden)], T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, }
impl FromRedisValue for InfoDict {
fn from_redis_value_ref(v: &Value) -> Result<InfoDict, ParsingError> {
let v = get_inner_value(v);
let s: String = from_redis_value_ref(v)?;
Ok(InfoDict::new(&s))
}
fn from_redis_value(v: Value) -> Result<InfoDict, ParsingError> {
let v = get_owned_inner_value(v);
let s: String = from_redis_value(v)?;
Ok(InfoDict::new(&s))
}
}
impl<T: FromRedisValue> FromRedisValue for Option<T> {
fn from_redis_value_ref(v: &Value) -> Result<Option<T>, ParsingError> {
let v = get_inner_value(v);
if *v == Value::Nil {
return Ok(None);
}
Ok(Some(from_redis_value_ref(v)?))
}
fn from_redis_value(v: Value) -> Result<Option<T>, ParsingError> {
let v = get_owned_inner_value(v);
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_ref(v: &Value) -> Result<Self, ParsingError> {
let v = get_inner_value(v);
match v {
Value::BulkString(bytes_vec) => Ok(bytes::Bytes::copy_from_slice(bytes_vec.as_ref())),
_ => crate::errors::invalid_type_error!(v, "Not a bulk string"),
}
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError> {
let v = get_owned_inner_value(v);
match v {
Value::BulkString(bytes_vec) => Ok(bytes_vec.into()),
_ => crate::errors::invalid_type_error!(v, "Not a bulk string"),
}
}
}
#[cfg(feature = "uuid")]
impl FromRedisValue for uuid::Uuid {
fn from_redis_value_ref(v: &Value) -> Result<Self, ParsingError> {
match *v {
Value::BulkString(ref bytes) => Ok(uuid::Uuid::from_slice(bytes)?),
_ => crate::errors::invalid_type_error!(v, "Response type not uuid compatible."),
}
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError> {
Self::from_redis_value_ref(&v)
}
}
#[cfg(feature = "uuid")]
impl ToRedisArgs for uuid::Uuid {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
out.write_arg(self.as_bytes());
}
}
#[cfg(feature = "uuid")]
impl ToSingleRedisArg for uuid::Uuid {}
pub fn from_redis_value_ref<T: FromRedisValue>(v: &Value) -> Result<T, ParsingError> {
FromRedisValue::from_redis_value_ref(v)
}
pub fn from_redis_value<T: FromRedisValue>(v: Value) -> Result<T, ParsingError> {
FromRedisValue::from_redis_value(v)
}
pub fn calculate_value_digest<T: ToRedisArgs>(value: T) -> String {
use xxhash_rust::xxh3::xxh3_64;
let args = value.to_redis_args();
let mut combined_bytes = Vec::new();
for arg in args {
combined_bytes.extend_from_slice(&arg);
}
let hash = xxh3_64(&combined_bytes);
format!("{:016x}", hash)
}
pub fn is_valid_16_bytes_hex_digest(s: &str) -> bool {
s.len() == 16 && s.chars().all(|c| c.is_ascii_hexdigit())
}
#[derive(Clone, Eq, PartialEq, Default, Debug, Copy)]
#[non_exhaustive]
pub enum ProtocolVersion {
#[default]
RESP2,
RESP3,
}
impl ProtocolVersion {
pub fn supports_resp3(&self) -> bool {
!matches!(self, ProtocolVersion::RESP2)
}
}
#[derive(Clone, Copy)]
#[non_exhaustive]
pub enum ExpireOption {
NONE,
NX,
XX,
GT,
LT,
}
impl ToRedisArgs for ExpireOption {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
match self {
ExpireOption::NX => out.write_arg(b"NX"),
ExpireOption::XX => out.write_arg(b"XX"),
ExpireOption::GT => out.write_arg(b"GT"),
ExpireOption::LT => out.write_arg(b"LT"),
_ => {}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PushInfo {
pub kind: PushKind,
pub data: Vec<Value>,
}
impl PushInfo {
pub(crate) fn disconnect() -> Self {
PushInfo {
kind: crate::PushKind::Disconnection,
data: vec![],
}
}
}
pub(crate) type SyncPushSender = std::sync::mpsc::Sender<PushInfo>;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum ValueType {
String,
List,
Set,
ZSet,
Hash,
Stream,
JSON,
Unknown(String),
}
impl<T: AsRef<str>> From<T> for ValueType {
fn from(s: T) -> Self {
match s.as_ref() {
"string" => ValueType::String,
"list" => ValueType::List,
"set" => ValueType::Set,
"zset" => ValueType::ZSet,
"hash" => ValueType::Hash,
"stream" => ValueType::Stream,
"ReJSON-RL" => ValueType::JSON,
s => ValueType::Unknown(s.to_string()),
}
}
}
impl From<ValueType> for String {
fn from(v: ValueType) -> Self {
match v {
ValueType::String => "string".to_string(),
ValueType::List => "list".to_string(),
ValueType::Set => "set".to_string(),
ValueType::ZSet => "zset".to_string(),
ValueType::Hash => "hash".to_string(),
ValueType::Stream => "stream".to_string(),
ValueType::JSON => "ReJSON-RL".to_string(),
ValueType::Unknown(s) => s,
}
}
}
impl FromRedisValue for ValueType {
fn from_redis_value_ref(v: &Value) -> Result<Self, ParsingError> {
match v {
Value::SimpleString(s) => Ok(s.into()),
_ => crate::errors::invalid_type_error!(v, "Value type should be a simple string"),
}
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError> {
match v {
Value::SimpleString(s) => Ok(s.into()),
_ => crate::errors::invalid_type_error!(v, "Value type should be a simple string"),
}
}
}
#[derive(Debug, PartialEq, Clone)]
#[non_exhaustive]
pub enum IntegerReplyOrNoOp {
IntegerReply(usize),
NotExists,
ExistsButNotRelevant,
}
impl IntegerReplyOrNoOp {
pub fn raw(&self) -> isize {
match self {
IntegerReplyOrNoOp::IntegerReply(s) => *s as isize,
IntegerReplyOrNoOp::NotExists => -2,
IntegerReplyOrNoOp::ExistsButNotRelevant => -1,
}
}
}
impl FromRedisValue for IntegerReplyOrNoOp {
fn from_redis_value_ref(v: &Value) -> Result<Self, ParsingError> {
match v {
Value::Int(s) => match s {
-2 => Ok(IntegerReplyOrNoOp::NotExists),
-1 => Ok(IntegerReplyOrNoOp::ExistsButNotRelevant),
_ => Ok(IntegerReplyOrNoOp::IntegerReply(*s as usize)),
},
_ => crate::errors::invalid_type_error!(v, "Value should be an integer"),
}
}
fn from_redis_value(v: Value) -> Result<Self, ParsingError> {
match v {
Value::Int(s) => match s {
-2 => Ok(IntegerReplyOrNoOp::NotExists),
-1 => Ok(IntegerReplyOrNoOp::ExistsButNotRelevant),
_ => Ok(IntegerReplyOrNoOp::IntegerReply(s as usize)),
},
_ => crate::errors::invalid_type_error!(v, "Value should be an integer"),
}
}
}
impl PartialEq<isize> for IntegerReplyOrNoOp {
fn eq(&self, other: &isize) -> bool {
match self {
IntegerReplyOrNoOp::IntegerReply(s) => *s as isize == *other,
IntegerReplyOrNoOp::NotExists => *other == -2,
IntegerReplyOrNoOp::ExistsButNotRelevant => *other == -1,
}
}
}
impl PartialEq<usize> for IntegerReplyOrNoOp {
fn eq(&self, other: &usize) -> bool {
match self {
IntegerReplyOrNoOp::IntegerReply(s) => *s == *other,
_ => false,
}
}
}
impl PartialEq<i32> for IntegerReplyOrNoOp {
fn eq(&self, other: &i32) -> bool {
match self {
IntegerReplyOrNoOp::IntegerReply(s) => *s as i32 == *other,
IntegerReplyOrNoOp::NotExists => *other == -2,
IntegerReplyOrNoOp::ExistsButNotRelevant => *other == -1,
}
}
}
impl PartialEq<u32> for IntegerReplyOrNoOp {
fn eq(&self, other: &u32) -> bool {
match self {
IntegerReplyOrNoOp::IntegerReply(s) => *s as u32 == *other,
_ => false,
}
}
}