redis_async/
resp.rs

1/*
2 * Copyright 2017-2024 Ben Ashford
3 *
4 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 * option. This file may not be copied, modified, or distributed
8 * except according to those terms.
9 */
10
11//! An implementation of the RESP protocol
12
13use std::collections::HashMap;
14use std::fmt;
15use std::hash::{BuildHasher, Hash};
16use std::io;
17use std::str;
18use std::sync::Arc;
19
20use bytes::{Buf, BufMut, BytesMut};
21
22use tokio_util::codec::{Decoder, Encoder};
23
24use super::error::{self, Error};
25
26/// A single RESP value, this owns the data that is read/to-be written to Redis.
27///
28/// It is cloneable to allow multiple copies to be delivered in certain circumstances, e.g. multiple
29/// subscribers to the same topic.
30#[derive(Clone, Eq, PartialEq)]
31pub enum RespValue {
32    Nil,
33
34    /// Zero, one or more other `RespValue`s.
35    Array(Vec<RespValue>),
36
37    /// A bulk string.  In Redis terminology a string is a byte-array, so this is stored as a
38    /// vector of `u8`s to allow clients to interpret the bytes as appropriate.
39    BulkString(Vec<u8>),
40
41    /// An error from the Redis server
42    Error(String),
43
44    /// Redis documentation defines an integer as being a signed 64-bit integer:
45    /// https://redis.io/topics/protocol#resp-integers
46    Integer(i64),
47
48    SimpleString(String),
49}
50
51impl RespValue {
52    #[inline]
53    fn into_result(self) -> Result<RespValue, Error> {
54        match self {
55            RespValue::Error(string) => Err(Error::Remote(string)),
56            x => Ok(x),
57        }
58    }
59
60    /// Convenience function for building dynamic Redis commands with variable numbers of
61    /// arguments, e.g. RPUSH
62    ///
63    /// This will panic if called for anything other than arrays
64    pub fn append<T>(mut self, other: impl IntoIterator<Item = T>) -> Self
65    where
66        T: Into<RespValue>,
67    {
68        match self {
69            RespValue::Array(ref mut vals) => {
70                vals.extend(other.into_iter().map(|t| t.into()));
71            }
72            _ => panic!("Can only append to arrays"),
73        }
74        self
75    }
76
77    /// Push item to Resp array
78    ///
79    /// This will panic if called for anything other than arrays
80    pub fn push<T: Into<RespValue>>(&mut self, item: T) {
81        match self {
82            RespValue::Array(ref mut vals) => {
83                vals.push(item.into());
84            }
85            _ => panic!("Can only push to arrays"),
86        }
87    }
88}
89
90impl fmt::Debug for RespValue {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        match self {
93            RespValue::Nil => write!(f, "Nil"),
94            RespValue::Array(vals) => write!(f, "Array({:?})", vals),
95            RespValue::BulkString(bytes) => {
96                // For BulkString we try and be clever and show the utf-8 version
97                // if it's valid utf-8
98                if let Ok(string) = str::from_utf8(bytes) {
99                    write!(f, "BulkString({:?})", string)
100                } else {
101                    write!(f, "BulkString({:?})", bytes)
102                }
103            }
104            RespValue::Error(string) => write!(f, "Error({:?})", string),
105            RespValue::Integer(int) => write!(f, "Integer({:?})", int),
106            RespValue::SimpleString(string) => write!(f, "SimpleString({:?})", string),
107        }
108    }
109}
110
111/// A trait to be implemented for every time which can be read from a RESP value.
112///
113/// Implementing this trait on a type means that type becomes a valid return type for calls such as `send` on
114/// `client::PairedConnection`
115pub trait FromResp: Sized {
116    /// Return a `Result` containing either `Self` or `Error`.  Errors can occur due to either: a) the particular
117    /// `RespValue` being incompatible with the required type, or b) a remote Redis error occuring.
118    #[inline]
119    fn from_resp(resp: RespValue) -> Result<Self, Error> {
120        Self::from_resp_int(resp.into_result()?)
121    }
122
123    fn from_resp_int(resp: RespValue) -> Result<Self, Error>;
124}
125
126impl FromResp for RespValue {
127    #[inline]
128    fn from_resp_int(resp: RespValue) -> Result<RespValue, Error> {
129        Ok(resp)
130    }
131}
132
133impl FromResp for String {
134    #[inline]
135    fn from_resp_int(resp: RespValue) -> Result<String, Error> {
136        match resp {
137            RespValue::BulkString(ref bytes) => Ok(String::from_utf8_lossy(bytes).into_owned()),
138            RespValue::Integer(i) => Ok(i.to_string()),
139            RespValue::SimpleString(string) => Ok(string),
140            _ => Err(error::resp("Cannot convert into a string", resp)),
141        }
142    }
143}
144
145impl FromResp for Arc<str> {
146    #[inline]
147    fn from_resp_int(resp: RespValue) -> Result<Arc<str>, Error> {
148        match resp {
149            RespValue::BulkString(ref bytes) => Ok(String::from_utf8_lossy(bytes).into()),
150            _ => Err(error::resp("Cannot convert into a Arc<str>", resp)),
151        }
152    }
153}
154
155impl FromResp for Vec<u8> {
156    #[inline]
157    fn from_resp_int(resp: RespValue) -> Result<Vec<u8>, Error> {
158        match resp {
159            RespValue::BulkString(bytes) => Ok(bytes),
160            _ => Err(error::resp("Not a bulk string", resp)),
161        }
162    }
163}
164
165impl FromResp for i64 {
166    #[inline]
167    fn from_resp_int(resp: RespValue) -> Result<i64, Error> {
168        match resp {
169            RespValue::Integer(i) => Ok(i),
170            _ => Err(error::resp("Cannot be converted into an i64", resp)),
171        }
172    }
173}
174
175macro_rules! impl_fromresp_integers {
176    ($($int_ty:ident),* $(,)*) => {
177        $(
178            #[allow(clippy::cast_lossless)]
179            impl FromResp for $int_ty {
180                #[inline]
181                fn from_resp_int(resp: RespValue) -> Result<Self, Error> {
182                    i64::from_resp_int(resp).and_then(|x| {
183                        // $int_ty::max_value() as i64 > 0 should be optimized out. It tests if
184                        // the target integer type needs an "upper bounds" check
185                        if x < ($int_ty::min_value() as i64)
186                            || ($int_ty::max_value() as i64 > 0
187                                && x > ($int_ty::max_value() as i64))
188                        {
189                            Err(error::resp(
190                                concat!(
191                                    "i64 value cannot be represented as {}",
192                                    stringify!($int_ty),
193                                ),
194                                RespValue::Integer(x),
195                            ))
196                        } else {
197                            Ok(x as $int_ty)
198                        }
199                    })
200                }
201            }
202        )*
203    };
204}
205
206impl_fromresp_integers!(isize, usize, i32, u32, u64);
207
208impl FromResp for bool {
209    #[inline]
210    fn from_resp_int(resp: RespValue) -> Result<bool, Error> {
211        i64::from_resp_int(resp).and_then(|x| match x {
212            0 => Ok(false),
213            1 => Ok(true),
214            _ => Err(error::resp(
215                "i64 value cannot be represented as bool",
216                RespValue::Integer(x),
217            )),
218        })
219    }
220}
221
222impl<T: FromResp> FromResp for Option<T> {
223    #[inline]
224    fn from_resp_int(resp: RespValue) -> Result<Option<T>, Error> {
225        match resp {
226            RespValue::Nil => Ok(None),
227            x => Ok(Some(T::from_resp_int(x)?)),
228        }
229    }
230}
231
232impl<T: FromResp> FromResp for Vec<T> {
233    #[inline]
234    fn from_resp_int(resp: RespValue) -> Result<Vec<T>, Error> {
235        match resp {
236            RespValue::Array(ary) => {
237                let mut ar = Vec::with_capacity(ary.len());
238                for value in ary {
239                    ar.push(T::from_resp(value)?);
240                }
241                Ok(ar)
242            }
243            _ => Err(error::resp("Cannot be converted into a vector", resp)),
244        }
245    }
246}
247
248impl<K: FromResp + Hash + Eq, T: FromResp, S: BuildHasher + Default> FromResp for HashMap<K, T, S> {
249    fn from_resp_int(resp: RespValue) -> Result<HashMap<K, T, S>, Error> {
250        match resp {
251            RespValue::Array(ary) => {
252                let mut map = HashMap::with_capacity_and_hasher(ary.len(), S::default());
253                let mut items = ary.into_iter();
254
255                while let Some(k) = items.next() {
256                    let key = K::from_resp(k)?;
257                    let value = T::from_resp(items.next().ok_or_else(|| {
258                        error::resp(
259                            "Cannot convert an odd number of elements into a hashmap",
260                            "".into(),
261                        )
262                    })?)?;
263
264                    map.insert(key, value);
265                }
266
267                Ok(map)
268            }
269            _ => Err(error::resp("Cannot be converted into a hashmap", resp)),
270        }
271    }
272}
273
274impl FromResp for () {
275    #[inline]
276    fn from_resp_int(resp: RespValue) -> Result<(), Error> {
277        match resp {
278            RespValue::SimpleString(string) => match string.as_ref() {
279                "OK" => Ok(()),
280                _ => Err(Error::Resp(
281                    format!("Unexpected value within SimpleString: {}", string),
282                    None,
283                )),
284            },
285            _ => Err(error::resp(
286                "Unexpected value, should be encoded as a SimpleString",
287                resp,
288            )),
289        }
290    }
291}
292
293impl<A, B> FromResp for (A, B)
294where
295    A: FromResp,
296    B: FromResp,
297{
298    #[inline]
299    fn from_resp_int(resp: RespValue) -> Result<(A, B), Error> {
300        match resp {
301            RespValue::Array(ary) => {
302                if ary.len() == 2 {
303                    let mut ary_iter = ary.into_iter();
304                    Ok((
305                        A::from_resp(ary_iter.next().expect("No value"))?,
306                        B::from_resp(ary_iter.next().expect("No value"))?,
307                    ))
308                } else {
309                    Err(Error::Resp(
310                        format!("Array needs to be 2 elements, is: {}", ary.len()),
311                        None,
312                    ))
313                }
314            }
315            _ => Err(error::resp(
316                "Unexpected value, should be encoded as an array",
317                resp,
318            )),
319        }
320    }
321}
322
323impl<A, B, C> FromResp for (A, B, C)
324where
325    A: FromResp,
326    B: FromResp,
327    C: FromResp,
328{
329    #[inline]
330    fn from_resp_int(resp: RespValue) -> Result<(A, B, C), Error> {
331        match resp {
332            RespValue::Array(ary) => {
333                if ary.len() == 3 {
334                    let mut ary_iter = ary.into_iter();
335                    Ok((
336                        A::from_resp(ary_iter.next().expect("No value"))?,
337                        B::from_resp(ary_iter.next().expect("No value"))?,
338                        C::from_resp(ary_iter.next().expect("No value"))?,
339                    ))
340                } else {
341                    Err(Error::Resp(
342                        format!("Array needs to be 3 elements, is: {}", ary.len()),
343                        None,
344                    ))
345                }
346            }
347            _ => Err(error::resp(
348                "Unexpected value, should be encoded as an array",
349                resp,
350            )),
351        }
352    }
353}
354
355/// Macro to create a RESP array, useful for preparing commands to send.  Elements can be any type, or a mixture
356/// of types, that satisfy `Into<RespValue>`.
357///
358/// As a general rule, if a value is moved, the data can be deconstructed (if appropriate, e.g. String) and the raw
359/// data moved into the corresponding `RespValue`.  If a reference is provided, the data will be copied instead.
360///
361/// # Examples
362///
363/// ```
364/// #[macro_use]
365/// extern crate redis_async;
366///
367/// fn main() {
368///     let value = format!("something_{}", 123);
369///     resp_array!["SET", "key_name", value];
370/// }
371/// ```
372///
373/// For variable length Redis commands:
374///
375/// ```
376/// #[macro_use]
377/// extern crate redis_async;
378///
379/// fn main() {
380///     let data = vec!["data", "from", "somewhere", "else"];
381///     let command = resp_array!["RPUSH", "mykey"].append(data);
382/// }
383/// ```
384#[macro_export]
385macro_rules! resp_array {
386    ($($e:expr),* $(,)?) => {
387        {
388            $crate::resp::RespValue::Array(vec![
389                $(
390                    $e.into(),
391                )*
392            ])
393        }
394    }
395}
396
397macro_rules! into_resp {
398    ($t:ty, $f:ident) => {
399        impl<'a> From<$t> for RespValue {
400            #[inline]
401            fn from(from: $t) -> RespValue {
402                from.$f()
403            }
404        }
405    };
406}
407
408/// A specific trait to convert into a `RespValue::BulkString`
409pub trait IntoRespString {
410    fn into_resp_string(self) -> RespValue;
411}
412
413macro_rules! string_into_resp {
414    ($t:ty) => {
415        into_resp!($t, into_resp_string);
416    };
417}
418
419impl IntoRespString for String {
420    #[inline]
421    fn into_resp_string(self) -> RespValue {
422        RespValue::BulkString(self.into_bytes())
423    }
424}
425string_into_resp!(String);
426
427impl<'a> IntoRespString for &'a String {
428    #[inline]
429    fn into_resp_string(self) -> RespValue {
430        RespValue::BulkString(self.as_bytes().into())
431    }
432}
433string_into_resp!(&'a String);
434
435impl<'a> IntoRespString for &'a str {
436    #[inline]
437    fn into_resp_string(self) -> RespValue {
438        RespValue::BulkString(self.as_bytes().into())
439    }
440}
441string_into_resp!(&'a str);
442
443impl<'a> IntoRespString for &'a [u8] {
444    #[inline]
445    fn into_resp_string(self) -> RespValue {
446        RespValue::BulkString(self.to_vec())
447    }
448}
449string_into_resp!(&'a [u8]);
450
451impl IntoRespString for Vec<u8> {
452    #[inline]
453    fn into_resp_string(self) -> RespValue {
454        RespValue::BulkString(self)
455    }
456}
457string_into_resp!(Vec<u8>);
458
459impl IntoRespString for Arc<str> {
460    #[inline]
461    fn into_resp_string(self) -> RespValue {
462        RespValue::BulkString(self.as_bytes().into())
463    }
464}
465string_into_resp!(Arc<str>);
466
467pub trait IntoRespInteger {
468    fn into_resp_integer(self) -> RespValue;
469}
470
471macro_rules! integer_into_resp {
472    ($t:ty) => {
473        into_resp!($t, into_resp_integer);
474    };
475}
476
477impl IntoRespInteger for usize {
478    #[inline]
479    fn into_resp_integer(self) -> RespValue {
480        RespValue::Integer(self as i64)
481    }
482}
483integer_into_resp!(usize);
484
485/// Codec to read frames
486pub struct RespCodec;
487
488fn write_rn(buf: &mut BytesMut) {
489    buf.put_u8(b'\r');
490    buf.put_u8(b'\n');
491}
492
493fn check_and_reserve(buf: &mut BytesMut, amt: usize) {
494    let remaining_bytes = buf.remaining_mut();
495    if remaining_bytes < amt {
496        buf.reserve(amt);
497    }
498}
499
500fn write_header(symb: u8, len: i64, buf: &mut BytesMut) {
501    let len_as_string = len.to_string();
502    let len_as_bytes = len_as_string.as_bytes();
503    let header_bytes = 1 + len_as_bytes.len() + 2;
504    check_and_reserve(buf, header_bytes);
505    buf.put_u8(symb);
506    buf.extend(len_as_bytes);
507    write_rn(buf);
508}
509
510fn write_simple_string(symb: u8, string: &str, buf: &mut BytesMut) {
511    let bytes = string.as_bytes();
512    let size = 1 + bytes.len() + 2;
513    check_and_reserve(buf, size);
514    buf.put_u8(symb);
515    buf.extend(bytes);
516    write_rn(buf);
517}
518
519impl Encoder<RespValue> for RespCodec {
520    type Error = io::Error;
521
522    fn encode(&mut self, msg: RespValue, buf: &mut BytesMut) -> Result<(), Self::Error> {
523        match msg {
524            RespValue::Nil => {
525                write_header(b'$', -1, buf);
526            }
527            RespValue::Array(ary) => {
528                write_header(b'*', ary.len() as i64, buf);
529                for v in ary {
530                    self.encode(v, buf)?;
531                }
532            }
533            RespValue::BulkString(bstr) => {
534                let len = bstr.len();
535                write_header(b'$', len as i64, buf);
536                check_and_reserve(buf, len + 2);
537                buf.extend(bstr);
538                write_rn(buf);
539            }
540            RespValue::Error(ref string) => {
541                write_simple_string(b'-', string, buf);
542            }
543            RespValue::Integer(val) => {
544                // Simple integer are just the header
545                write_header(b':', val, buf);
546            }
547            RespValue::SimpleString(ref string) => {
548                write_simple_string(b'+', string, buf);
549            }
550        }
551        Ok(())
552    }
553}
554
555#[inline]
556fn parse_error(message: String) -> Error {
557    Error::Resp(message, None)
558}
559
560/// Many RESP types have their length (which is either bytes or "number of elements", depending on context)
561/// encoded as a string, terminated by "\r\n", this looks for them.
562///
563/// Only return the string if the whole sequence is complete, including the terminator bytes (but those final
564/// two bytes will not be returned)
565///
566/// TODO - rename this function potentially, it's used for simple integers too
567fn scan_integer(buf: &mut BytesMut, idx: usize) -> Result<Option<(usize, &[u8])>, Error> {
568    let length = buf.len();
569    let mut at_end = false;
570    let mut pos = idx;
571    loop {
572        if length <= pos {
573            return Ok(None);
574        }
575        match (at_end, buf[pos]) {
576            (true, b'\n') => return Ok(Some((pos + 1, &buf[idx..pos - 1]))),
577            (false, b'\r') => at_end = true,
578            (false, b'0'..=b'9') => (),
579            (false, b'-') => (),
580            (_, val) => {
581                return Err(parse_error(format!(
582                    "Unexpected byte in size_string: {}",
583                    val
584                )));
585            }
586        }
587        pos += 1;
588    }
589}
590
591fn scan_string(buf: &mut BytesMut, idx: usize) -> Option<(usize, String)> {
592    let length = buf.len();
593    let mut at_end = false;
594    let mut pos = idx;
595    loop {
596        if length <= pos {
597            return None;
598        }
599        match (at_end, buf[pos]) {
600            (true, b'\n') => {
601                let value = String::from_utf8_lossy(&buf[idx..pos - 1]).into_owned();
602                return Some((pos + 1, value));
603            }
604            (true, _) => at_end = false,
605            (false, b'\r') => at_end = true,
606            (false, _) => (),
607        }
608        pos += 1;
609    }
610}
611
612fn decode_raw_integer(buf: &mut BytesMut, idx: usize) -> Result<Option<(usize, i64)>, Error> {
613    match scan_integer(buf, idx) {
614        Ok(None) => Ok(None),
615        Ok(Some((pos, int_str))) => {
616            // Redis integers are transmitted as strings, so we first convert the raw bytes into a string...
617            match str::from_utf8(int_str) {
618                Ok(string) => {
619                    // ...and then parse the string.
620                    match string.parse() {
621                        Ok(int) => Ok(Some((pos, int))),
622                        Err(_) => Err(parse_error(format!("Not an integer: {}", string))),
623                    }
624                }
625                Err(_) => Err(parse_error(format!("Not a valid string: {:?}", int_str))),
626            }
627        }
628        Err(e) => Err(e),
629    }
630}
631
632type DecodeResult = Result<Option<(usize, RespValue)>, Error>;
633
634fn decode_bulk_string(buf: &mut BytesMut, idx: usize) -> DecodeResult {
635    match decode_raw_integer(buf, idx) {
636        Ok(None) => Ok(None),
637        Ok(Some((pos, -1))) => Ok(Some((pos, RespValue::Nil))),
638        Ok(Some((pos, size))) if size >= 0 => {
639            let size = size as usize;
640            let remaining = buf.len() - pos;
641            let required_bytes = size + 2;
642
643            if remaining < required_bytes {
644                return Ok(None);
645            }
646
647            let bulk_string = RespValue::BulkString(buf[pos..(pos + size)].to_vec());
648            Ok(Some((pos + required_bytes, bulk_string)))
649        }
650        Ok(Some((_, size))) => Err(parse_error(format!("Invalid string size: {}", size))),
651        Err(e) => Err(e),
652    }
653}
654
655fn decode_array(buf: &mut BytesMut, idx: usize) -> DecodeResult {
656    match decode_raw_integer(buf, idx) {
657        Ok(None) => Ok(None),
658        Ok(Some((pos, -1))) => Ok(Some((pos, RespValue::Nil))),
659        Ok(Some((pos, size))) if size >= 0 => {
660            let size = size as usize;
661            let mut pos = pos;
662            let mut values = Vec::with_capacity(size);
663            for _ in 0..size {
664                match decode(buf, pos) {
665                    Ok(None) => return Ok(None),
666                    Ok(Some((new_pos, value))) => {
667                        values.push(value);
668                        pos = new_pos;
669                    }
670                    Err(e) => return Err(e),
671                }
672            }
673            Ok(Some((pos, RespValue::Array(values))))
674        }
675        Ok(Some((_, size))) => Err(parse_error(format!("Invalid array size: {}", size))),
676        Err(e) => Err(e),
677    }
678}
679
680fn decode_integer(buf: &mut BytesMut, idx: usize) -> DecodeResult {
681    match decode_raw_integer(buf, idx) {
682        Ok(None) => Ok(None),
683        Ok(Some((pos, int))) => Ok(Some((pos, RespValue::Integer(int)))),
684        Err(e) => Err(e),
685    }
686}
687
688/// A simple string is any series of bytes that ends with `\r\n`
689#[allow(clippy::unnecessary_wraps)]
690fn decode_simple_string(buf: &mut BytesMut, idx: usize) -> DecodeResult {
691    match scan_string(buf, idx) {
692        None => Ok(None),
693        Some((pos, string)) => Ok(Some((pos, RespValue::SimpleString(string)))),
694    }
695}
696
697#[allow(clippy::unnecessary_wraps)]
698fn decode_error(buf: &mut BytesMut, idx: usize) -> DecodeResult {
699    match scan_string(buf, idx) {
700        None => Ok(None),
701        Some((pos, string)) => Ok(Some((pos, RespValue::Error(string)))),
702    }
703}
704
705fn decode(buf: &mut BytesMut, idx: usize) -> DecodeResult {
706    let length = buf.len();
707    if length <= idx {
708        return Ok(None);
709    }
710
711    let first_byte = buf[idx];
712    match first_byte {
713        b'$' => decode_bulk_string(buf, idx + 1),
714        b'*' => decode_array(buf, idx + 1),
715        b':' => decode_integer(buf, idx + 1),
716        b'+' => decode_simple_string(buf, idx + 1),
717        b'-' => decode_error(buf, idx + 1),
718        _ => Err(parse_error(format!("Unexpected byte: {}", first_byte))),
719    }
720}
721
722impl Decoder for RespCodec {
723    type Item = RespValue;
724    type Error = Error;
725
726    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
727        match decode(buf, 0) {
728            Ok(None) => Ok(None),
729            Ok(Some((pos, item))) => {
730                buf.advance(pos);
731                Ok(Some(item))
732            }
733            Err(e) => Err(e),
734        }
735    }
736}
737
738#[cfg(test)]
739mod tests {
740    use std::collections::HashMap;
741
742    use bytes::BytesMut;
743
744    use tokio_util::codec::{Decoder, Encoder};
745
746    use super::{Error, FromResp, RespCodec, RespValue};
747
748    fn obj_to_bytes(obj: RespValue) -> Vec<u8> {
749        let mut bytes = BytesMut::new();
750        let mut codec = RespCodec;
751        codec.encode(obj, &mut bytes).unwrap();
752        bytes.to_vec()
753    }
754
755    #[test]
756    fn test_resp_array_macro() {
757        let resp_object = resp_array!["SET", "x"];
758        let bytes = obj_to_bytes(resp_object);
759        assert_eq!(b"*2\r\n$3\r\nSET\r\n$1\r\nx\r\n", bytes.as_slice());
760
761        let resp_object = resp_array!["RPUSH", "wyz"].append(vec!["a", "b"]);
762        let bytes = obj_to_bytes(resp_object);
763        assert_eq!(
764            &b"*4\r\n$5\r\nRPUSH\r\n$3\r\nwyz\r\n$1\r\na\r\n$1\r\nb\r\n"[..],
765            bytes.as_slice()
766        );
767
768        let vals = vec![String::from("a"), String::from("b")];
769        #[allow(clippy::needless_borrow)]
770        let resp_object = resp_array!["RPUSH", "xyz"].append(vals);
771        let bytes = obj_to_bytes(resp_object);
772        assert_eq!(
773            &b"*4\r\n$5\r\nRPUSH\r\n$3\r\nxyz\r\n$1\r\na\r\n$1\r\nb\r\n"[..],
774            bytes.as_slice()
775        );
776    }
777
778    #[test]
779    fn test_bulk_string() {
780        let resp_object = RespValue::BulkString(b"THISISATEST".to_vec());
781        let mut bytes = BytesMut::new();
782        let mut codec = RespCodec;
783        codec.encode(resp_object.clone(), &mut bytes).unwrap();
784        assert_eq!(b"$11\r\nTHISISATEST\r\n".to_vec(), bytes.to_vec());
785
786        let deserialized = codec.decode(&mut bytes).unwrap().unwrap();
787        assert_eq!(deserialized, resp_object);
788    }
789
790    #[test]
791    fn test_array() {
792        let resp_object = RespValue::Array(vec!["TEST1".into(), "TEST2".into()]);
793        let mut bytes = BytesMut::new();
794        let mut codec = RespCodec;
795        codec.encode(resp_object.clone(), &mut bytes).unwrap();
796        assert_eq!(
797            b"*2\r\n$5\r\nTEST1\r\n$5\r\nTEST2\r\n".to_vec(),
798            bytes.to_vec()
799        );
800
801        let deserialized = codec.decode(&mut bytes).unwrap().unwrap();
802        assert_eq!(deserialized, resp_object);
803    }
804
805    #[test]
806    fn test_nil_string() {
807        let mut bytes = BytesMut::new();
808        bytes.extend_from_slice(&b"$-1\r\n"[..]);
809
810        let mut codec = RespCodec;
811        let deserialized = codec.decode(&mut bytes).unwrap().unwrap();
812        assert_eq!(deserialized, RespValue::Nil);
813    }
814
815    #[test]
816    fn test_integer_overflow() {
817        let resp_object = RespValue::Integer(i64::max_value());
818        let res = i32::from_resp(resp_object);
819        assert!(res.is_err());
820    }
821
822    #[test]
823    fn test_integer_underflow() {
824        let resp_object = RespValue::Integer(-2);
825        let res = u64::from_resp(resp_object);
826        assert!(res.is_err());
827    }
828
829    #[test]
830    fn test_integer_convesion() {
831        let resp_object = RespValue::Integer(50);
832        assert_eq!(u32::from_resp(resp_object).unwrap(), 50);
833    }
834
835    #[test]
836    fn test_hashmap_conversion() {
837        let mut expected = HashMap::new();
838        expected.insert("KEY1".to_string(), "VALUE1".to_string());
839        expected.insert("KEY2".to_string(), "VALUE2".to_string());
840
841        let resp_object = RespValue::Array(vec![
842            "KEY1".into(),
843            "VALUE1".into(),
844            "KEY2".into(),
845            "VALUE2".into(),
846        ]);
847        assert_eq!(
848            HashMap::<String, String>::from_resp(resp_object).unwrap(),
849            expected
850        );
851    }
852
853    #[test]
854    fn test_hashmap_conversion_fails_with_odd_length_array() {
855        let resp_object = RespValue::Array(vec![
856            "KEY1".into(),
857            "VALUE1".into(),
858            "KEY2".into(),
859            "VALUE2".into(),
860            "KEY3".into(),
861        ]);
862        let res = HashMap::<String, String>::from_resp(resp_object);
863
864        match res {
865            Err(Error::Resp(_, _)) => {}
866            _ => panic!("Should not be able to convert an odd number of elements to a hashmap"),
867        }
868    }
869}