mco_redis_rs/
types.rs

1use std::collections::{BTreeMap, BTreeSet};
2use std::collections::{HashMap, HashSet};
3use std::convert::From;
4use std::default::Default;
5use std::error;
6use std::fmt;
7use std::hash::{BuildHasher, Hash};
8use std::io;
9use std::str::{from_utf8, Utf8Error};
10use std::string::FromUtf8Error;
11
12macro_rules! invalid_type_error {
13    ($v:expr, $det:expr) => {{
14        fail!(invalid_type_error_inner!($v, $det))
15    }};
16}
17
18macro_rules! invalid_type_error_inner {
19    ($v:expr, $det:expr) => {
20        RedisError::from((
21            ErrorKind::TypeError,
22            "Response was of incompatible type",
23            format!("{:?} (response was {:?})", $det, $v),
24        ))
25    };
26}
27
28/// Helper enum that is used in some situations to describe
29/// the behavior of arguments in a numeric context.
30#[derive(PartialEq, Eq, Clone, Debug, Copy)]
31pub enum NumericBehavior {
32    /// This argument is not numeric.
33    NonNumeric,
34    /// This argument is an integer.
35    NumberIsInteger,
36    /// This argument is a floating point value.
37    NumberIsFloat,
38}
39
40/// An enum of all error kinds.
41#[derive(PartialEq, Eq, Copy, Clone, Debug)]
42#[non_exhaustive]
43pub enum ErrorKind {
44    /// The server generated an invalid response.
45    ResponseError,
46    /// The authentication with the server failed.
47    AuthenticationFailed,
48    /// Operation failed because of a type mismatch.
49    TypeError,
50    /// A script execution was aborted.
51    ExecAbortError,
52    /// The server cannot response because it's loading a dump.
53    BusyLoadingError,
54    /// A script that was requested does not actually exist.
55    NoScriptError,
56    /// An error that was caused because the parameter to the
57    /// client were wrong.
58    InvalidClientConfig,
59    /// Raised if a key moved to a different node.
60    Moved,
61    /// Raised if a key moved to a different node but we need to ask.
62    Ask,
63    /// Raised if a request needs to be retried.
64    TryAgain,
65    /// Raised if a redis cluster is down.
66    ClusterDown,
67    /// A request spans multiple slots
68    CrossSlot,
69    /// A cluster master is unavailable.
70    MasterDown,
71    /// This kind is returned if the redis error is one that is
72    /// not native to the system.  This is usually the case if
73    /// the cause is another error.
74    IoError,
75    /// An error raised that was identified on the client before execution.
76    ClientError,
77    /// An extension error.  This is an error created by the server
78    /// that is not directly understood by the library.
79    ExtensionError,
80    /// Attempt to write to a read-only server
81    ReadOnly,
82}
83
84/// Internal low-level redis value enum.
85#[derive(PartialEq, Eq, Clone)]
86pub enum Value {
87    /// A nil response from the server.
88    Nil,
89    /// An integer response.  Note that there are a few situations
90    /// in which redis actually returns a string for an integer which
91    /// is why this library generally treats integers and strings
92    /// the same for all numeric responses.
93    Int(i64),
94    /// An arbitary binary data.
95    Data(Vec<u8>),
96    /// A bulk response of more data.  This is generally used by redis
97    /// to express nested structures.
98    Bulk(Vec<Value>),
99    /// A status response.
100    Status(String),
101    /// A status response which represents the string "OK".
102    Okay,
103}
104
105pub struct MapIter<'a>(std::slice::Iter<'a, Value>);
106
107impl<'a> Iterator for MapIter<'a> {
108    type Item = (&'a Value, &'a Value);
109
110    fn next(&mut self) -> Option<Self::Item> {
111        Some((self.0.next()?, self.0.next()?))
112    }
113
114    fn size_hint(&self) -> (usize, Option<usize>) {
115        let (low, high) = self.0.size_hint();
116        (low / 2, high.map(|h| h / 2))
117    }
118}
119
120/// Values are generally not used directly unless you are using the
121/// more low level functionality in the library.  For the most part
122/// this is hidden with the help of the `FromRedisValue` trait.
123///
124/// While on the redis protocol there is an error type this is already
125/// separated at an early point so the value only holds the remaining
126/// types.
127impl Value {
128    /// Checks if the return value looks like it fulfils the cursor
129    /// protocol.  That means the result is a bulk item of length
130    /// two with the first one being a cursor and the second a
131    /// bulk response.
132    pub fn looks_like_cursor(&self) -> bool {
133        match *self {
134            Value::Bulk(ref items) => {
135                if items.len() != 2 {
136                    return false;
137                }
138                match items[0] {
139                    Value::Data(_) => {}
140                    _ => {
141                        return false;
142                    }
143                };
144                match items[1] {
145                    Value::Bulk(_) => {}
146                    _ => {
147                        return false;
148                    }
149                }
150                true
151            }
152            _ => false,
153        }
154    }
155
156    /// Returns an `&[Value]` if `self` is compatible with a sequence type
157    pub fn as_sequence(&self) -> Option<&[Value]> {
158        match self {
159            Value::Bulk(items) => Some(&items[..]),
160            Value::Nil => Some(&[]),
161            _ => None,
162        }
163    }
164
165    /// Returns an iterator of `(&Value, &Value)` if `self` is compatible with a map type
166    pub fn as_map_iter(&self) -> Option<MapIter<'_>> {
167        match self {
168            Value::Bulk(items) => Some(MapIter(items.iter())),
169            _ => None,
170        }
171    }
172}
173
174impl fmt::Debug for Value {
175    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
176        match *self {
177            Value::Nil => write!(fmt, "nil"),
178            Value::Int(val) => write!(fmt, "int({:?})", val),
179            Value::Data(ref val) => match from_utf8(val) {
180                Ok(x) => write!(fmt, "string-data('{:?}')", x),
181                Err(_) => write!(fmt, "binary-data({:?})", val),
182            },
183            Value::Bulk(ref values) => {
184                write!(fmt, "bulk(")?;
185                let mut is_first = true;
186                for val in values.iter() {
187                    if !is_first {
188                        write!(fmt, ", ")?;
189                    }
190                    write!(fmt, "{:?}", val)?;
191                    is_first = false;
192                }
193                write!(fmt, ")")
194            }
195            Value::Okay => write!(fmt, "ok"),
196            Value::Status(ref s) => write!(fmt, "status({:?})", s),
197        }
198    }
199}
200
201/// Represents a redis error.  For the most part you should be using
202/// the Error trait to interact with this rather than the actual
203/// struct.
204pub struct RedisError {
205    repr: ErrorRepr,
206}
207
208#[derive(Debug)]
209enum ErrorRepr {
210    WithDescription(ErrorKind, &'static str),
211    WithDescriptionAndDetail(ErrorKind, &'static str, String),
212    ExtensionError(String, String),
213    IoError(io::Error),
214}
215
216impl PartialEq for RedisError {
217    fn eq(&self, other: &RedisError) -> bool {
218        match (&self.repr, &other.repr) {
219            (&ErrorRepr::WithDescription(kind_a, _), &ErrorRepr::WithDescription(kind_b, _)) => {
220                kind_a == kind_b
221            }
222            (
223                &ErrorRepr::WithDescriptionAndDetail(kind_a, _, _),
224                &ErrorRepr::WithDescriptionAndDetail(kind_b, _, _),
225            ) => kind_a == kind_b,
226            (&ErrorRepr::ExtensionError(ref a, _), &ErrorRepr::ExtensionError(ref b, _)) => {
227                *a == *b
228            }
229            _ => false,
230        }
231    }
232}
233
234impl From<io::Error> for RedisError {
235    fn from(err: io::Error) -> RedisError {
236        RedisError {
237            repr: ErrorRepr::IoError(err),
238        }
239    }
240}
241
242impl From<Utf8Error> for RedisError {
243    fn from(_: Utf8Error) -> RedisError {
244        RedisError {
245            repr: ErrorRepr::WithDescription(ErrorKind::TypeError, "Invalid UTF-8"),
246        }
247    }
248}
249
250#[cfg(feature = "tls")]
251impl From<native_tls::Error> for RedisError {
252    fn from(err: native_tls::Error) -> RedisError {
253        RedisError {
254            repr: ErrorRepr::WithDescriptionAndDetail(
255                ErrorKind::IoError,
256                "TLS error",
257                err.to_string(),
258            ),
259        }
260    }
261}
262
263impl From<FromUtf8Error> for RedisError {
264    fn from(_: FromUtf8Error) -> RedisError {
265        RedisError {
266            repr: ErrorRepr::WithDescription(ErrorKind::TypeError, "Cannot convert from UTF-8"),
267        }
268    }
269}
270
271impl From<(ErrorKind, &'static str)> for RedisError {
272    fn from((kind, desc): (ErrorKind, &'static str)) -> RedisError {
273        RedisError {
274            repr: ErrorRepr::WithDescription(kind, desc),
275        }
276    }
277}
278
279impl From<(ErrorKind, &'static str, String)> for RedisError {
280    fn from((kind, desc, detail): (ErrorKind, &'static str, String)) -> RedisError {
281        RedisError {
282            repr: ErrorRepr::WithDescriptionAndDetail(kind, desc, detail),
283        }
284    }
285}
286
287impl error::Error for RedisError {
288    #[allow(deprecated)]
289    fn description(&self) -> &str {
290        match self.repr {
291            ErrorRepr::WithDescription(_, desc) => desc,
292            ErrorRepr::WithDescriptionAndDetail(_, desc, _) => desc,
293            ErrorRepr::ExtensionError(_, _) => "extension error",
294            ErrorRepr::IoError(ref err) => err.description(),
295        }
296    }
297
298    fn cause(&self) -> Option<&dyn error::Error> {
299        match self.repr {
300            ErrorRepr::IoError(ref err) => Some(err as &dyn error::Error),
301            _ => None,
302        }
303    }
304}
305
306impl fmt::Display for RedisError {
307    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
308        match self.repr {
309            ErrorRepr::WithDescription(_, desc) => desc.fmt(f),
310            ErrorRepr::WithDescriptionAndDetail(_, desc, ref detail) => {
311                desc.fmt(f)?;
312                f.write_str(": ")?;
313                detail.fmt(f)
314            }
315            ErrorRepr::ExtensionError(ref code, ref detail) => {
316                code.fmt(f)?;
317                f.write_str(": ")?;
318                detail.fmt(f)
319            }
320            ErrorRepr::IoError(ref err) => err.fmt(f),
321        }
322    }
323}
324
325impl fmt::Debug for RedisError {
326    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
327        fmt::Display::fmt(self, f)
328    }
329}
330
331/// Indicates a general failure in the library.
332impl RedisError {
333    /// Returns the kind of the error.
334    pub fn kind(&self) -> ErrorKind {
335        match self.repr {
336            ErrorRepr::WithDescription(kind, _)
337            | ErrorRepr::WithDescriptionAndDetail(kind, _, _) => kind,
338            ErrorRepr::ExtensionError(_, _) => ErrorKind::ExtensionError,
339            ErrorRepr::IoError(_) => ErrorKind::IoError,
340        }
341    }
342
343    /// Returns the error detail.
344    pub fn detail(&self) -> Option<&str> {
345        match self.repr {
346            ErrorRepr::WithDescriptionAndDetail(_, _, ref detail)
347            | ErrorRepr::ExtensionError(_, ref detail) => Some(detail.as_str()),
348            _ => None,
349        }
350    }
351
352    /// Returns the raw error code if available.
353    pub fn code(&self) -> Option<&str> {
354        match self.kind() {
355            ErrorKind::ResponseError => Some("ERR"),
356            ErrorKind::ExecAbortError => Some("EXECABORT"),
357            ErrorKind::BusyLoadingError => Some("LOADING"),
358            ErrorKind::NoScriptError => Some("NOSCRIPT"),
359            ErrorKind::Moved => Some("MOVED"),
360            ErrorKind::Ask => Some("ASK"),
361            ErrorKind::TryAgain => Some("TRYAGAIN"),
362            ErrorKind::ClusterDown => Some("CLUSTERDOWN"),
363            ErrorKind::CrossSlot => Some("CROSSSLOT"),
364            ErrorKind::MasterDown => Some("MASTERDOWN"),
365            ErrorKind::ReadOnly => Some("READONLY"),
366            _ => match self.repr {
367                ErrorRepr::ExtensionError(ref code, _) => Some(&code),
368                _ => None,
369            },
370        }
371    }
372
373    /// Returns the name of the error category for display purposes.
374    pub fn category(&self) -> &str {
375        match self.kind() {
376            ErrorKind::ResponseError => "response error",
377            ErrorKind::AuthenticationFailed => "authentication failed",
378            ErrorKind::TypeError => "type error",
379            ErrorKind::ExecAbortError => "script execution aborted",
380            ErrorKind::BusyLoadingError => "busy loading",
381            ErrorKind::NoScriptError => "no script",
382            ErrorKind::InvalidClientConfig => "invalid client config",
383            ErrorKind::Moved => "key moved",
384            ErrorKind::Ask => "key moved (ask)",
385            ErrorKind::TryAgain => "try again",
386            ErrorKind::ClusterDown => "cluster down",
387            ErrorKind::CrossSlot => "cross-slot",
388            ErrorKind::MasterDown => "master down",
389            ErrorKind::IoError => "I/O error",
390            ErrorKind::ExtensionError => "extension error",
391            ErrorKind::ClientError => "client error",
392            ErrorKind::ReadOnly => "read-only",
393        }
394    }
395
396    /// Indicates that this failure is an IO failure.
397    pub fn is_io_error(&self) -> bool {
398        self.as_io_error().is_some()
399    }
400
401    pub(crate) fn as_io_error(&self) -> Option<&io::Error> {
402        match &self.repr {
403            ErrorRepr::IoError(e) => Some(e),
404            _ => None,
405        }
406    }
407
408    /// Indicates that this is a cluster error.
409    pub fn is_cluster_error(&self) -> bool {
410        matches!(
411            self.kind(),
412            ErrorKind::Moved | ErrorKind::Ask | ErrorKind::TryAgain | ErrorKind::ClusterDown
413        )
414    }
415
416    /// Returns true if this error indicates that the connection was
417    /// refused.  You should generally not rely much on this function
418    /// unless you are writing unit tests that want to detect if a
419    /// local server is available.
420    pub fn is_connection_refusal(&self) -> bool {
421        match self.repr {
422            ErrorRepr::IoError(ref err) => {
423                #[allow(clippy::match_like_matches_macro)]
424                match err.kind() {
425                    io::ErrorKind::ConnectionRefused => true,
426                    // if we connect to a unix socket and the file does not
427                    // exist yet, then we want to treat this as if it was a
428                    // connection refusal.
429                    io::ErrorKind::NotFound => cfg!(unix),
430                    _ => false,
431                }
432            }
433            _ => false,
434        }
435    }
436
437    /// Returns true if error was caused by I/O time out.
438    /// Note that this may not be accurate depending on platform.
439    pub fn is_timeout(&self) -> bool {
440        match self.repr {
441            ErrorRepr::IoError(ref err) => matches!(
442                err.kind(),
443                io::ErrorKind::TimedOut | io::ErrorKind::WouldBlock
444            ),
445            _ => false,
446        }
447    }
448
449    /// Returns true if error was caused by a dropped connection.
450    pub fn is_connection_dropped(&self) -> bool {
451        match self.repr {
452            ErrorRepr::IoError(ref err) => matches!(
453                err.kind(),
454                io::ErrorKind::BrokenPipe | io::ErrorKind::ConnectionReset
455            ),
456            _ => false,
457        }
458    }
459
460    /// Returns the node the error refers to.
461    ///
462    /// This returns `(addr, slot_id)`.
463    pub fn redirect_node(&self) -> Option<(&str, u16)> {
464        match self.kind() {
465            ErrorKind::Ask | ErrorKind::Moved => (),
466            _ => return None,
467        }
468        let mut iter = self.detail()?.split_ascii_whitespace();
469        let slot_id: u16 = iter.next()?.parse().ok()?;
470        let addr = iter.next()?;
471        Some((addr, slot_id))
472    }
473
474    /// Returns the extension error code.
475    ///
476    /// This method should not be used because every time the redis library
477    /// adds support for a new error code it would disappear form this method.
478    /// `code()` always returns the code.
479    #[deprecated(note = "use code() instead")]
480    pub fn extension_error_code(&self) -> Option<&str> {
481        match self.repr {
482            ErrorRepr::ExtensionError(ref code, _) => Some(&code),
483            _ => None,
484        }
485    }
486
487    /// Clone the `RedisError`, throwing away non-cloneable parts of an `IoError`.
488    ///
489    /// Deriving `Clone` is not possible because the wrapped `io::Error` is not
490    /// cloneable.
491    ///
492    /// The `ioerror_description` parameter will be prepended to the message in
493    /// case an `IoError` is found.
494    #[cfg(feature = "connection-manager")] // Used to avoid "unused method" warning
495    pub(crate) fn clone_mostly(&self, ioerror_description: &'static str) -> Self {
496        let repr = match self.repr {
497            ErrorRepr::WithDescription(kind, desc) => ErrorRepr::WithDescription(kind, desc),
498            ErrorRepr::WithDescriptionAndDetail(kind, desc, ref detail) => {
499                ErrorRepr::WithDescriptionAndDetail(kind, desc, detail.clone())
500            }
501            ErrorRepr::ExtensionError(ref code, ref detail) => {
502                ErrorRepr::ExtensionError(code.clone(), detail.clone())
503            }
504            ErrorRepr::IoError(ref e) => ErrorRepr::IoError(io::Error::new(
505                e.kind(),
506                format!("{}: {}", ioerror_description, e),
507            )),
508        };
509        Self { repr }
510    }
511}
512
513pub fn make_extension_error(code: &str, detail: Option<&str>) -> RedisError {
514    RedisError {
515        repr: ErrorRepr::ExtensionError(
516            code.to_string(),
517            match detail {
518                Some(x) => x.to_string(),
519                None => "Unknown extension error encountered".to_string(),
520            },
521        ),
522    }
523}
524
525/// Library generic result type.
526pub type RedisResult<T> = Result<T, RedisError>;
527
528/// Library generic future type.
529#[cfg(feature = "aio")]
530pub type RedisFuture<'a, T> = futures_util::future::BoxFuture<'a, RedisResult<T>>;
531
532/// An info dictionary type.
533#[derive(Debug)]
534pub struct InfoDict {
535    map: HashMap<String, Value>,
536}
537
538/// This type provides convenient access to key/value data returned by
539/// the "INFO" command.  It acts like a regular mapping but also has
540/// a convenience method `get` which can return data in the appropriate
541/// type.
542///
543/// For instance this can be used to query the server for the role it's
544/// in (master, slave) etc:
545///
546/// ```rust,no_run
547/// # fn do_something() -> redis::RedisResult<()> {
548/// # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
549/// # let mut con = client.get_connection().unwrap();
550/// let info : redis::InfoDict = redis::cmd("INFO").query(&mut con)?;
551/// let role : Option<String> = info.get("role");
552/// # Ok(()) }
553/// ```
554impl InfoDict {
555    /// Creates a new info dictionary from a string in the response of
556    /// the INFO command.  Each line is a key, value pair with the
557    /// key and value separated by a colon (`:`).  Lines starting with a
558    /// hash (`#`) are ignored.
559    pub fn new(kvpairs: &str) -> InfoDict {
560        let mut map = HashMap::new();
561        for line in kvpairs.lines() {
562            if line.is_empty() || line.starts_with('#') {
563                continue;
564            }
565            let mut p = line.splitn(2, ':');
566            let k = unwrap_or!(p.next(), continue).to_string();
567            let v = unwrap_or!(p.next(), continue).to_string();
568            map.insert(k, Value::Status(v));
569        }
570        InfoDict { map }
571    }
572
573    /// Fetches a value by key and converts it into the given type.
574    /// Typical types are `String`, `bool` and integer types.
575    pub fn get<T: FromRedisValue>(&self, key: &str) -> Option<T> {
576        match self.find(&key) {
577            Some(ref x) => from_redis_value(*x).ok(),
578            None => None,
579        }
580    }
581
582    /// Looks up a key in the info dict.
583    pub fn find(&self, key: &&str) -> Option<&Value> {
584        self.map.get(*key)
585    }
586
587    /// Checks if a key is contained in the info dicf.
588    pub fn contains_key(&self, key: &&str) -> bool {
589        self.find(key).is_some()
590    }
591
592    /// Returns the size of the info dict.
593    pub fn len(&self) -> usize {
594        self.map.len()
595    }
596
597    /// Checks if the dict is empty.
598    pub fn is_empty(&self) -> bool {
599        self.map.is_empty()
600    }
601}
602
603/// Abstraction trait for redis command abstractions.
604pub trait RedisWrite {
605    /// Accepts a serialized redis command.
606    fn write_arg(&mut self, arg: &[u8]);
607
608    /// Accepts a serialized redis command.
609    fn write_arg_fmt(&mut self, arg: impl fmt::Display) {
610        self.write_arg(&arg.to_string().as_bytes())
611    }
612}
613
614impl RedisWrite for Vec<Vec<u8>> {
615    fn write_arg(&mut self, arg: &[u8]) {
616        self.push(arg.to_owned());
617    }
618
619    fn write_arg_fmt(&mut self, arg: impl fmt::Display) {
620        self.push(arg.to_string().into_bytes())
621    }
622}
623
624/// Used to convert a value into one or multiple redis argument
625/// strings.  Most values will produce exactly one item but in
626/// some cases it might make sense to produce more than one.
627pub trait ToRedisArgs: Sized {
628    /// This converts the value into a vector of bytes.  Each item
629    /// is a single argument.  Most items generate a vector of a
630    /// single item.
631    ///
632    /// The exception to this rule currently are vectors of items.
633    fn to_redis_args(&self) -> Vec<Vec<u8>> {
634        let mut out = Vec::new();
635        self.write_redis_args(&mut out);
636        out
637    }
638
639    /// This writes the value into a vector of bytes.  Each item
640    /// is a single argument.  Most items generate a single item.
641    ///
642    /// The exception to this rule currently are vectors of items.
643    fn write_redis_args<W>(&self, out: &mut W)
644    where
645        W: ?Sized + RedisWrite;
646
647    /// Returns an information about the contained value with regards
648    /// to it's numeric behavior in a redis context.  This is used in
649    /// some high level concepts to switch between different implementations
650    /// of redis functions (for instance `INCR` vs `INCRBYFLOAT`).
651    fn describe_numeric_behavior(&self) -> NumericBehavior {
652        NumericBehavior::NonNumeric
653    }
654
655    /// Returns an indiciation if the value contained is exactly one
656    /// argument.  It returns false if it's zero or more than one.  This
657    /// is used in some high level functions to intelligently switch
658    /// between `GET` and `MGET` variants.
659    fn is_single_arg(&self) -> bool {
660        true
661    }
662
663    /// This only exists internally as a workaround for the lack of
664    /// specialization.
665    #[doc(hidden)]
666    fn make_arg_vec<W>(items: &[Self], out: &mut W)
667    where
668        W: ?Sized + RedisWrite,
669    {
670        for item in items.iter() {
671            item.write_redis_args(out);
672        }
673    }
674
675    /// This only exists internally as a workaround for the lack of
676    /// specialization.
677    #[doc(hidden)]
678    fn make_arg_iter_ref<'a, I, W>(items: I, out: &mut W)
679    where
680        W: ?Sized + RedisWrite,
681        I: Iterator<Item = &'a Self>,
682        Self: 'a,
683    {
684        for item in items {
685            item.write_redis_args(out);
686        }
687    }
688
689    #[doc(hidden)]
690    fn is_single_vec_arg(items: &[Self]) -> bool {
691        items.len() == 1 && items[0].is_single_arg()
692    }
693}
694
695macro_rules! itoa_based_to_redis_impl {
696    ($t:ty, $numeric:expr) => {
697        impl ToRedisArgs for $t {
698            fn write_redis_args<W>(&self, out: &mut W)
699            where
700                W: ?Sized + RedisWrite,
701            {
702                let mut buf = ::itoa::Buffer::new();
703                let s = buf.format(*self);
704                out.write_arg(s.as_bytes())
705            }
706
707            fn describe_numeric_behavior(&self) -> NumericBehavior {
708                $numeric
709            }
710        }
711    };
712}
713
714macro_rules! non_zero_itoa_based_to_redis_impl {
715    ($t:ty, $numeric:expr) => {
716        impl ToRedisArgs for $t {
717            fn write_redis_args<W>(&self, out: &mut W)
718            where
719                W: ?Sized + RedisWrite,
720            {
721                let mut buf = ::itoa::Buffer::new();
722                let s = buf.format(self.get());
723                out.write_arg(s.as_bytes())
724            }
725
726            fn describe_numeric_behavior(&self) -> NumericBehavior {
727                $numeric
728            }
729        }
730    };
731}
732
733macro_rules! ryu_based_to_redis_impl {
734    ($t:ty, $numeric:expr) => {
735        impl ToRedisArgs for $t {
736            fn write_redis_args<W>(&self, out: &mut W)
737            where
738                W: ?Sized + RedisWrite,
739            {
740                let mut buf = ::ryu::Buffer::new();
741                let s = buf.format(*self);
742                out.write_arg(s.as_bytes())
743            }
744
745            fn describe_numeric_behavior(&self) -> NumericBehavior {
746                $numeric
747            }
748        }
749    };
750}
751
752impl ToRedisArgs for u8 {
753    fn write_redis_args<W>(&self, out: &mut W)
754    where
755        W: ?Sized + RedisWrite,
756    {
757        let mut buf = ::itoa::Buffer::new();
758        let s = buf.format(*self);
759        out.write_arg(s.as_bytes())
760    }
761
762    fn make_arg_vec<W>(items: &[u8], out: &mut W)
763    where
764        W: ?Sized + RedisWrite,
765    {
766        out.write_arg(items);
767    }
768
769    fn is_single_vec_arg(_items: &[u8]) -> bool {
770        true
771    }
772}
773
774itoa_based_to_redis_impl!(i8, NumericBehavior::NumberIsInteger);
775itoa_based_to_redis_impl!(i16, NumericBehavior::NumberIsInteger);
776itoa_based_to_redis_impl!(u16, NumericBehavior::NumberIsInteger);
777itoa_based_to_redis_impl!(i32, NumericBehavior::NumberIsInteger);
778itoa_based_to_redis_impl!(u32, NumericBehavior::NumberIsInteger);
779itoa_based_to_redis_impl!(i64, NumericBehavior::NumberIsInteger);
780itoa_based_to_redis_impl!(u64, NumericBehavior::NumberIsInteger);
781itoa_based_to_redis_impl!(isize, NumericBehavior::NumberIsInteger);
782itoa_based_to_redis_impl!(usize, NumericBehavior::NumberIsInteger);
783
784non_zero_itoa_based_to_redis_impl!(core::num::NonZeroU8, NumericBehavior::NumberIsInteger);
785non_zero_itoa_based_to_redis_impl!(core::num::NonZeroI8, NumericBehavior::NumberIsInteger);
786non_zero_itoa_based_to_redis_impl!(core::num::NonZeroU16, NumericBehavior::NumberIsInteger);
787non_zero_itoa_based_to_redis_impl!(core::num::NonZeroI16, NumericBehavior::NumberIsInteger);
788non_zero_itoa_based_to_redis_impl!(core::num::NonZeroU32, NumericBehavior::NumberIsInteger);
789non_zero_itoa_based_to_redis_impl!(core::num::NonZeroI32, NumericBehavior::NumberIsInteger);
790non_zero_itoa_based_to_redis_impl!(core::num::NonZeroU64, NumericBehavior::NumberIsInteger);
791non_zero_itoa_based_to_redis_impl!(core::num::NonZeroI64, NumericBehavior::NumberIsInteger);
792non_zero_itoa_based_to_redis_impl!(core::num::NonZeroUsize, NumericBehavior::NumberIsInteger);
793non_zero_itoa_based_to_redis_impl!(core::num::NonZeroIsize, NumericBehavior::NumberIsInteger);
794
795ryu_based_to_redis_impl!(f32, NumericBehavior::NumberIsFloat);
796ryu_based_to_redis_impl!(f64, NumericBehavior::NumberIsFloat);
797
798impl ToRedisArgs for bool {
799    fn write_redis_args<W>(&self, out: &mut W)
800    where
801        W: ?Sized + RedisWrite,
802    {
803        out.write_arg(if *self { b"1" } else { b"0" })
804    }
805}
806
807impl ToRedisArgs for String {
808    fn write_redis_args<W>(&self, out: &mut W)
809    where
810        W: ?Sized + RedisWrite,
811    {
812        out.write_arg(self.as_bytes())
813    }
814}
815
816impl<'a> ToRedisArgs for &'a str {
817    fn write_redis_args<W>(&self, out: &mut W)
818    where
819        W: ?Sized + RedisWrite,
820    {
821        out.write_arg(self.as_bytes())
822    }
823}
824
825impl<T: ToRedisArgs> ToRedisArgs for Vec<T> {
826    fn write_redis_args<W>(&self, out: &mut W)
827    where
828        W: ?Sized + RedisWrite,
829    {
830        ToRedisArgs::make_arg_vec(self, out)
831    }
832
833    fn is_single_arg(&self) -> bool {
834        ToRedisArgs::is_single_vec_arg(&self[..])
835    }
836}
837
838impl<'a, T: ToRedisArgs> ToRedisArgs for &'a [T] {
839    fn write_redis_args<W>(&self, out: &mut W)
840    where
841        W: ?Sized + RedisWrite,
842    {
843        ToRedisArgs::make_arg_vec(*self, out)
844    }
845
846    fn is_single_arg(&self) -> bool {
847        ToRedisArgs::is_single_vec_arg(*self)
848    }
849}
850
851impl<T: ToRedisArgs> ToRedisArgs for Option<T> {
852    fn write_redis_args<W>(&self, out: &mut W)
853    where
854        W: ?Sized + RedisWrite,
855    {
856        if let Some(ref x) = *self {
857            x.write_redis_args(out);
858        }
859    }
860
861    fn describe_numeric_behavior(&self) -> NumericBehavior {
862        match *self {
863            Some(ref x) => x.describe_numeric_behavior(),
864            None => NumericBehavior::NonNumeric,
865        }
866    }
867
868    fn is_single_arg(&self) -> bool {
869        match *self {
870            Some(ref x) => x.is_single_arg(),
871            None => false,
872        }
873    }
874}
875
876impl<T: ToRedisArgs> ToRedisArgs for &T {
877    fn write_redis_args<W>(&self, out: &mut W)
878    where
879        W: ?Sized + RedisWrite,
880    {
881        (*self).write_redis_args(out)
882    }
883}
884
885/// @note: Redis cannot store empty sets so the application has to
886/// check whether the set is empty and if so, not attempt to use that
887/// result
888impl<T: ToRedisArgs + Hash + Eq, S: BuildHasher + Default> ToRedisArgs for HashSet<T, S> {
889    fn write_redis_args<W>(&self, out: &mut W)
890    where
891        W: ?Sized + RedisWrite,
892    {
893        ToRedisArgs::make_arg_iter_ref(self.iter(), out)
894    }
895
896    fn is_single_arg(&self) -> bool {
897        self.len() <= 1
898    }
899}
900
901/// @note: Redis cannot store empty sets so the application has to
902/// check whether the set is empty and if so, not attempt to use that
903/// result
904impl<T: ToRedisArgs + Hash + Eq + Ord> ToRedisArgs for BTreeSet<T> {
905    fn write_redis_args<W>(&self, out: &mut W)
906    where
907        W: ?Sized + RedisWrite,
908    {
909        ToRedisArgs::make_arg_iter_ref(self.iter(), out)
910    }
911
912    fn is_single_arg(&self) -> bool {
913        self.len() <= 1
914    }
915}
916
917/// this flattens BTreeMap into something that goes well with HMSET
918/// @note: Redis cannot store empty sets so the application has to
919/// check whether the set is empty and if so, not attempt to use that
920/// result
921impl<T: ToRedisArgs + Hash + Eq + Ord, V: ToRedisArgs> ToRedisArgs for BTreeMap<T, V> {
922    fn write_redis_args<W>(&self, out: &mut W)
923    where
924        W: ?Sized + RedisWrite,
925    {
926        for (key, value) in self {
927            // otherwise things like HMSET will simply NOT work
928            assert!(key.is_single_arg() && value.is_single_arg());
929
930            key.write_redis_args(out);
931            value.write_redis_args(out);
932        }
933    }
934
935    fn is_single_arg(&self) -> bool {
936        self.len() <= 1
937    }
938}
939
940macro_rules! to_redis_args_for_tuple {
941    () => ();
942    ($($name:ident,)+) => (
943        #[doc(hidden)]
944        impl<$($name: ToRedisArgs),*> ToRedisArgs for ($($name,)*) {
945            // we have local variables named T1 as dummies and those
946            // variables are unused.
947            #[allow(non_snake_case, unused_variables)]
948            fn write_redis_args<W>(&self, out: &mut W) where W: ?Sized + RedisWrite {
949                let ($(ref $name,)*) = *self;
950                $($name.write_redis_args(out);)*
951            }
952
953            #[allow(non_snake_case, unused_variables)]
954            fn is_single_arg(&self) -> bool {
955                let mut n = 0u32;
956                $(let $name = (); n += 1;)*
957                n == 1
958            }
959        }
960        to_redis_args_for_tuple_peel!($($name,)*);
961    )
962}
963
964/// This chips of the leading one and recurses for the rest.  So if the first
965/// iteration was T1, T2, T3 it will recurse to T2, T3.  It stops for tuples
966/// of size 1 (does not implement down to unit).
967macro_rules! to_redis_args_for_tuple_peel {
968    ($name:ident, $($other:ident,)*) => (to_redis_args_for_tuple!($($other,)*);)
969}
970
971to_redis_args_for_tuple! { T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, }
972
973macro_rules! to_redis_args_for_array {
974    ($($N:expr)+) => {
975        $(
976            impl<'a, T: ToRedisArgs> ToRedisArgs for &'a [T; $N] {
977                fn write_redis_args<W>(&self, out: &mut W) where W: ?Sized + RedisWrite {
978                    ToRedisArgs::make_arg_vec(*self, out)
979                }
980
981                fn is_single_arg(&self) -> bool {
982                    ToRedisArgs::is_single_vec_arg(*self)
983                }
984            }
985        )+
986    }
987}
988
989to_redis_args_for_array! {
990     0  1  2  3  4  5  6  7  8  9
991    10 11 12 13 14 15 16 17 18 19
992    20 21 22 23 24 25 26 27 28 29
993    30 31 32
994}
995
996/// This trait is used to convert a redis value into a more appropriate
997/// type.  While a redis `Value` can represent any response that comes
998/// back from the redis server, usually you want to map this into something
999/// that works better in rust.  For instance you might want to convert the
1000/// return value into a `String` or an integer.
1001///
1002/// This trait is well supported throughout the library and you can
1003/// implement it for your own types if you want.
1004///
1005/// In addition to what you can see from the docs, this is also implemented
1006/// for tuples up to size 12 and for Vec<u8>.
1007pub trait FromRedisValue: Sized {
1008    /// Given a redis `Value` this attempts to convert it into the given
1009    /// destination type.  If that fails because it's not compatible an
1010    /// appropriate error is generated.
1011    fn from_redis_value(v: &Value) -> RedisResult<Self>;
1012
1013    /// Similar to `from_redis_value` but constructs a vector of objects
1014    /// from another vector of values.  This primarily exists internally
1015    /// to customize the behavior for vectors of tuples.
1016    fn from_redis_values(items: &[Value]) -> RedisResult<Vec<Self>> {
1017        Ok(items
1018            .iter()
1019            .filter_map(|item| FromRedisValue::from_redis_value(item).ok())
1020            .collect())
1021    }
1022
1023    /// This only exists internally as a workaround for the lack of
1024    /// specialization.
1025    #[doc(hidden)]
1026    fn from_byte_vec(_vec: &[u8]) -> Option<Vec<Self>> {
1027        None
1028    }
1029}
1030
1031macro_rules! from_redis_value_for_num_internal {
1032    ($t:ty, $v:expr) => {{
1033        let v = $v;
1034        match *v {
1035            Value::Int(val) => Ok(val as $t),
1036            Value::Status(ref s) => match s.parse::<$t>() {
1037                Ok(rv) => Ok(rv),
1038                Err(_) => invalid_type_error!(v, "Could not convert from string."),
1039            },
1040            Value::Data(ref bytes) => match from_utf8(bytes)?.parse::<$t>() {
1041                Ok(rv) => Ok(rv),
1042                Err(_) => invalid_type_error!(v, "Could not convert from string."),
1043            },
1044            _ => invalid_type_error!(v, "Response type not convertible to numeric."),
1045        }
1046    }};
1047}
1048
1049macro_rules! from_redis_value_for_num {
1050    ($t:ty) => {
1051        impl FromRedisValue for $t {
1052            fn from_redis_value(v: &Value) -> RedisResult<$t> {
1053                from_redis_value_for_num_internal!($t, v)
1054            }
1055        }
1056    };
1057}
1058
1059impl FromRedisValue for u8 {
1060    fn from_redis_value(v: &Value) -> RedisResult<u8> {
1061        from_redis_value_for_num_internal!(u8, v)
1062    }
1063
1064    fn from_byte_vec(vec: &[u8]) -> Option<Vec<u8>> {
1065        Some(vec.to_vec())
1066    }
1067}
1068
1069from_redis_value_for_num!(i8);
1070from_redis_value_for_num!(i16);
1071from_redis_value_for_num!(u16);
1072from_redis_value_for_num!(i32);
1073from_redis_value_for_num!(u32);
1074from_redis_value_for_num!(i64);
1075from_redis_value_for_num!(u64);
1076from_redis_value_for_num!(i128);
1077from_redis_value_for_num!(u128);
1078from_redis_value_for_num!(f32);
1079from_redis_value_for_num!(f64);
1080from_redis_value_for_num!(isize);
1081from_redis_value_for_num!(usize);
1082
1083impl FromRedisValue for bool {
1084    fn from_redis_value(v: &Value) -> RedisResult<bool> {
1085        match *v {
1086            Value::Nil => Ok(false),
1087            Value::Int(val) => Ok(val != 0),
1088            Value::Status(ref s) => {
1089                if &s[..] == "1" {
1090                    Ok(true)
1091                } else if &s[..] == "0" {
1092                    Ok(false)
1093                } else {
1094                    invalid_type_error!(v, "Response status not valid boolean");
1095                }
1096            }
1097            Value::Data(ref bytes) => {
1098                if bytes == b"1" {
1099                    Ok(true)
1100                } else if bytes == b"0" {
1101                    Ok(false)
1102                } else {
1103                    invalid_type_error!(v, "Response type not bool compatible.");
1104                }
1105            }
1106            Value::Okay => Ok(true),
1107            _ => invalid_type_error!(v, "Response type not bool compatible."),
1108        }
1109    }
1110}
1111
1112impl FromRedisValue for String {
1113    fn from_redis_value(v: &Value) -> RedisResult<String> {
1114        match *v {
1115            Value::Data(ref bytes) => Ok(from_utf8(bytes)?.to_string()),
1116            Value::Okay => Ok("OK".to_string()),
1117            Value::Status(ref val) => Ok(val.to_string()),
1118            _ => invalid_type_error!(v, "Response type not string compatible."),
1119        }
1120    }
1121}
1122
1123impl<T: FromRedisValue> FromRedisValue for Vec<T> {
1124    fn from_redis_value(v: &Value) -> RedisResult<Vec<T>> {
1125        match *v {
1126            // this hack allows us to specialize Vec<u8> to work with
1127            // binary data whereas all others will fail with an error.
1128            Value::Data(ref bytes) => match FromRedisValue::from_byte_vec(bytes) {
1129                Some(x) => Ok(x),
1130                None => invalid_type_error!(v, "Response type not vector compatible."),
1131            },
1132            Value::Bulk(ref items) => FromRedisValue::from_redis_values(items),
1133            Value::Nil => Ok(vec![]),
1134            _ => invalid_type_error!(v, "Response type not vector compatible."),
1135        }
1136    }
1137}
1138
1139impl<K: FromRedisValue + Eq + Hash, V: FromRedisValue, S: BuildHasher + Default> FromRedisValue
1140    for HashMap<K, V, S>
1141{
1142    fn from_redis_value(v: &Value) -> RedisResult<HashMap<K, V, S>> {
1143        v.as_map_iter()
1144            .ok_or_else(|| invalid_type_error_inner!(v, "Response type not hashmap compatible"))?
1145            .map(|(k, v)| Ok((from_redis_value(k)?, from_redis_value(v)?)))
1146            .collect()
1147    }
1148}
1149
1150impl<K: FromRedisValue + Eq + Hash, V: FromRedisValue> FromRedisValue for BTreeMap<K, V>
1151where
1152    K: Ord,
1153{
1154    fn from_redis_value(v: &Value) -> RedisResult<BTreeMap<K, V>> {
1155        v.as_map_iter()
1156            .ok_or_else(|| invalid_type_error_inner!(v, "Response type not btreemap compatible"))?
1157            .map(|(k, v)| Ok((from_redis_value(k)?, from_redis_value(v)?)))
1158            .collect()
1159    }
1160}
1161
1162impl<T: FromRedisValue + Eq + Hash, S: BuildHasher + Default> FromRedisValue for HashSet<T, S> {
1163    fn from_redis_value(v: &Value) -> RedisResult<HashSet<T, S>> {
1164        let items = v
1165            .as_sequence()
1166            .ok_or_else(|| invalid_type_error_inner!(v, "Response type not hashset compatible"))?;
1167        items.iter().map(|item| from_redis_value(item)).collect()
1168    }
1169}
1170
1171impl<T: FromRedisValue + Eq + Hash> FromRedisValue for BTreeSet<T>
1172where
1173    T: Ord,
1174{
1175    fn from_redis_value(v: &Value) -> RedisResult<BTreeSet<T>> {
1176        let items = v
1177            .as_sequence()
1178            .ok_or_else(|| invalid_type_error_inner!(v, "Response type not btreeset compatible"))?;
1179        items.iter().map(|item| from_redis_value(item)).collect()
1180    }
1181}
1182
1183impl FromRedisValue for Value {
1184    fn from_redis_value(v: &Value) -> RedisResult<Value> {
1185        Ok(v.clone())
1186    }
1187}
1188
1189impl FromRedisValue for () {
1190    fn from_redis_value(_v: &Value) -> RedisResult<()> {
1191        Ok(())
1192    }
1193}
1194
1195macro_rules! from_redis_value_for_tuple {
1196    () => ();
1197    ($($name:ident,)+) => (
1198        #[doc(hidden)]
1199        impl<$($name: FromRedisValue),*> FromRedisValue for ($($name,)*) {
1200            // we have local variables named T1 as dummies and those
1201            // variables are unused.
1202            #[allow(non_snake_case, unused_variables)]
1203            fn from_redis_value(v: &Value) -> RedisResult<($($name,)*)> {
1204                match *v {
1205                    Value::Bulk(ref items) => {
1206                        // hacky way to count the tuple size
1207                        let mut n = 0;
1208                        $(let $name = (); n += 1;)*
1209                        if items.len() != n {
1210                            invalid_type_error!(v, "Bulk response of wrong dimension")
1211                        }
1212
1213                        // this is pretty ugly too.  The { i += 1; i - 1} is rust's
1214                        // postfix increment :)
1215                        let mut i = 0;
1216                        Ok(($({let $name = (); from_redis_value(
1217                             &items[{ i += 1; i - 1 }])?},)*))
1218                    }
1219                    _ => invalid_type_error!(v, "Not a bulk response")
1220                }
1221            }
1222
1223            #[allow(non_snake_case, unused_variables)]
1224            fn from_redis_values(items: &[Value]) -> RedisResult<Vec<($($name,)*)>> {
1225                // hacky way to count the tuple size
1226                let mut n = 0;
1227                $(let $name = (); n += 1;)*
1228                if items.len() % n != 0 {
1229                    invalid_type_error!(items, "Bulk response of wrong dimension")
1230                }
1231
1232                // this is pretty ugly too.  The { i += 1; i - 1} is rust's
1233                // postfix increment :)
1234                let mut rv = vec![];
1235                if items.len() == 0 {
1236                    return Ok(rv)
1237                }
1238                for chunk in items.chunks_exact(n) {
1239                    match chunk {
1240                        [$($name),*] => rv.push(($(from_redis_value($name)?),*),),
1241                         _ => unreachable!(),
1242                    }
1243                }
1244                Ok(rv)
1245            }
1246        }
1247        from_redis_value_for_tuple_peel!($($name,)*);
1248    )
1249}
1250
1251/// This chips of the leading one and recurses for the rest.  So if the first
1252/// iteration was T1, T2, T3 it will recurse to T2, T3.  It stops for tuples
1253/// of size 1 (does not implement down to unit).
1254macro_rules! from_redis_value_for_tuple_peel {
1255    ($name:ident, $($other:ident,)*) => (from_redis_value_for_tuple!($($other,)*);)
1256}
1257
1258from_redis_value_for_tuple! { T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, }
1259
1260impl FromRedisValue for InfoDict {
1261    fn from_redis_value(v: &Value) -> RedisResult<InfoDict> {
1262        let s: String = from_redis_value(v)?;
1263        Ok(InfoDict::new(&s))
1264    }
1265}
1266
1267impl<T: FromRedisValue> FromRedisValue for Option<T> {
1268    fn from_redis_value(v: &Value) -> RedisResult<Option<T>> {
1269        if let Value::Nil = *v {
1270            return Ok(None);
1271        }
1272        Ok(Some(from_redis_value(v)?))
1273    }
1274}
1275
1276#[cfg(feature = "bytes")]
1277impl FromRedisValue for bytes::Bytes {
1278    fn from_redis_value(v: &Value) -> RedisResult<Self> {
1279        match v {
1280            Value::Data(bytes_vec) => Ok(bytes::Bytes::copy_from_slice(bytes_vec.as_ref())),
1281            _ => invalid_type_error!(v, "Not binary data"),
1282        }
1283    }
1284}
1285
1286/// A shortcut function to invoke `FromRedisValue::from_redis_value`
1287/// to make the API slightly nicer.
1288pub fn from_redis_value<T: FromRedisValue>(v: &Value) -> RedisResult<T> {
1289    FromRedisValue::from_redis_value(v)
1290}