1use crate::error::ProtocolError;
12use crate::request::{find_crlf, parse_int};
13
14#[derive(Debug, Clone, PartialEq)]
26pub enum Reply {
27 Simple(Vec<u8>),
29 Error(Vec<u8>),
31 Int(i64),
33 Bulk(Vec<u8>),
35 Nil,
38 Array(Vec<Reply>),
40 Map(Vec<(Reply, Reply)>),
45 Set(Vec<Reply>),
48 Double(f64),
51 Boolean(bool),
53 Verbatim {
57 fmt: [u8; 3],
59 data: Vec<u8>,
61 },
62 BigNumber(Vec<u8>),
66 Null,
68 Push(Vec<Reply>),
72 BlobError(Vec<u8>),
76}
77
78pub fn parse_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
90 let Some(&tag) = buf.first() else {
91 return Ok(None);
92 };
93 match tag {
94 b'+' => Ok(reply_line(buf).map(|(b, used)| (Reply::Simple(b.to_vec()), used))),
95 b'-' => Ok(reply_line(buf).map(|(b, used)| (Reply::Error(b.to_vec()), used))),
96 b':' => match reply_line(buf) {
97 None => Ok(None),
98 Some((b, used)) => {
99 let n = parse_int(b).ok_or(ProtocolError::Malformed("bad integer reply"))?;
100 Ok(Some((Reply::Int(n), used)))
101 }
102 },
103 b'$' => parse_bulk_reply(buf),
104 b'*' => parse_array_reply(buf, false),
105 b'%' => parse_map_reply(buf),
107 b'~' => parse_set_reply(buf),
108 b',' => parse_double_reply(buf),
109 b'#' => parse_boolean_reply(buf),
110 b'=' => parse_verbatim_reply(buf),
111 b'(' => match reply_line(buf) {
112 None => Ok(None),
113 Some((b, used)) => Ok(Some((Reply::BigNumber(b.to_vec()), used))),
114 },
115 b'_' => parse_null_reply(buf),
116 b'>' => parse_array_reply(buf, true),
117 b'!' => parse_blob_error_reply(buf),
118 b'|' => parse_attributed_reply(buf),
119 _ => Err(ProtocolError::Malformed("unknown reply type")),
120 }
121}
122
123fn reply_line(buf: &[u8]) -> Option<(&[u8], usize)> {
125 find_crlf(buf, 1).map(|eol| (&buf[1..eol], eol + 2))
126}
127
128fn parse_bulk_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
129 let Some(hdr_end) = find_crlf(buf, 1) else {
130 return Ok(None);
131 };
132 let len = parse_int(&buf[1..hdr_end]).ok_or(ProtocolError::Malformed("bad bulk length"))?;
133 if len < 0 {
134 return Ok(Some((Reply::Nil, hdr_end + 2)));
135 }
136 let data_start = hdr_end + 2;
137 let data_end = data_start + len as usize;
138 if buf.len() < data_end + 2 {
139 return Ok(None);
140 }
141 Ok(Some((Reply::Bulk(buf[data_start..data_end].to_vec()), data_end + 2)))
142}
143
144fn parse_array_reply(buf: &[u8], push: bool) -> Result<Option<(Reply, usize)>, ProtocolError> {
149 let Some(hdr_end) = find_crlf(buf, 1) else {
150 return Ok(None);
151 };
152 let count = parse_int(&buf[1..hdr_end]).ok_or(ProtocolError::Malformed("bad array length"))?;
153 if count < 0 {
154 if push {
155 return Err(ProtocolError::Malformed("push frame cannot be null"));
156 }
157 return Ok(Some((Reply::Nil, hdr_end + 2)));
158 }
159 let mut pos = hdr_end + 2;
160 let cap = (count as usize).min(buf.len().saturating_sub(pos));
167 let mut items = Vec::with_capacity(cap);
168 for _ in 0..count {
169 match parse_reply(&buf[pos..])? {
170 None => return Ok(None),
171 Some((r, used)) => {
172 items.push(r);
173 pos += used;
174 }
175 }
176 }
177 let reply = if push { Reply::Push(items) } else { Reply::Array(items) };
178 Ok(Some((reply, pos)))
179}
180
181fn parse_map_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
183 let Some(hdr_end) = find_crlf(buf, 1) else {
184 return Ok(None);
185 };
186 let count = parse_int(&buf[1..hdr_end]).ok_or(ProtocolError::Malformed("bad map length"))?;
187 if count < 0 {
188 return Err(ProtocolError::Malformed("map length cannot be negative"));
189 }
190 let mut pos = hdr_end + 2;
191 let cap = (count as usize).min(buf.len().saturating_sub(pos) / 2);
193 let mut pairs: Vec<(Reply, Reply)> = Vec::with_capacity(cap);
194 for _ in 0..count {
195 let Some((k, used_k)) = parse_reply(&buf[pos..])? else {
196 return Ok(None);
197 };
198 pos += used_k;
199 let Some((v, used_v)) = parse_reply(&buf[pos..])? else {
200 return Ok(None);
201 };
202 pos += used_v;
203 pairs.push((k, v));
204 }
205 Ok(Some((Reply::Map(pairs), pos)))
206}
207
208fn parse_set_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
210 let Some(hdr_end) = find_crlf(buf, 1) else {
211 return Ok(None);
212 };
213 let count = parse_int(&buf[1..hdr_end]).ok_or(ProtocolError::Malformed("bad set length"))?;
214 if count < 0 {
215 return Err(ProtocolError::Malformed("set length cannot be negative"));
216 }
217 let mut pos = hdr_end + 2;
218 let cap = (count as usize).min(buf.len().saturating_sub(pos));
219 let mut items = Vec::with_capacity(cap);
220 for _ in 0..count {
221 match parse_reply(&buf[pos..])? {
222 None => return Ok(None),
223 Some((r, used)) => {
224 items.push(r);
225 pos += used;
226 }
227 }
228 }
229 Ok(Some((Reply::Set(items), pos)))
230}
231
232fn parse_double_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
235 let Some((bytes, used)) = reply_line(buf) else {
236 return Ok(None);
237 };
238 let s = std::str::from_utf8(bytes).map_err(|_| ProtocolError::Malformed("bad double utf8"))?;
239 let v: f64 = s.parse().map_err(|_| ProtocolError::Malformed("bad double"))?;
240 Ok(Some((Reply::Double(v), used)))
241}
242
243fn parse_boolean_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
245 let Some((bytes, used)) = reply_line(buf) else {
246 return Ok(None);
247 };
248 let v = match bytes {
249 b"t" => true,
250 b"f" => false,
251 _ => return Err(ProtocolError::Malformed("bad boolean payload")),
252 };
253 Ok(Some((Reply::Boolean(v), used)))
254}
255
256fn parse_verbatim_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
259 let Some(hdr_end) = find_crlf(buf, 1) else {
260 return Ok(None);
261 };
262 let len = parse_int(&buf[1..hdr_end]).ok_or(ProtocolError::Malformed("bad verbatim length"))?;
263 if len < 4 {
264 return Err(ProtocolError::Malformed("verbatim length < 4 (fmt + ':')"));
265 }
266 let data_start = hdr_end + 2;
267 let data_end = data_start + len as usize;
268 if buf.len() < data_end + 2 {
269 return Ok(None);
270 }
271 let body = &buf[data_start..data_end];
272 if body[3] != b':' {
273 return Err(ProtocolError::Malformed("verbatim missing fmt:data separator"));
274 }
275 let mut fmt = [0u8; 3];
276 fmt.copy_from_slice(&body[..3]);
277 let data = body[4..].to_vec();
278 Ok(Some((Reply::Verbatim { fmt, data }, data_end + 2)))
279}
280
281fn parse_null_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
283 if buf.len() < 3 {
284 return Ok(None);
285 }
286 if &buf[..3] != b"_\r\n" {
287 return Err(ProtocolError::Malformed("bad null payload"));
288 }
289 Ok(Some((Reply::Null, 3)))
290}
291
292fn parse_blob_error_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
294 let Some(hdr_end) = find_crlf(buf, 1) else {
295 return Ok(None);
296 };
297 let len =
298 parse_int(&buf[1..hdr_end]).ok_or(ProtocolError::Malformed("bad blob error length"))?;
299 if len < 0 {
300 return Err(ProtocolError::Malformed("blob error length cannot be negative"));
301 }
302 let data_start = hdr_end + 2;
303 let data_end = data_start + len as usize;
304 if buf.len() < data_end + 2 {
305 return Ok(None);
306 }
307 Ok(Some((Reply::BlobError(buf[data_start..data_end].to_vec()), data_end + 2)))
308}
309
310fn parse_attributed_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
315 let Some((_attrs, used_attrs)) = parse_map_reply(buf)? else {
318 return Ok(None);
319 };
320 match parse_reply(&buf[used_attrs..])? {
321 None => Ok(None),
322 Some((r, used)) => Ok(Some((r, used_attrs + used))),
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 #[test]
331 fn parse_replies() {
332 let r = |b: &[u8]| parse_reply(b).unwrap().unwrap().0;
333 assert_eq!(r(b"+OK\r\n"), Reply::Simple(b"OK".to_vec()));
334 assert_eq!(r(b"-ERR bad\r\n"), Reply::Error(b"ERR bad".to_vec()));
335 assert_eq!(r(b":42\r\n"), Reply::Int(42));
336 assert_eq!(r(b"$5\r\nhello\r\n"), Reply::Bulk(b"hello".to_vec()));
337 assert_eq!(r(b"$-1\r\n"), Reply::Nil);
338 assert_eq!(r(b"*-1\r\n"), Reply::Nil);
339
340 let (arr, used) = parse_reply(b"*2\r\n:1\r\n$2\r\nhi\r\n").unwrap().unwrap();
341 assert_eq!(arr, Reply::Array(vec![Reply::Int(1), Reply::Bulk(b"hi".to_vec())]));
342 assert_eq!(used, 16);
343
344 assert_eq!(parse_reply(b"$5\r\nhel").unwrap(), None);
346 assert_eq!(parse_reply(b"*2\r\n:1\r\n").unwrap(), None);
347 assert!(parse_reply(b"@huh\r\n").is_err());
350 }
351
352 #[test]
353 fn parse_resp3_scalars() {
354 let r = |b: &[u8]| parse_reply(b).unwrap().unwrap().0;
355 assert_eq!(r(b"_\r\n"), Reply::Null);
356 assert_eq!(r(b"#t\r\n"), Reply::Boolean(true));
357 assert_eq!(r(b"#f\r\n"), Reply::Boolean(false));
358 assert_eq!(r(b",1.5\r\n"), Reply::Double(1.5));
359 assert_eq!(r(b",inf\r\n"), Reply::Double(f64::INFINITY));
360 assert_eq!(r(b",-inf\r\n"), Reply::Double(f64::NEG_INFINITY));
361 match r(b",nan\r\n") {
363 Reply::Double(v) => assert!(v.is_nan()),
364 other => panic!("expected Double(nan), got {other:?}"),
365 }
366 assert_eq!(
367 r(b"(170141183460469231731687303715884105727\r\n"),
368 Reply::BigNumber(b"170141183460469231731687303715884105727".to_vec())
369 );
370 assert_eq!(r(b"!11\r\nERR bad cmd\r\n"), Reply::BlobError(b"ERR bad cmd".to_vec()));
371 }
372
373 #[test]
374 fn parse_resp3_verbatim() {
375 let r = |b: &[u8]| parse_reply(b).unwrap().unwrap().0;
376 assert_eq!(
377 r(b"=15\r\ntxt:Some string\r\n"),
378 Reply::Verbatim { fmt: *b"txt", data: b"Some string".to_vec() }
379 );
380 assert!(parse_reply(b"=3\r\ntxt\r\n").is_err());
382 assert!(parse_reply(b"=7\r\ntxt+abc\r\n").is_err());
384 }
385
386 #[test]
387 fn parse_resp3_map_and_set() {
388 let r = |b: &[u8]| parse_reply(b).unwrap().unwrap().0;
389 let m = r(b"%2\r\n:1\r\n$1\r\na\r\n:2\r\n$1\r\nb\r\n");
391 assert_eq!(
392 m,
393 Reply::Map(vec![
394 (Reply::Int(1), Reply::Bulk(b"a".to_vec())),
395 (Reply::Int(2), Reply::Bulk(b"b".to_vec())),
396 ])
397 );
398 let s = r(b"~3\r\n:1\r\n:2\r\n:3\r\n");
400 assert_eq!(s, Reply::Set(vec![Reply::Int(1), Reply::Int(2), Reply::Int(3)]));
401 assert_eq!(r(b"%0\r\n"), Reply::Map(vec![]));
403 assert_eq!(r(b"~0\r\n"), Reply::Set(vec![]));
404 assert!(parse_reply(b"%-1\r\n").is_err());
406 assert!(parse_reply(b"~-1\r\n").is_err());
407 }
408
409 #[test]
410 fn parse_resp3_push_frame() {
411 let r = |b: &[u8]| parse_reply(b).unwrap().unwrap().0;
412 let push = r(b">3\r\n+message\r\n$4\r\nnews\r\n$5\r\nhello\r\n");
413 assert_eq!(
414 push,
415 Reply::Push(vec![
416 Reply::Simple(b"message".to_vec()),
417 Reply::Bulk(b"news".to_vec()),
418 Reply::Bulk(b"hello".to_vec()),
419 ])
420 );
421 assert!(parse_reply(b">-1\r\n").is_err());
423 }
424
425 #[test]
426 fn parse_resp3_attributes_are_skipped() {
427 let frame =
430 b"|1\r\n+key-popularity\r\n%2\r\n$1\r\na\r\n,0.5\r\n$1\r\nb\r\n,0.3\r\n*2\r\n:1\r\n:2\r\n";
431 let (r, used) = parse_reply(frame).unwrap().unwrap();
432 assert_eq!(r, Reply::Array(vec![Reply::Int(1), Reply::Int(2)]));
433 assert_eq!(used, frame.len());
434 }
435
436 #[test]
437 fn parse_resp3_partial_returns_none() {
438 for cut in &[b"_".as_slice(), b"_\r", b"#t", b"#t\r"] {
440 assert_eq!(parse_reply(cut).unwrap(), None);
441 }
442 assert_eq!(parse_reply(b"=15\r\ntxt:Some str").unwrap(), None);
443 assert_eq!(parse_reply(b"%2\r\n:1\r\n$1\r\na\r\n:2\r\n").unwrap(), None);
445 }
446}