From 1b636ca1e867945bb6f67029b4ad583b13820645 Mon Sep 17 00:00:00 2001 From: kindywu Date: Fri, 2 Aug 2024 23:36:36 +0800 Subject: [PATCH] commit --- benches/resp.rs | 18 ++- examples/expect_length.rs | 195 ++++++++++++++++++++++------- src/lib.rs | 2 + src/resp/frame.rs | 1 + src/respv3/mod.rs | 256 ++++++++++++++++++++++++++++++++++++++ src/respv3/parse.rs | 151 ++++++++++++++++++++++ 6 files changed, 580 insertions(+), 43 deletions(-) create mode 100644 src/respv3/mod.rs create mode 100644 src/respv3/parse.rs diff --git a/benches/resp.rs b/benches/resp.rs index e6ec102..5773f60 100644 --- a/benches/resp.rs +++ b/benches/resp.rs @@ -14,7 +14,9 @@ use simple_redis::RespFrame; // cmd 4 response: value // cmd 5: sadd key member // cmd 5 response: 1 -const DATA: &str = "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n*1\r\n+OK\r\n*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n*4\r\n$4\r\nHSET\r\n$3\r\nkey\r\n$5\r\nfield\r\n$5\r\nvalue\r\n*1\r\n-ERR\r\n*3\r\n$4\r\nHGET\r\n$3\r\nkey\r\n$5\r\nfield\r\n$5\r\nvalue\r\n*3\r\n$4\r\nSADD\r\n$3\r\nkey\r\n$6\r\nmember\r\n:1\r\n"; +// const DATA: &str = "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n*1\r\n+OK\r\n*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n*4\r\n$4\r\nHSET\r\n$3\r\nkey\r\n$5\r\nfield\r\n$5\r\nvalue\r\n*1\r\n-ERR\r\n*3\r\n$4\r\nHGET\r\n$3\r\nkey\r\n$5\r\nfield\r\n$5\r\nvalue\r\n*3\r\n$4\r\nSADD\r\n$3\r\nkey\r\n$6\r\nmember\r\n:1\r\n"; + +const DATA: &str = "*3\r\n$4\r\necho\r\n$5\r\nhello\r\n+OK\r\n"; fn v1_decode(buf: &mut BytesMut) -> Result> { use simple_redis::RespDecode; @@ -36,6 +38,16 @@ fn v2_decode(buf: &mut BytesMut) -> Result> { Ok(frames) } +fn v3_decode(buf: &mut BytesMut) -> Result> { + use simple_redis::RespDecodeV3; + let mut frames = Vec::new(); + while !buf.is_empty() { + let frame = RespFrame::decode(buf)?; + frames.push(frame); + } + Ok(frames) +} + fn criterion_benchmark(c: &mut Criterion) { let buf = BytesMut::from(DATA); @@ -46,6 +58,10 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function("v2_decode", |b| { b.iter(|| black_box(v2_decode(&mut buf.clone()))) }); + + c.bench_function("v3_decode", |b| { + b.iter(|| black_box(v3_decode(&mut buf.clone()))) + }); } criterion_group!(benches, criterion_benchmark); diff --git a/examples/expect_length.rs b/examples/expect_length.rs index f8a92af..5148187 100644 --- a/examples/expect_length.rs +++ b/examples/expect_length.rs @@ -1,6 +1,6 @@ use simple_redis::RespError; use winnow::{ - ascii::dec_int, + ascii::{crlf, dec_int}, combinator::terminated, stream::AsChar, token::{take, take_until}, @@ -31,81 +31,192 @@ fn main() { // expect_length(buf).unwrap(); // let buf = b"*0\r\n"; // expect_length(buf).unwrap(); - let buf = b"*2\r\n$4\r\necho\r\n$5\r\nhello\r\n"; + // let buf = b"*2\r\n$4\r\necho\r\n$5\r\nhello\r\n"; + // expect_length(buf).unwrap(); + + let buf = b"%2\r\n+hello\r\n$5\r\nworld\r\n+foo\r\n$3\r\nbar\r\n"; expect_length(buf).unwrap(); } fn expect_length(input: &[u8]) -> Result { match expect_length_inner(input) { Ok(v) => { - println!("input:{}, remain: {:?}, len:{}", input.len(), v.0, v.1 + 1); - Ok(v.1 + 1) + println!("input:{}, remain: {:?}, len:{}", input.len(), v.0, v.1); + Ok(v.1) } Err(_) => Err(RespError::NotComplete), } } fn expect_length_inner(input: &[u8]) -> PResult<(&[u8], usize)> { - Ok(match take(1usize).parse_peek(input)? { - (i, b"+") => cal_by_crlf(i)?, + let (remain, len) = match take(1usize).parse_peek(input)? { + (i, b"+") => cal_util_crlf(i)?, (i, b"$") => cal_by_len(i)?, - (i, b"*") => array_length(i)?, + (i, b"*") => cal_array(i)?, + (i, b"%") => cal_map(i)?, (i, p) => { println!("{:?} {i:?}", p[0].as_char()); unreachable!() } - }) + }; + Ok((remain, len + 1)) } -fn cal_by_crlf(input: &[u8]) -> PResult<(&[u8], usize)> { +// \r\n +#[inline] +fn cal_crlf(input: &[u8]) -> PResult<(&[u8], usize)> { + let (remain, found) = crlf.parse_peek(input)?; + Ok((remain, found.len())) +} + +// +OK\r\n +#[inline] +fn cal_util_crlf(input: &[u8]) -> PResult<(&[u8], usize)> { let (remain, found) = terminated(take_until(0.., CRLF), CRLF).parse_peek(input)?; Ok((remain, found.len() + 2)) } +#[inline] fn cal_by_len(input: &[u8]) -> PResult<(&[u8], usize)> { let (remain, len): (&[u8], i64) = dec_int.parse_peek(input)?; - if len <= 0 { - let (remain, size) = cal_by_crlf(remain)?; - let mut total = if len == 0 { 1 } else { 2 }; + if len == -1 { + let mut total = 2; + let (remain, size) = cal_crlf(remain)?; + total += size; + return Ok((remain, total)); + } else if len == 0 { + let mut total = 1; + let (remain, size) = cal_crlf(remain)?; total += size; - Ok((remain, total)) - } else { - let len = len as usize; - let mut total = len / 10 + 1; - let (remain, size) = cal_by_crlf(remain)?; + let (remain, size) = cal_crlf(remain)?; total += size; - let (remain, found) = take(len).parse_peek(remain)?; - total += found.len(); - let (remain, size) = cal_by_crlf(remain)?; + return Ok((remain, total)); + } + + let len = len as usize; + let mut total = len / 10 + 1; + let (remain, size) = cal_crlf(remain)?; + total += size; + let (remain, found) = take(len).parse_peek(remain)?; + total += found.len(); + let (remain, size) = cal_crlf(remain)?; + total += size; + Ok((remain, total)) +} + +#[inline] +fn cal_array(input: &[u8]) -> PResult<(&[u8], usize)> { + let (remain, len): (&[u8], i64) = dec_int.parse_peek(input.as_ref())?; + if len == 0 { + let (remain, size) = cal_crlf(remain)?; + return Ok((remain, size + 1)); + } else if len == -1 { + let (remain, size) = cal_crlf(remain)?; + return Ok((remain, size + 2)); + } + + let len = len as usize; + let mut total = len / 10 + 1; + let (mut r1, size) = cal_crlf(remain)?; + total += size; + + for _ in 0..len { + let (r2, size) = expect_length_inner(r1)?; total += size; - Ok((remain, total)) + r1 = r2; } + + Ok((r1, total)) } -fn array_length(input: &[u8]) -> PResult<(&[u8], usize)> { +#[inline] +fn cal_map(input: &[u8]) -> PResult<(&[u8], usize)> { + // 2\r\n+hello\r\n$5\r\nworld\r\n+foo\r\n$3\r\nbar\r\n + let (remain, len): (&[u8], i64) = dec_int.parse_peek(input.as_ref())?; + if len == 0 { + let (remain, size) = cal_crlf(remain)?; + return Ok((remain, size + 1)); + } else if len == -1 { + let (remain, size) = cal_crlf(remain)?; + return Ok((remain, size + 2)); + } + + let len = len as usize; + let mut total = len / 10 + 1; + let (mut r1, size) = cal_crlf(remain)?; + total += size; + + for _ in 0..len { + let (r2, size) = expect_length_inner(r1)?; + total += size; - if len <= 0 { - let (remain, size) = cal_by_crlf(remain)?; - let mut total = if len == 0 { 1 } else { 2 }; + let (r3, size) = expect_length_inner(r2)?; total += size; - Ok((remain, total)) - } else { - let mut len = len as usize; - let mut total = len / 10 + 1; - - let remain = loop { - let (remain, size) = cal_by_crlf(remain)?; - total += size; - let (remain, size) = expect_length_inner(remain)?; - total += size; - len -= 1; - if len == 0 { - break remain; - } - }; - - Ok((remain, total)) + r1 = r3; + } + + Ok((r1, total)) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn cal_crlf_should_work() -> PResult<()> { + let buf = b"\r\n"; + let result = cal_crlf(buf)?; + assert_eq!(buf.len(), result.1); + Ok(()) + } + + #[test] + fn cal_util_crlf_should_work() -> PResult<()> { + let buf = b"OK\r\n"; + let result = cal_util_crlf(buf)?; + assert_eq!(buf.len(), result.1); + Ok(()) + } + + #[test] + fn cal_by_len_should_work() -> PResult<()> { + let buf = b"0\r\n\r\n"; + let result = cal_by_len(buf)?; + assert_eq!(buf.len(), result.1); + + let buf = b"-1\r\n"; + let result = cal_by_len(buf)?; + assert_eq!(buf.len(), result.1); + + let buf = b"5\r\nhello\r\n"; + let result = cal_by_len(buf)?; + assert_eq!(buf.len(), result.1); + Ok(()) + } + + #[test] + fn cal_array_should_work() -> PResult<()> { + let buf = b"-1\r\n"; + let result = cal_array(buf)?; + assert_eq!(buf.len(), result.1); + + let buf = b"0\r\n"; + let result = cal_array(buf)?; + assert_eq!(buf.len(), result.1); + + let buf = b"2\r\n$4\r\necho\r\n$5\r\nhello\r\n"; + let result = cal_array(buf)?; + assert_eq!(buf.len(), result.1); + Ok(()) + } + + #[test] + fn cal_map_should_work() -> PResult<()> { + let buf = b"2\r\n+hello\r\n$5\r\nworld\r\n+foo\r\n*2\r\n$4\r\necho\r\n$5\r\nhello\r\n"; + let result = cal_map(buf)?; + assert_eq!(buf.len(), result.1); + Ok(()) } } diff --git a/src/lib.rs b/src/lib.rs index 77a26cc..0b7747d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ mod backend; mod cmd; mod resp; mod respv2; +mod respv3; pub use backend::*; pub use cmd::*; @@ -11,3 +12,4 @@ pub use resp::{ }; // pub use resp::*; pub use respv2::RespDecodeV2; +pub use respv3::RespDecodeV3; diff --git a/src/resp/frame.rs b/src/resp/frame.rs index 3bce617..456f231 100644 --- a/src/resp/frame.rs +++ b/src/resp/frame.rs @@ -84,6 +84,7 @@ impl RespDecode for RespFrame { Some(b'*') => Option::::expect_length(buf), Some(b'$') => Option::::expect_length(buf), Some(b'+') => SimpleString::expect_length(buf), + Some(b'-') => SimpleError::expect_length(buf), _ => Err(RespError::NotComplete), } } diff --git a/src/respv3/mod.rs b/src/respv3/mod.rs new file mode 100644 index 0000000..a18c6c6 --- /dev/null +++ b/src/respv3/mod.rs @@ -0,0 +1,256 @@ +use crate::{RespError, RespFrame}; +use bytes::BytesMut; + +mod parse; + +use parse::*; +use winnow::{ + ascii::{crlf, dec_int}, + combinator::terminated, + token::{take, take_until}, + PResult, Parser, +}; + +const CRLF: &[u8] = b"\r\n"; + +#[allow(unused)] +pub trait RespDecodeV3: Sized { + fn decode(buf: &mut BytesMut) -> Result; + fn expect_length(buf: &[u8]) -> Result; +} + +impl RespDecodeV3 for RespFrame { + fn decode(buf: &mut bytes::BytesMut) -> Result { + let len = Self::expect_length(buf)?; + let data = buf.split_to(len); + parse_resp(&mut data.as_ref()).map_err(|e| RespError::InvalidFrame(e.to_string())) + } + + fn expect_length(input: &[u8]) -> Result { + match expect_length_inner(input) { + Ok(v) => Ok(v.1), + Err(_) => Err(RespError::NotComplete), + } + } +} + +fn expect_length_inner(input: &[u8]) -> PResult<(&[u8], usize)> { + let (remain, len) = match take(1usize).parse_peek(input)? { + (i, b"+") => cal_util_crlf(i)?, + (i, b"-") => cal_util_crlf(i)?, + (i, b"_") => cal_util_crlf(i)?, + (i, b":") => cal_util_crlf(i)?, + (i, b"#") => cal_util_crlf(i)?, + (i, b",") => cal_util_crlf(i)?, + (i, b"$") => cal_by_len(i)?, + (i, b"*") => cal_array(i)?, + (i, b"%") => cal_map(i)?, + _ => { + unreachable!() + } + }; + Ok((remain, len + 1)) +} + +// \r\n +#[inline] +fn cal_crlf(input: &[u8]) -> PResult<(&[u8], usize)> { + let (remain, found) = crlf.parse_peek(input)?; + Ok((remain, found.len())) +} + +// +OK\r\n +#[inline] +fn cal_util_crlf(input: &[u8]) -> PResult<(&[u8], usize)> { + let (remain, found) = terminated(take_until(0.., CRLF), CRLF).parse_peek(input)?; + Ok((remain, found.len() + 2)) +} + +#[inline] +fn cal_by_len(input: &[u8]) -> PResult<(&[u8], usize)> { + let (remain, len): (&[u8], i64) = dec_int.parse_peek(input)?; + + if len == -1 { + let mut total = 2; + let (remain, size) = cal_crlf(remain)?; + total += size; + return Ok((remain, total)); + } else if len == 0 { + let mut total = 1; + let (remain, size) = cal_crlf(remain)?; + total += size; + let (remain, size) = cal_crlf(remain)?; + total += size; + return Ok((remain, total)); + } + + let len = len as usize; + let mut total = len / 10 + 1; + let (remain, size) = cal_crlf(remain)?; + total += size; + let (remain, found) = take(len).parse_peek(remain)?; + total += found.len(); + let (remain, size) = cal_crlf(remain)?; + total += size; + Ok((remain, total)) +} + +#[inline] +fn cal_array(input: &[u8]) -> PResult<(&[u8], usize)> { + let (remain, len): (&[u8], i64) = dec_int.parse_peek(input.as_ref())?; + if len == 0 { + let (remain, size) = cal_crlf(remain)?; + return Ok((remain, size + 1)); + } else if len == -1 { + let (remain, size) = cal_crlf(remain)?; + return Ok((remain, size + 2)); + } + + let len = len as usize; + let mut total = len / 10 + 1; + let (mut r1, size) = cal_crlf(remain)?; + total += size; + + for _ in 0..len { + let (r2, size) = expect_length_inner(r1)?; + total += size; + r1 = r2; + } + + Ok((r1, total)) +} + +#[inline] +fn cal_map(input: &[u8]) -> PResult<(&[u8], usize)> { + // 2\r\n+hello\r\n$5\r\nworld\r\n+foo\r\n$3\r\nbar\r\n + + let (remain, len): (&[u8], i64) = dec_int.parse_peek(input.as_ref())?; + if len == 0 { + let (remain, size) = cal_crlf(remain)?; + return Ok((remain, size + 1)); + } else if len == -1 { + let (remain, size) = cal_crlf(remain)?; + return Ok((remain, size + 2)); + } + + let len = len as usize; + let mut total = len / 10 + 1; + let (mut r1, size) = cal_crlf(remain)?; + total += size; + + for _ in 0..len { + let (r2, size) = expect_length_inner(r1)?; + total += size; + + let (r3, size) = expect_length_inner(r2)?; + total += size; + r1 = r3; + } + + Ok((r1, total)) +} + +#[cfg(test)] +mod tests { + use crate::{BulkString, RespArray, RespNull, SimpleError, SimpleString}; + + use super::*; + + #[test] + fn respv3_should_work() { + let s: &str = "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n*1\r\n+OK\r\n*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n*4\r\n$4\r\nHSET\r\n$3\r\nkey\r\n$5\r\nfield\r\n$5\r\nvalue\r\n*1\r\n-ERR\r\n*3\r\n$4\r\nHGET\r\n$3\r\nkey\r\n$5\r\nfield\r\n$5\r\nvalue\r\n*3\r\n$4\r\nSADD\r\n$3\r\nkey\r\n$6\r\nmember\r\n:1\r\n"; + + let mut buf = BytesMut::from(s); + + let resp = RespFrame::decode(&mut buf); + assert!(resp.is_ok()); + } + + #[test] + fn respv3_decode_simple_string_should_work() { + let mut buf = BytesMut::from("+OK\r\n"); + let resp = RespFrame::decode(&mut buf).unwrap(); + assert_eq!(RespFrame::SimpleString(SimpleString::new("OK")), resp) + } + + #[test] + fn respv3_decode_uncomple_simple_string_should_fail() { + let mut buf = BytesMut::from("+OK\r"); + let resp = RespFrame::decode(&mut buf); + assert_eq!(RespError::NotComplete, resp.unwrap_err()) + } + + #[test] + fn respv3_decode_integer_should_work() { + let mut buf = BytesMut::from(":10\r\n"); + let resp = RespFrame::decode(&mut buf).unwrap(); + assert_eq!(RespFrame::Integer(10), resp); + + let mut buf = BytesMut::from(":-10\r\n"); + let resp = RespFrame::decode(&mut buf).unwrap(); + assert_eq!(RespFrame::Integer(-10), resp) + } + + #[test] + fn respv3_decode_error_should_work() { + let mut buf = BytesMut::from("-ERR\r\n"); + let resp = RespFrame::decode(&mut buf).unwrap(); + assert_eq!(RespFrame::Error(SimpleError::new("ERR")), resp) + } + + #[test] + fn respv3_decode_null_should_work() { + let mut buf = BytesMut::from("+OK\r\n_\r\n+OK\r\n"); + + let resp = RespFrame::decode(&mut buf).unwrap(); + assert_eq!(RespFrame::SimpleString(SimpleString::new("OK")), resp); + + let resp = RespFrame::decode(&mut buf).unwrap(); + assert_eq!(RespFrame::Null(RespNull), resp); + + let resp = RespFrame::decode(&mut buf).unwrap(); + assert_eq!(RespFrame::SimpleString(SimpleString::new("OK")), resp); + } + + #[test] + fn respv3_decode_bulk_string_should_work() { + let test_cases = ["$5\r\nhello\r\n", "$0\r\n\r\n", "$-1\r\n"]; + let test_expecteds = [ + RespFrame::BulkString(Some(BulkString::new("hello"))), + RespFrame::BulkString(Some(BulkString::new(""))), + RespFrame::BulkString(None), + ]; + + for (&test, excepted) in test_cases.iter().zip(test_expecteds) { + let mut buf = BytesMut::from(test); + let result = RespFrame::decode(&mut buf); + assert!(result.is_ok()); + assert_eq!(excepted, result.unwrap()); + } + } + + #[test] + fn respv3_decode_array_string_should_work() { + let test_cases = [ + "*-1\r\n", + "*0\r\n", + "*3\r\n$4\r\necho\r\n$5\r\nhello\r\n+OK\r\n", + ]; + let test_expecteds = [ + RespFrame::Array(None), + RespFrame::Array(Some(RespArray::new(vec![]))), + RespFrame::Array(Some(RespArray::new([ + b"echo".into(), + b"hello".into(), + RespFrame::SimpleString(SimpleString::new("OK")), + ]))), + ]; + + for (&test, excepted) in test_cases.iter().zip(test_expecteds) { + let mut buf = BytesMut::from(test); + let result = RespFrame::decode(&mut buf); + assert!(result.is_ok()); + assert_eq!(excepted, result.unwrap()); + } + } +} diff --git a/src/respv3/parse.rs b/src/respv3/parse.rs new file mode 100644 index 0000000..6ecb0ce --- /dev/null +++ b/src/respv3/parse.rs @@ -0,0 +1,151 @@ +use std::collections::BTreeMap; + +use crate::resp::Double; +use crate::{BulkString, RespArray, RespFrame, RespNull, SimpleError, SimpleString}; +use winnow::ascii::{crlf, dec_int, digit1, float}; +use winnow::combinator::{alt, dispatch, fail, terminated}; +use winnow::token::any; +use winnow::{token::take_until, PResult, Parser}; + +const CRLF: &[u8] = b"\r\n"; + +pub fn parse_resp(input: &mut &[u8]) -> PResult { + // match take(1usize).parse_next(input)? { + // b"+" => simple_string(input).map(RespFrame::SimpleString), + // b"-" => simple_error(input).map(RespFrame::Error), + // _ => todo!(), + // } + + dispatch! {any; + b'+' => simple_string.map(RespFrame::SimpleString), + b'-' => error.map(RespFrame::Error), + b'_' => null.map(RespFrame::Null), + b':' => integer.map(RespFrame::Integer), + b'*' => array.map(RespFrame::Array), + b'$' => bulk_string.map(RespFrame::BulkString), + b'#' => boolean.map(RespFrame::Boolean), + b',' => double.map(RespFrame::Double), + b'%' => map.map(RespFrame::Map), + _ => fail::<_, _, _>, + } + .parse_next(input) +} + +fn simple_string(input: &mut &[u8]) -> PResult { + Ok(SimpleString::new(parse_string(input)?)) +} + +fn error(input: &mut &[u8]) -> PResult { + Ok(SimpleError::new(parse_string(input)?)) +} + +// - boolean: "#t\r\n" +fn boolean(input: &mut &[u8]) -> PResult { + let b = alt(('t', 'f')).parse_next(input)?; + Ok(b == 't') +} + +// - float: ",3.14\r\n" +fn double(input: &mut &[u8]) -> PResult { + terminated(float, CRLF).map(Double).parse_next(input) +} + +// _\r\n +fn null(input: &mut &[u8]) -> PResult { + crlf(input)?; + Ok(RespNull) +} + +// :[<+|->]\r\n +fn integer(input: &mut &[u8]) -> PResult { + dec_int(input) +} + +// $5\r\nhello\r\n +// $0\r\n\r\n +// $-1\r\n +fn bulk_string(input: &mut &[u8]) -> PResult> { + let len: i64 = dec_int(input)?; + crlf(input)?; + if len == -1 { + return Ok(None); + } + Ok(Some(BulkString::new(parse_string(input)?))) +} + +// *3\r\n$4\r\necho\r\n$5\r\nhello\r\n+OK\r\n +// *0\r\n +// *-1\r\n +fn array(input: &mut &[u8]) -> PResult> { + let len: i64 = dec_int(input)?; + crlf(input)?; + if len == -1 { + return Ok(None); + } + let mut arr = Vec::new(); + for _ in 0..len { + arr.push(parse_resp(input)?); + } + + Ok(Some(RespArray::new(arr))) +} + +// "%2\r\n+hello\r\n$5\r\nworld\r\n+foo\r\n$3\r\nbar\r\n" +fn map(input: &mut &[u8]) -> PResult> { + let len: u32 = digit1.parse_to().parse_next(input)?; + crlf(input)?; + let mut map = BTreeMap::new(); + for _ in 0..len { + let key = parse_resp(input)?; + let value = parse_resp(input)?; + map.insert(key, value); + } + Ok(map) +} + +fn parse_string(input: &mut &[u8]) -> PResult { + terminated(take_until(0.., CRLF), CRLF) + .map(|v| String::from_utf8_lossy(v).to_string()) + .parse_next(input) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn respv2_simple_string_should_work() { + let s = b"+OK\r\n"; + let resp = parse_resp(&mut s.as_ref()).unwrap(); + assert_eq!(RespFrame::SimpleString(SimpleString::new("OK")), resp) + } + + #[test] + fn respv2_simple_string_should_fail() { + let s = b"+OK\r"; + let resp = parse_resp(&mut s.as_ref()); + assert!(resp.is_err()); + } + + #[test] + fn respv2_map_should_work() { + let s = b"%2\r\n+hello\r\n$5\r\nworld\r\n+foo\r\n$3\r\nbar\r\n"; + let resp = parse_resp(&mut s.as_ref()); + let mut map = BTreeMap::new(); + map.insert( + SimpleString::new("hello").into(), + Some(BulkString::new("world")).into(), + ); + map.insert( + SimpleString::new("foo").into(), + Some(BulkString::new("bar")).into(), + ); + assert!(resp.is_ok()); + assert_eq!(RespFrame::Map(map), resp.unwrap()); + + let s = b"%0\r\n"; + let resp = parse_resp(&mut s.as_ref()); + assert!(resp.is_ok()); + assert_eq!(RespFrame::Map(BTreeMap::new()), resp.unwrap()) + } +}