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((
142 Reply::Bulk(buf[data_start..data_end].to_vec()),
143 data_end + 2,
144 )))
145}
146
147fn parse_array_reply(buf: &[u8], push: bool) -> Result<Option<(Reply, usize)>, ProtocolError> {
152 let Some(hdr_end) = find_crlf(buf, 1) else {
153 return Ok(None);
154 };
155 let count = parse_int(&buf[1..hdr_end]).ok_or(ProtocolError::Malformed("bad array length"))?;
156 if count < 0 {
157 if push {
158 return Err(ProtocolError::Malformed("push frame cannot be null"));
159 }
160 return Ok(Some((Reply::Nil, hdr_end + 2)));
161 }
162 let mut pos = hdr_end + 2;
163 let cap = (count as usize).min(buf.len().saturating_sub(pos));
171 let mut items = Vec::with_capacity(cap);
172 for _ in 0..count {
173 match parse_reply(&buf[pos..])? {
174 None => return Ok(None),
175 Some((r, used)) => {
176 items.push(r);
177 pos += used;
178 }
179 }
180 }
181 let reply = if push { Reply::Push(items) } else { Reply::Array(items) };
182 Ok(Some((reply, pos)))
183}
184
185fn parse_map_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
187 let Some(hdr_end) = find_crlf(buf, 1) else {
188 return Ok(None);
189 };
190 let count = parse_int(&buf[1..hdr_end]).ok_or(ProtocolError::Malformed("bad map length"))?;
191 if count < 0 {
192 return Err(ProtocolError::Malformed("map length cannot be negative"));
193 }
194 let mut pos = hdr_end + 2;
195 let cap = (count as usize).min(buf.len().saturating_sub(pos) / 2);
197 let mut pairs: Vec<(Reply, Reply)> = Vec::with_capacity(cap);
198 for _ in 0..count {
199 let Some((k, used_k)) = parse_reply(&buf[pos..])? else {
200 return Ok(None);
201 };
202 pos += used_k;
203 let Some((v, used_v)) = parse_reply(&buf[pos..])? else {
204 return Ok(None);
205 };
206 pos += used_v;
207 pairs.push((k, v));
208 }
209 Ok(Some((Reply::Map(pairs), pos)))
210}
211
212fn parse_set_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
214 let Some(hdr_end) = find_crlf(buf, 1) else {
215 return Ok(None);
216 };
217 let count = parse_int(&buf[1..hdr_end]).ok_or(ProtocolError::Malformed("bad set length"))?;
218 if count < 0 {
219 return Err(ProtocolError::Malformed("set length cannot be negative"));
220 }
221 let mut pos = hdr_end + 2;
222 let cap = (count as usize).min(buf.len().saturating_sub(pos));
223 let mut items = Vec::with_capacity(cap);
224 for _ in 0..count {
225 match parse_reply(&buf[pos..])? {
226 None => return Ok(None),
227 Some((r, used)) => {
228 items.push(r);
229 pos += used;
230 }
231 }
232 }
233 Ok(Some((Reply::Set(items), pos)))
234}
235
236fn parse_double_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
239 let Some((bytes, used)) = reply_line(buf) else {
240 return Ok(None);
241 };
242 let s = std::str::from_utf8(bytes).map_err(|_| ProtocolError::Malformed("bad double utf8"))?;
243 let v: f64 = s.parse().map_err(|_| ProtocolError::Malformed("bad double"))?;
244 Ok(Some((Reply::Double(v), used)))
245}
246
247fn parse_boolean_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
249 let Some((bytes, used)) = reply_line(buf) else {
250 return Ok(None);
251 };
252 let v = match bytes {
253 b"t" => true,
254 b"f" => false,
255 _ => return Err(ProtocolError::Malformed("bad boolean payload")),
256 };
257 Ok(Some((Reply::Boolean(v), used)))
258}
259
260fn parse_verbatim_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
263 let Some(hdr_end) = find_crlf(buf, 1) else {
264 return Ok(None);
265 };
266 let len = parse_int(&buf[1..hdr_end])
267 .ok_or(ProtocolError::Malformed("bad verbatim length"))?;
268 if len < 4 {
269 return Err(ProtocolError::Malformed("verbatim length < 4 (fmt + ':')"));
270 }
271 let data_start = hdr_end + 2;
272 let data_end = data_start + len as usize;
273 if buf.len() < data_end + 2 {
274 return Ok(None);
275 }
276 let body = &buf[data_start..data_end];
277 if body[3] != b':' {
278 return Err(ProtocolError::Malformed("verbatim missing fmt:data separator"));
279 }
280 let mut fmt = [0u8; 3];
281 fmt.copy_from_slice(&body[..3]);
282 let data = body[4..].to_vec();
283 Ok(Some((Reply::Verbatim { fmt, data }, data_end + 2)))
284}
285
286fn parse_null_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
288 if buf.len() < 3 {
289 return Ok(None);
290 }
291 if &buf[..3] != b"_\r\n" {
292 return Err(ProtocolError::Malformed("bad null payload"));
293 }
294 Ok(Some((Reply::Null, 3)))
295}
296
297fn parse_blob_error_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
299 let Some(hdr_end) = find_crlf(buf, 1) else {
300 return Ok(None);
301 };
302 let len = parse_int(&buf[1..hdr_end])
303 .ok_or(ProtocolError::Malformed("bad blob error length"))?;
304 if len < 0 {
305 return Err(ProtocolError::Malformed("blob error length cannot be negative"));
306 }
307 let data_start = hdr_end + 2;
308 let data_end = data_start + len as usize;
309 if buf.len() < data_end + 2 {
310 return Ok(None);
311 }
312 Ok(Some((Reply::BlobError(buf[data_start..data_end].to_vec()), data_end + 2)))
313}
314
315fn parse_attributed_reply(buf: &[u8]) -> Result<Option<(Reply, usize)>, ProtocolError> {
320 let Some((_attrs, used_attrs)) = parse_map_reply(buf)? else {
323 return Ok(None);
324 };
325 match parse_reply(&buf[used_attrs..])? {
326 None => Ok(None),
327 Some((r, used)) => Ok(Some((r, used_attrs + used))),
328 }
329}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334
335 #[test]
336 fn parse_replies() {
337 let r = |b: &[u8]| parse_reply(b).unwrap().unwrap().0;
338 assert_eq!(r(b"+OK\r\n"), Reply::Simple(b"OK".to_vec()));
339 assert_eq!(r(b"-ERR bad\r\n"), Reply::Error(b"ERR bad".to_vec()));
340 assert_eq!(r(b":42\r\n"), Reply::Int(42));
341 assert_eq!(r(b"$5\r\nhello\r\n"), Reply::Bulk(b"hello".to_vec()));
342 assert_eq!(r(b"$-1\r\n"), Reply::Nil);
343 assert_eq!(r(b"*-1\r\n"), Reply::Nil);
344
345 let (arr, used) = parse_reply(b"*2\r\n:1\r\n$2\r\nhi\r\n").unwrap().unwrap();
346 assert_eq!(
347 arr,
348 Reply::Array(vec![Reply::Int(1), Reply::Bulk(b"hi".to_vec())])
349 );
350 assert_eq!(used, 16);
351
352 assert_eq!(parse_reply(b"$5\r\nhel").unwrap(), None);
354 assert_eq!(parse_reply(b"*2\r\n:1\r\n").unwrap(), None);
355 assert!(parse_reply(b"@huh\r\n").is_err());
358 }
359
360 #[test]
361 fn parse_resp3_scalars() {
362 let r = |b: &[u8]| parse_reply(b).unwrap().unwrap().0;
363 assert_eq!(r(b"_\r\n"), Reply::Null);
364 assert_eq!(r(b"#t\r\n"), Reply::Boolean(true));
365 assert_eq!(r(b"#f\r\n"), Reply::Boolean(false));
366 assert_eq!(r(b",1.5\r\n"), Reply::Double(1.5));
367 assert_eq!(r(b",inf\r\n"), Reply::Double(f64::INFINITY));
368 assert_eq!(r(b",-inf\r\n"), Reply::Double(f64::NEG_INFINITY));
369 match r(b",nan\r\n") {
371 Reply::Double(v) => assert!(v.is_nan()),
372 other => panic!("expected Double(nan), got {other:?}"),
373 }
374 assert_eq!(
375 r(b"(170141183460469231731687303715884105727\r\n"),
376 Reply::BigNumber(b"170141183460469231731687303715884105727".to_vec())
377 );
378 assert_eq!(
379 r(b"!11\r\nERR bad cmd\r\n"),
380 Reply::BlobError(b"ERR bad cmd".to_vec())
381 );
382 }
383
384 #[test]
385 fn parse_resp3_verbatim() {
386 let r = |b: &[u8]| parse_reply(b).unwrap().unwrap().0;
387 assert_eq!(
388 r(b"=15\r\ntxt:Some string\r\n"),
389 Reply::Verbatim { fmt: *b"txt", data: b"Some string".to_vec() }
390 );
391 assert!(parse_reply(b"=3\r\ntxt\r\n").is_err());
393 assert!(parse_reply(b"=7\r\ntxt+abc\r\n").is_err());
395 }
396
397 #[test]
398 fn parse_resp3_map_and_set() {
399 let r = |b: &[u8]| parse_reply(b).unwrap().unwrap().0;
400 let m = r(b"%2\r\n:1\r\n$1\r\na\r\n:2\r\n$1\r\nb\r\n");
402 assert_eq!(
403 m,
404 Reply::Map(vec![
405 (Reply::Int(1), Reply::Bulk(b"a".to_vec())),
406 (Reply::Int(2), Reply::Bulk(b"b".to_vec())),
407 ])
408 );
409 let s = r(b"~3\r\n:1\r\n:2\r\n:3\r\n");
411 assert_eq!(s, Reply::Set(vec![Reply::Int(1), Reply::Int(2), Reply::Int(3)]));
412 assert_eq!(r(b"%0\r\n"), Reply::Map(vec![]));
414 assert_eq!(r(b"~0\r\n"), Reply::Set(vec![]));
415 assert!(parse_reply(b"%-1\r\n").is_err());
417 assert!(parse_reply(b"~-1\r\n").is_err());
418 }
419
420 #[test]
421 fn parse_resp3_push_frame() {
422 let r = |b: &[u8]| parse_reply(b).unwrap().unwrap().0;
423 let push = r(b">3\r\n+message\r\n$4\r\nnews\r\n$5\r\nhello\r\n");
424 assert_eq!(
425 push,
426 Reply::Push(vec![
427 Reply::Simple(b"message".to_vec()),
428 Reply::Bulk(b"news".to_vec()),
429 Reply::Bulk(b"hello".to_vec()),
430 ])
431 );
432 assert!(parse_reply(b">-1\r\n").is_err());
434 }
435
436 #[test]
437 fn parse_resp3_attributes_are_skipped() {
438 let frame =
441 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";
442 let (r, used) = parse_reply(frame).unwrap().unwrap();
443 assert_eq!(r, Reply::Array(vec![Reply::Int(1), Reply::Int(2)]));
444 assert_eq!(used, frame.len());
445 }
446
447 #[test]
448 fn parse_resp3_partial_returns_none() {
449 for cut in [b"_".as_slice(), b"_\r", b"#t", b"#t\r"].iter() {
451 assert_eq!(parse_reply(cut).unwrap(), None);
452 }
453 assert_eq!(parse_reply(b"=15\r\ntxt:Some str").unwrap(), None);
454 assert_eq!(parse_reply(b"%2\r\n:1\r\n$1\r\na\r\n:2\r\n").unwrap(), None);
456 }
457}