Skip to main content

redis/
parser.rs

1// TODO remove this `allow` once `combine` released a fix. Upstream bug: https://github.com/Marwes/combine/issues/372
2// `combine`'s foreign `opaque!` macro trips the nightly-only `semicolon_in_expressions_from_non_local_macros` lint.
3// `unknown_lints` is applied so the other toolchains, which do not know that lint, ignore it instead of failing under `-D warnings`.
4#![allow(unknown_lints)]
5#![allow(
6    semicolon_in_expressions_from_non_local_macros,
7    reason = "This lint is on in nightly since 2026-07-16, but `combine-4.6.7` violates it in `opaque`. As we cannot decorate directly, we allow it for the whole module for now"
8)]
9use std::{
10    io::{self, Read},
11    str,
12};
13
14use crate::errors::{ParsingError, RedisError, Repr, ServerError, ServerErrorKind};
15use crate::types::{PushKind, RedisResult, Value, VerbatimFormat};
16
17use combine::{
18    ParseError, Parser as _, any,
19    error::StreamError,
20    opaque,
21    parser::{
22        byte::{crlf, take_until_bytes},
23        combinator::{AnySendSyncPartialState, any_send_sync_partial_state},
24        range::{recognize, take},
25    },
26    stream::{
27        PointerOffset, RangeStream, StreamErrorFor,
28        decoder::{self, Decoder},
29    },
30    unexpected_any,
31};
32
33const MAX_RECURSE_DEPTH: usize = 100;
34
35fn err_parser(line: &str) -> ServerError {
36    let mut pieces = line.splitn(2, ' ');
37    let kind = match pieces.next().unwrap() {
38        "ERR" => ServerErrorKind::ResponseError,
39        "EXECABORT" => ServerErrorKind::ExecAbort,
40        "LOADING" => ServerErrorKind::BusyLoading,
41        "NOSCRIPT" => ServerErrorKind::NoScript,
42        "MOVED" => ServerErrorKind::Moved,
43        "ASK" => ServerErrorKind::Ask,
44        "TRYAGAIN" => ServerErrorKind::TryAgain,
45        "CLUSTERDOWN" => ServerErrorKind::ClusterDown,
46        "CROSSSLOT" => ServerErrorKind::CrossSlot,
47        "MASTERDOWN" => ServerErrorKind::MasterDown,
48        "READONLY" => ServerErrorKind::ReadOnly,
49        "NOTBUSY" => ServerErrorKind::NotBusy,
50        "NOSUB" => ServerErrorKind::NoSub,
51        "NOPERM" => ServerErrorKind::NoPerm,
52        code => {
53            return ServerError(Repr::Extension {
54                code: code.into(),
55                detail: pieces.next().map(|str| str.into()),
56            });
57        }
58    };
59    let detail = pieces.next().map(|str| str.into());
60    ServerError(Repr::Known { kind, detail })
61}
62
63pub fn get_push_kind(kind: String) -> PushKind {
64    match kind.as_str() {
65        "invalidate" => PushKind::Invalidate,
66        "message" => PushKind::Message,
67        "pmessage" => PushKind::PMessage,
68        "smessage" => PushKind::SMessage,
69        "unsubscribe" => PushKind::Unsubscribe,
70        "punsubscribe" => PushKind::PUnsubscribe,
71        "sunsubscribe" => PushKind::SUnsubscribe,
72        "subscribe" => PushKind::Subscribe,
73        "psubscribe" => PushKind::PSubscribe,
74        "ssubscribe" => PushKind::SSubscribe,
75        _ => PushKind::Other(kind),
76    }
77}
78
79fn value<'a, I>(
80    count: Option<usize>,
81) -> impl combine::Parser<I, Output = Value, PartialState = AnySendSyncPartialState>
82where
83    I: RangeStream<Token = u8, Range = &'a [u8]>,
84    I::Error: combine::ParseError<u8, &'a [u8], I::Position>,
85{
86    let count = count.unwrap_or(1);
87
88    opaque!(any_send_sync_partial_state(
89        any()
90            .then_partial(move |&mut b| {
91                if count > MAX_RECURSE_DEPTH {
92                    combine::unexpected_any("Maximum recursion depth exceeded").left()
93                } else {
94                    combine::value(b).right()
95                }
96            })
97            .then_partial(move |&mut b| {
98                let line = || {
99                    recognize(take_until_bytes(&b"\r\n"[..]).with(take(2).map(|_| ()))).and_then(
100                        |line: &[u8]| {
101                            str::from_utf8(&line[..line.len() - 2])
102                                .map_err(StreamErrorFor::<I>::other)
103                        },
104                    )
105                };
106
107                let simple_string = || {
108                    line().map(|line| {
109                        if line == "OK" {
110                            Value::Okay
111                        } else {
112                            Value::SimpleString(line.into())
113                        }
114                    })
115                };
116
117                let int = || {
118                    line().and_then(|line| {
119                        line.trim().parse::<i64>().map_err(|_| {
120                            StreamErrorFor::<I>::message_static_message(
121                                "Expected integer, got garbage",
122                            )
123                        })
124                    })
125                };
126
127                let bulk_string = || {
128                    int().then_partial(move |size| {
129                        if *size < 0 {
130                            combine::produce(|| Value::Nil).left()
131                        } else {
132                            take(*size as usize)
133                                .map(|bs: &[u8]| Value::BulkString(bs.to_vec()))
134                                .skip(crlf())
135                                .right()
136                        }
137                    })
138                };
139                let blob = || {
140                    int().then_partial(move |size| {
141                        take(*size as usize)
142                            .map(|bs: &[u8]| String::from_utf8_lossy(bs).to_string())
143                            .skip(crlf())
144                    })
145                };
146
147                let array = || {
148                    int().then_partial(move |&mut length| {
149                        if length < 0 {
150                            combine::produce(|| Value::Nil).left()
151                        } else {
152                            let length = length as usize;
153                            combine::count_min_max(length, length, value(Some(count + 1)))
154                                .map(Value::Array)
155                                .right()
156                        }
157                    })
158                };
159
160                let error = || line().map(err_parser);
161                let map = || {
162                    int().then_partial(move |&mut kv_length| {
163                        match (kv_length as usize).checked_mul(2) {
164                            Some(length) => {
165                                combine::count_min_max(length, length, value(Some(count + 1)))
166                                    .map(move |result: Vec<Value>| {
167                                        let mut it = result.into_iter();
168                                        let mut x = vec![];
169                                        for _ in 0..kv_length {
170                                            if let (Some(k), Some(v)) = (it.next(), it.next()) {
171                                                x.push((k, v));
172                                            }
173                                        }
174                                        Value::Map(x)
175                                    })
176                                    .left()
177                            }
178                            None => {
179                                unexpected_any("Attribute key-value length is too large").right()
180                            }
181                        }
182                    })
183                };
184                let attribute = || {
185                    int().then_partial(move |&mut kv_length| {
186                        match (kv_length as usize).checked_mul(2) {
187                            Some(length) => {
188                                // + 1 is for data!
189                                let length = length + 1;
190                                combine::count_min_max(length, length, value(Some(count + 1)))
191                                    .map(move |result: Vec<Value>| {
192                                        let mut it = result.into_iter();
193                                        let mut attributes = vec![];
194                                        for _ in 0..kv_length {
195                                            if let (Some(k), Some(v)) = (it.next(), it.next()) {
196                                                attributes.push((k, v));
197                                            }
198                                        }
199                                        Value::Attribute {
200                                            data: Box::new(it.next().unwrap()),
201                                            attributes,
202                                        }
203                                    })
204                                    .left()
205                            }
206                            None => {
207                                unexpected_any("Attribute key-value length is too large").right()
208                            }
209                        }
210                    })
211                };
212                let set = || {
213                    int().then_partial(move |&mut length| {
214                        if length < 0 {
215                            combine::produce(|| Value::Nil).left()
216                        } else {
217                            let length = length as usize;
218                            combine::count_min_max(length, length, value(Some(count + 1)))
219                                .map(Value::Set)
220                                .right()
221                        }
222                    })
223                };
224                let push = || {
225                    int().then_partial(move |&mut length| {
226                        if length <= 0 {
227                            combine::produce(|| Value::Push {
228                                kind: PushKind::Other("".to_string()),
229                                data: vec![],
230                            })
231                            .left()
232                        } else {
233                            let length = length as usize;
234                            combine::count_min_max(length, length, value(Some(count + 1)))
235                                .and_then(|result: Vec<Value>| {
236                                    let mut it = result.into_iter();
237                                    let first = it.next().unwrap_or(Value::Nil);
238                                    if let Value::BulkString(kind) = first {
239                                        let push_kind = String::from_utf8(kind)
240                                            .map_err(StreamErrorFor::<I>::other)?;
241                                        Ok(Value::Push {
242                                            kind: get_push_kind(push_kind),
243                                            data: it.collect(),
244                                        })
245                                    } else if let Value::SimpleString(kind) = first {
246                                        Ok(Value::Push {
247                                            kind: get_push_kind(kind),
248                                            data: it.collect(),
249                                        })
250                                    } else {
251                                        Err(StreamErrorFor::<I>::message_static_message(
252                                            "parse error when decoding push",
253                                        ))
254                                    }
255                                })
256                                .right()
257                        }
258                    })
259                };
260                let null = || line().map(|_| Value::Nil);
261                let double = || {
262                    line().and_then(|line| {
263                        line.trim()
264                            .parse::<f64>()
265                            .map_err(StreamErrorFor::<I>::other)
266                    })
267                };
268                let boolean = || {
269                    line().and_then(|line: &str| match line {
270                        "t" => Ok(true),
271                        "f" => Ok(false),
272                        _ => Err(StreamErrorFor::<I>::message_static_message(
273                            "Expected boolean, got garbage",
274                        )),
275                    })
276                };
277                let blob_error = || blob().map(|line| err_parser(&line));
278                let verbatim = || {
279                    blob().and_then(|line| {
280                        if let Some((format, text)) = line.split_once(':') {
281                            let format = match format {
282                                "txt" => VerbatimFormat::Text,
283                                "mkd" => VerbatimFormat::Markdown,
284                                x => VerbatimFormat::Unknown(x.to_string()),
285                            };
286                            Ok(Value::VerbatimString {
287                                format,
288                                text: text.to_string(),
289                            })
290                        } else {
291                            Err(StreamErrorFor::<I>::message_static_message(
292                                "parse error when decoding verbatim string",
293                            ))
294                        }
295                    })
296                };
297                let big_number = || {
298                    line().and_then(|line| {
299                        #[cfg(not(feature = "num-bigint"))]
300                        return Ok::<_, StreamErrorFor<I>>(Value::BigNumber(
301                            line.as_bytes().to_vec(),
302                        ));
303                        #[cfg(feature = "num-bigint")]
304                        num_bigint::BigInt::parse_bytes(line.as_bytes(), 10)
305                            .ok_or_else(|| {
306                                StreamErrorFor::<I>::message_static_message(
307                                    "Expected bigint, got garbage",
308                                )
309                            })
310                            .map(Value::BigNumber)
311                    })
312                };
313                combine::dispatch!(b;
314                    b'+' => simple_string(),
315                    b':' => int().map(Value::Int),
316                    b'$' => bulk_string(),
317                    b'*' => array(),
318                    b'%' => map(),
319                    b'|' => attribute(),
320                    b'~' => set(),
321                    b'-' => error().map(Value::ServerError),
322                    b'_' => null(),
323                    b',' => double().map(Value::Double),
324                    b'#' => boolean().map(Value::Boolean),
325                    b'!' => blob_error().map(Value::ServerError),
326                    b'=' => verbatim(),
327                    b'(' => big_number(),
328                    b'>' => push(),
329                    b => combine::unexpected_any(combine::error::Token(b))
330                )
331            })
332    ))
333}
334
335// a macro is needed because of lifetime shenanigans with `decoder`.
336macro_rules! to_redis_err {
337    ($err: expr, $decoder: expr) => {
338        match $err {
339            decoder::Error::Io { error, .. } => error.into(),
340            decoder::Error::Parse(err) => {
341                if err.is_unexpected_end_of_input() {
342                    RedisError::from(io::Error::from(io::ErrorKind::UnexpectedEof))
343                } else {
344                    let err = err
345                        .map_range(|range| format!("{range:?}"))
346                        .map_position(|pos| pos.translate_position($decoder.buffer()))
347                        .to_string();
348                    RedisError::from(ParsingError::from(err))
349                }
350            }
351        }
352    };
353}
354
355#[cfg(feature = "aio")]
356mod aio_support {
357    use super::*;
358
359    use bytes::{Buf, BytesMut};
360    use tokio::io::AsyncRead;
361    use tokio_util::codec::{Decoder, Encoder};
362
363    #[derive(Default)]
364    pub struct ValueCodec {
365        state: AnySendSyncPartialState,
366    }
367
368    impl ValueCodec {
369        fn decode_stream(&mut self, bytes: &mut BytesMut, eof: bool) -> RedisResult<Option<Value>> {
370            let (opt, removed_len) = {
371                let buffer = &bytes[..];
372                let mut stream =
373                    combine::easy::Stream(combine::stream::MaybePartialStream(buffer, !eof));
374                match combine::stream::decode_tokio(value(None), &mut stream, &mut self.state) {
375                    Ok(x) => x,
376                    Err(err) => {
377                        let err = err
378                            .map_position(|pos| pos.translate_position(buffer))
379                            .map_range(|range| format!("{range:?}"))
380                            .to_string();
381                        return Err(RedisError::from(ParsingError::from(err)));
382                    }
383                }
384            };
385
386            bytes.advance(removed_len);
387            match opt {
388                Some(result) => Ok(Some(result)),
389                None => Ok(None),
390            }
391        }
392    }
393
394    impl Encoder<Vec<u8>> for ValueCodec {
395        type Error = RedisError;
396        fn encode(&mut self, item: Vec<u8>, dst: &mut BytesMut) -> Result<(), Self::Error> {
397            dst.extend_from_slice(item.as_ref());
398            Ok(())
399        }
400    }
401
402    impl Decoder for ValueCodec {
403        type Item = Value;
404        type Error = RedisError;
405
406        fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
407            self.decode_stream(src, false)
408        }
409
410        fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
411            self.decode_stream(buf, true)
412        }
413    }
414
415    /// Parses a redis value asynchronously.
416    pub async fn parse_redis_value_async<R>(
417        decoder: &mut combine::stream::Decoder<AnySendSyncPartialState, PointerOffset<[u8]>>,
418        read: &mut R,
419    ) -> RedisResult<Value>
420    where
421        R: AsyncRead + std::marker::Unpin,
422    {
423        let result = combine::decode_tokio!(*decoder, *read, value(None), |input, _| {
424            combine::stream::easy::Stream::from(input)
425        });
426        match result {
427            Err(err) => Err(to_redis_err!(err, decoder)),
428            Ok(result) => Ok(result),
429        }
430    }
431}
432
433#[cfg(feature = "aio")]
434#[cfg_attr(docsrs, doc(cfg(feature = "aio")))]
435pub use self::aio_support::*;
436
437/// The internal redis response parser.
438pub struct Parser {
439    decoder: Decoder<AnySendSyncPartialState, PointerOffset<[u8]>>,
440}
441
442impl Default for Parser {
443    fn default() -> Self {
444        Self::new()
445    }
446}
447
448/// The parser can be used to parse redis responses into values.  Generally
449/// you normally do not use this directly as it's already done for you by
450/// the client but in some more complex situations it might be useful to be
451/// able to parse the redis responses.
452impl Parser {
453    /// Creates a new parser that parses the data behind the reader.  More
454    /// than one value can be behind the reader in which case the parser can
455    /// be invoked multiple times.  In other words: the stream does not have
456    /// to be terminated.
457    pub fn new() -> Self {
458        Self {
459            decoder: Decoder::new(),
460        }
461    }
462
463    // public api
464
465    /// Parses synchronously into a single value from the reader.
466    pub fn parse_value<T: Read>(&mut self, mut reader: T) -> RedisResult<Value> {
467        let mut decoder = &mut self.decoder;
468        let result = combine::decode!(decoder, reader, value(None), |input, _| {
469            combine::stream::easy::Stream::from(input)
470        });
471        match result {
472            Err(err) => Err(to_redis_err!(err, decoder)),
473            Ok(result) => Ok(result),
474        }
475    }
476}
477
478/// Parses bytes into a redis value.
479///
480/// This is the most straightforward way to parse something into a low
481/// level redis value instead of having to use a whole parser.
482pub fn parse_redis_value(bytes: &[u8]) -> RedisResult<Value> {
483    let mut parser = Parser::new();
484    parser.parse_value(bytes)
485}
486
487#[cfg(test)]
488mod tests {
489    use super::*;
490    use crate::errors::ErrorKind;
491    use assert_matches::assert_matches;
492
493    #[cfg(feature = "aio")]
494    #[test]
495    fn decode_eof_returns_none_at_eof() {
496        use tokio_util::codec::Decoder;
497        let mut codec = ValueCodec::default();
498
499        let mut bytes = bytes::BytesMut::from(&b"+GET 123\r\n"[..]);
500        assert_eq!(
501            codec.decode_eof(&mut bytes),
502            Ok(Some(parse_redis_value(b"+GET 123\r\n").unwrap()))
503        );
504        assert_eq!(codec.decode_eof(&mut bytes), Ok(None));
505        assert_eq!(codec.decode_eof(&mut bytes), Ok(None));
506    }
507
508    #[cfg(feature = "aio")]
509    #[test]
510    fn decode_eof_returns_error_inside_array_and_can_parse_more_inputs() {
511        use tokio_util::codec::Decoder;
512        let mut codec = ValueCodec::default();
513
514        let mut bytes =
515            bytes::BytesMut::from(b"*3\r\n+OK\r\n-LOADING server is loading\r\n+OK\r\n".as_slice());
516        let result = codec.decode_eof(&mut bytes).unwrap().unwrap();
517
518        assert_eq!(
519            result,
520            Value::Array(vec![
521                Value::Okay,
522                Value::ServerError(ServerError(Repr::Known {
523                    kind: ServerErrorKind::BusyLoading,
524                    detail: Some(arcstr::literal!("server is loading"))
525                })),
526                Value::Okay
527            ])
528        );
529
530        let mut bytes = bytes::BytesMut::from(b"+OK\r\n".as_slice());
531        let result = codec.decode_eof(&mut bytes).unwrap().unwrap();
532
533        assert_eq!(result, Value::Okay);
534    }
535
536    #[test]
537    fn parse_nested_error_and_handle_more_inputs() {
538        // from https://redis.io/docs/interact/transactions/ -
539        // "EXEC returned two-element bulk string reply where one is an OK code and the other an error reply. It's up to the client library to find a sensible way to provide the error to the user."
540
541        let bytes = b"*3\r\n+OK\r\n-LOADING server is loading\r\n+OK\r\n";
542        let result = parse_redis_value(bytes);
543
544        assert_eq!(
545            result.unwrap(),
546            Value::Array(vec![
547                Value::Okay,
548                Value::ServerError(ServerError(Repr::Known {
549                    kind: ServerErrorKind::BusyLoading,
550                    detail: Some(arcstr::literal!("server is loading"))
551                })),
552                Value::Okay
553            ])
554        );
555
556        let result = parse_redis_value(b"+OK\r\n").unwrap();
557
558        assert_eq!(result, Value::Okay);
559    }
560
561    #[test]
562    fn decode_resp3_double() {
563        let val = parse_redis_value(b",1.23\r\n").unwrap();
564        assert_eq!(val, Value::Double(1.23));
565        let val = parse_redis_value(b",nan\r\n").unwrap();
566        if let Value::Double(val) = val {
567            assert!(val.is_sign_positive());
568            assert!(val.is_nan());
569        } else {
570            panic!("expected double");
571        }
572        // -nan is supported prior to redis 7.2
573        let val = parse_redis_value(b",-nan\r\n").unwrap();
574        if let Value::Double(val) = val {
575            assert!(val.is_sign_negative());
576            assert!(val.is_nan());
577        } else {
578            panic!("expected double");
579        }
580        //Allow doubles in scientific E notation
581        let val = parse_redis_value(b",2.67923e+8\r\n").unwrap();
582        assert_eq!(val, Value::Double(267923000.0));
583        let val = parse_redis_value(b",2.67923E+8\r\n").unwrap();
584        assert_eq!(val, Value::Double(267923000.0));
585        let val = parse_redis_value(b",-2.67923E+8\r\n").unwrap();
586        assert_eq!(val, Value::Double(-267923000.0));
587        let val = parse_redis_value(b",2.1E-2\r\n").unwrap();
588        assert_eq!(val, Value::Double(0.021));
589
590        let val = parse_redis_value(b",-inf\r\n").unwrap();
591        assert_eq!(val, Value::Double(-f64::INFINITY));
592        let val = parse_redis_value(b",inf\r\n").unwrap();
593        assert_eq!(val, Value::Double(f64::INFINITY));
594    }
595
596    #[test]
597    fn decode_resp3_map() {
598        let val = parse_redis_value(b"%2\r\n+first\r\n:1\r\n+second\r\n:2\r\n").unwrap();
599        let mut v = val.as_map_iter().unwrap();
600        assert_eq!(
601            (&Value::SimpleString("first".to_string()), &Value::Int(1)),
602            v.next().unwrap()
603        );
604        assert_eq!(
605            (&Value::SimpleString("second".to_string()), &Value::Int(2)),
606            v.next().unwrap()
607        );
608    }
609
610    #[test]
611    fn decode_resp3_boolean() {
612        let val = parse_redis_value(b"#t\r\n").unwrap();
613        assert_eq!(val, Value::Boolean(true));
614        let val = parse_redis_value(b"#f\r\n").unwrap();
615        assert_eq!(val, Value::Boolean(false));
616        let val = parse_redis_value(b"#x\r\n");
617        assert_matches!(val, Err(_));
618        let val = parse_redis_value(b"#\r\n");
619        assert_matches!(val, Err(_));
620    }
621
622    #[test]
623    fn decode_resp3_blob_error() {
624        let val = parse_redis_value(b"!21\r\nSYNTAX invalid syntax\r\n");
625        assert_eq!(
626            val.unwrap(),
627            Value::ServerError(ServerError(Repr::Extension {
628                code: arcstr::literal!("SYNTAX"),
629                detail: Some(arcstr::literal!("invalid syntax"))
630            }))
631        );
632    }
633
634    #[test]
635    fn decode_resp3_big_number() {
636        let val = parse_redis_value(b"(3492890328409238509324850943850943825024385\r\n").unwrap();
637        #[cfg(feature = "num-bigint")]
638        let expected = Value::BigNumber(
639            num_bigint::BigInt::parse_bytes(b"3492890328409238509324850943850943825024385", 10)
640                .unwrap(),
641        );
642        #[cfg(not(feature = "num-bigint"))]
643        let expected = Value::BigNumber(b"3492890328409238509324850943850943825024385".to_vec());
644        assert_eq!(val, expected);
645    }
646
647    #[test]
648    fn decode_resp3_set() {
649        let val = parse_redis_value(b"~5\r\n+orange\r\n+apple\r\n#t\r\n:100\r\n:999\r\n").unwrap();
650        let v = val.as_sequence().unwrap();
651        assert!(v.len() >= 5);
652        assert_eq!(Value::SimpleString("orange".to_string()), v[0]);
653        assert_eq!(Value::SimpleString("apple".to_string()), v[1]);
654        assert_eq!(Value::Boolean(true), v[2]);
655        assert_eq!(Value::Int(100), v[3]);
656        assert_eq!(Value::Int(999), v[4]);
657    }
658
659    #[test]
660    fn decode_resp3_push() {
661        let val = parse_redis_value(b">3\r\n+message\r\n+somechannel\r\n+this is the message\r\n")
662            .unwrap();
663        if let Value::Push { ref kind, ref data } = val {
664            assert_eq!(&PushKind::Message, kind);
665            assert_eq!(Value::SimpleString("somechannel".to_string()), data[0]);
666            assert_eq!(
667                Value::SimpleString("this is the message".to_string()),
668                data[1]
669            );
670        } else {
671            panic!("Expected Value::Push")
672        }
673    }
674
675    #[test]
676    fn test_max_recursion_depth_set_and_array() {
677        for test_byte in ["*", "~"] {
678            let initial = format!("{test_byte}1\r\n").as_bytes().to_vec();
679            let end = format!("{test_byte}0\r\n").as_bytes().to_vec();
680
681            let mut ba = initial.repeat(MAX_RECURSE_DEPTH - 1).to_vec();
682            ba.extend(end.clone());
683            match parse_redis_value(&ba) {
684                Ok(Value::Array(a)) => assert_eq!(a.len(), 1),
685                Ok(Value::Set(s)) => assert_eq!(s.len(), 1),
686                _ => panic!("Expected valid array or set"),
687            }
688
689            let mut ba = initial.repeat(MAX_RECURSE_DEPTH).to_vec();
690            ba.extend(end);
691            match parse_redis_value(&ba) {
692                Ok(_) => panic!("Expected ParseError"),
693                Err(e) => assert_matches!(e.kind(), ErrorKind::Parse),
694            }
695        }
696    }
697
698    #[test]
699    fn test_max_recursion_depth_map() {
700        let initial = b"%1\r\n+a\r\n";
701        let end = b"%0\r\n";
702
703        let mut ba = initial.repeat(MAX_RECURSE_DEPTH - 1).to_vec();
704        ba.extend(*end);
705        match parse_redis_value(&ba) {
706            Ok(Value::Map(m)) => assert_eq!(m.len(), 1),
707            Ok(Value::Set(s)) => assert_eq!(s.len(), 1),
708            _ => panic!("Expected valid array or set"),
709        }
710
711        let mut ba = initial.repeat(MAX_RECURSE_DEPTH).to_vec();
712        ba.extend(end);
713        match parse_redis_value(&ba) {
714            Ok(_) => panic!("Expected ParseError"),
715            Err(e) => assert_matches!(e.kind(), ErrorKind::Parse),
716        }
717    }
718}