redis_parser/
resp2.rs

1/// # RESP2
2/// This module provides utilities to parse the RESP2 protocol.
3use nom::branch::alt;
4use nom::multi::many_m_n;
5use nom::{bytes::streaming::tag, IResult};
6
7use crate::utils::{parse_bytes_with_length, parse_integer_with_prefix, parse_str_with_prefix};
8
9/// Resp2Type represents all possible response types from the RESP2 protocol.
10#[derive(Debug, PartialEq)]
11pub enum Resp2Type<'a> {
12    String(&'a str),
13    Error(&'a str),
14    Integer(usize),
15    BulkString(&'a [u8]),
16    Null,
17    Array(Vec<Resp2Type<'a>>),
18}
19
20fn parse_simple_string(input: &[u8]) -> IResult<&[u8], Resp2Type> {
21    let (input, string) = parse_str_with_prefix(input, '+')?;
22
23    Ok((input, Resp2Type::String(string)))
24}
25
26fn parse_error(input: &[u8]) -> IResult<&[u8], Resp2Type> {
27    let (input, string) = parse_str_with_prefix(input, '-')?;
28
29    Ok((input, Resp2Type::Error(string)))
30}
31
32fn parse_integer(input: &[u8]) -> IResult<&[u8], Resp2Type> {
33    let (input, int) = parse_integer_with_prefix(input, ':')?;
34
35    Ok((input, Resp2Type::Integer(int)))
36}
37
38fn parse_bulk_string(input: &[u8]) -> IResult<&[u8], Resp2Type> {
39    let (input, length) = parse_integer_with_prefix(input, '$')?;
40    let (input, bytes) = parse_bytes_with_length(input, length)?;
41
42    Ok((input, Resp2Type::BulkString(bytes)))
43}
44
45fn parse_null_string(input: &[u8]) -> IResult<&[u8], Resp2Type> {
46    let (input, _) = tag("$-1\r\n")(input)?;
47
48    Ok((input, Resp2Type::Null))
49}
50
51fn parse_array(input: &[u8]) -> IResult<&[u8], Resp2Type> {
52    let (input, length) = parse_integer_with_prefix(input, '*')?;
53    let (input, result) = many_m_n(length, length, parse)(input)?;
54
55    Ok((input, Resp2Type::Array(result)))
56}
57
58/// Parse bytes into a `Resp2Type` enum
59pub fn parse(input: &[u8]) -> IResult<&[u8], Resp2Type> {
60    alt((
61        parse_simple_string,
62        parse_error,
63        parse_integer,
64        parse_bulk_string,
65        parse_null_string,
66        parse_array,
67    ))(input)
68}
69
70#[cfg(test)]
71mod tests {
72    use crate::resp2::{parse, Resp2Type};
73
74    #[test]
75    fn test_parse_simple_string() {
76        assert_eq!(
77            parse(&b"+OK\r\n"[..]),
78            Ok((&b""[..], Resp2Type::String("OK")))
79        );
80    }
81
82    #[test]
83    fn test_parse_error() {
84        assert_eq!(
85            parse(&b"-Error message\r\n"[..]),
86            Ok((&b""[..], Resp2Type::Error("Error message")))
87        );
88    }
89
90    #[test]
91    fn test_parse_integer() {
92        assert_eq!(
93            parse(&b":100\r\n"[..]),
94            Ok((&b""[..], Resp2Type::Integer(100)))
95        );
96    }
97
98    #[test]
99    fn test_parse_bulk_string() {
100        assert_eq!(
101            parse(&b"$10\r\n1234567890\r\n"[..]),
102            Ok((&b""[..], Resp2Type::BulkString(&b"1234567890"[..])))
103        );
104    }
105
106    #[test]
107    fn test_parse_null_string() {
108        assert_eq!(parse(&b"$-1\r\n"[..]), Ok((&b""[..], Resp2Type::Null)));
109    }
110
111    #[test]
112    fn test_parse_array_empty() {
113        assert_eq!(
114            parse(&b"*0\r\n"[..]),
115            Ok((&b""[..], Resp2Type::Array(vec![])))
116        );
117    }
118
119    #[test]
120    fn test_parse_array_mixed_objecs() {
121        assert_eq!(
122            parse(&b"*3\r\n:1\r\n:2\r\n$3\r\nfoo\r\n"[..]),
123            Ok((
124                &b""[..],
125                Resp2Type::Array(vec![
126                    Resp2Type::Integer(1),
127                    Resp2Type::Integer(2),
128                    Resp2Type::BulkString(&b"foo"[..])
129                ])
130            ))
131        );
132    }
133}