Skip to main content

rust_cache_server/
protocol.rs

1use std::fmt;
2
3use bytes::{Buf, BytesMut};
4use thiserror::Error;
5
6#[derive(Debug, Error)]
7pub enum RespError {
8    #[error("incomplete frame")]
9    Incomplete,
10    #[error("protocol error: {0}")]
11    Protocol(String),
12}
13
14#[derive(Debug, Clone)]
15pub enum RespFrame {
16    SimpleString(String),
17    Error(String),
18    Integer(i64),
19    BulkString(Option<Vec<u8>>),
20    Array(Option<Vec<RespFrame>>),
21}
22
23impl fmt::Display for RespFrame {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        match self {
26            RespFrame::SimpleString(s) => write!(f, "{}", s),
27            RespFrame::Error(s) => write!(f, "ERR {}", s),
28            RespFrame::Integer(i) => write!(f, "{}", i),
29            RespFrame::BulkString(Some(b)) => write!(f, "{}", String::from_utf8_lossy(b)),
30            RespFrame::BulkString(None) => write!(f, "(nil)"),
31            RespFrame::Array(Some(arr)) => {
32                write!(f, "[")?;
33                for (i, a) in arr.iter().enumerate() {
34                    if i > 0 {
35                        write!(f, ", ")?;
36                    }
37                    write!(f, "{a}")?;
38                }
39                write!(f, "]")
40            }
41            RespFrame::Array(None) => write!(f, "(nil array)"),
42        }
43    }
44}
45
46fn read_line(buf: &mut BytesMut) -> Result<Option<Vec<u8>>, RespError> {
47    if let Some(pos) = buf.windows(2).position(|w| w == b"\r\n") {
48        let mut line = buf.split_to(pos + 2);
49        line.truncate(pos);
50        Ok(Some(line.to_vec()))
51    } else {
52        Ok(None)
53    }
54}
55
56fn parse_isize(line: &[u8]) -> Result<isize, RespError> {
57    let s = std::str::from_utf8(line).map_err(|e| RespError::Protocol(e.to_string()))?;
58    s.parse::<isize>()
59        .map_err(|e| RespError::Protocol(e.to_string()))
60}
61
62pub fn parse_frame(buf: &mut BytesMut) -> Result<Option<RespFrame>, RespError> {
63    if !buf.has_remaining() {
64        return Ok(None);
65    }
66
67    match buf[0] {
68        b'+' | b'-' | b':' => {
69            let line = read_line(buf)?.ok_or(RespError::Incomplete)?;
70            let (ty, data) = line
71                .split_first()
72                .ok_or_else(|| RespError::Protocol("empty line".into()))?;
73            let s =
74                std::str::from_utf8(data).map_err(|e| RespError::Protocol(e.to_string()))?;
75            let frame = match ty {
76                b'+' => RespFrame::SimpleString(s.to_string()),
77                b'-' => RespFrame::Error(s.to_string()),
78                b':' => RespFrame::Integer(
79                    s.parse::<i64>()
80                        .map_err(|e| RespError::Protocol(e.to_string()))?,
81                ),
82                _ => return Err(RespError::Protocol("invalid prefix".into())),
83            };
84            Ok(Some(frame))
85        }
86        b'$' => {
87            let header = read_line(buf)?.ok_or(RespError::Incomplete)?;
88            let len = parse_isize(&header[1..])?;
89            if len < 0 {
90                return Ok(Some(RespFrame::BulkString(None)));
91            }
92            let len = len as usize;
93            if buf.len() < len + 2 {
94                return Err(RespError::Incomplete);
95            }
96            let data = buf.split_to(len).to_vec();
97            if buf.len() < 2 || &buf[..2] != b"\r\n" {
98                return Err(RespError::Protocol("bulkstring missing CRLF".into()));
99            }
100            buf.advance(2);
101            Ok(Some(RespFrame::BulkString(Some(data))))
102        }
103        b'*' => {
104            let header = read_line(buf)?.ok_or(RespError::Incomplete)?;
105            let len = parse_isize(&header[1..])?;
106            if len < 0 {
107                return Ok(Some(RespFrame::Array(None)));
108            }
109            let len = len as usize;
110            let mut items = Vec::with_capacity(len);
111            for _ in 0..len {
112                let frame = parse_frame(buf)?.ok_or(RespError::Incomplete)?;
113                items.push(frame);
114            }
115            Ok(Some(RespFrame::Array(Some(items))))
116        }
117        _ => Err(RespError::Protocol("unknown RESP type".into())),
118    }
119}
120
121pub fn encode_frame(frame: &RespFrame) -> Vec<u8> {
122    match frame {
123        RespFrame::SimpleString(s) => {
124            let mut out = Vec::with_capacity(s.len() + 3);
125            out.extend_from_slice(b"+");
126            out.extend_from_slice(s.as_bytes());
127            out.extend_from_slice(b"\r\n");
128            out
129        }
130        RespFrame::Error(s) => {
131            let mut out = Vec::with_capacity(s.len() + 3);
132            out.extend_from_slice(b"-");
133            out.extend_from_slice(s.as_bytes());
134            out.extend_from_slice(b"\r\n");
135            out
136        }
137        RespFrame::Integer(i) => {
138            let s = i.to_string();
139            let mut out = Vec::with_capacity(s.len() + 3);
140            out.extend_from_slice(b":");
141            out.extend_from_slice(s.as_bytes());
142            out.extend_from_slice(b"\r\n");
143            out
144        }
145        RespFrame::BulkString(None) => b"$-1\r\n".to_vec(),
146        RespFrame::BulkString(Some(b)) => {
147            let len = b.len().to_string();
148            let mut out = Vec::with_capacity(len.len() + b.len() + 5);
149            out.extend_from_slice(b"$");
150            out.extend_from_slice(len.as_bytes());
151            out.extend_from_slice(b"\r\n");
152            out.extend_from_slice(b);
153            out.extend_from_slice(b"\r\n");
154            out
155        }
156        RespFrame::Array(None) => b"*-1\r\n".to_vec(),
157        RespFrame::Array(Some(arr)) => {
158            let len = arr.len().to_string();
159            let mut out = Vec::new();
160            out.extend_from_slice(b"*");
161            out.extend_from_slice(len.as_bytes());
162            out.extend_from_slice(b"\r\n");
163            for f in arr {
164                out.extend_from_slice(&encode_frame(f));
165            }
166            out
167        }
168    }
169}
170
171