Skip to content

Commit

Permalink
feature: support resp decode
Browse files Browse the repository at this point in the history
  • Loading branch information
luffy2025 committed Nov 30, 2024
1 parent e7a3b21 commit 8fdfda6
Show file tree
Hide file tree
Showing 16 changed files with 520 additions and 93 deletions.
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
fail_fast: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v5.0.0
hooks:
- id: check-byte-order-marker
- id: check-case-conflict
Expand All @@ -12,7 +12,7 @@ repos:
- id: mixed-line-ending
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.10.0
rev: 24.10.0
hooks:
- id: black
- repo: local
Expand All @@ -23,14 +23,14 @@ repos:
entry: bash -c 'cargo fmt -- --check'
language: rust
files: \.rs$
args: []
args: [ ]
- id: cargo-deny
name: cargo deny check
description: Check cargo dependencies
entry: bash -c 'cargo deny check -d'
language: rust
files: \.rs$
args: []
args: [ ]
- id: typos
name: typos
description: check typo
Expand Down
28 changes: 28 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ edition = "2021"

[dependencies]
anyhow = "1.0.93"
bytes = "1.8.0"
enum_dispatch = "0.3.13"
thiserror = "2.0.3"
57 changes: 56 additions & 1 deletion src/resp/array.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::{RespEncode, RespFrame};
use crate::{
extract_end_and_length, is_combine_complete, RespDecode, RespEncode, RespError, RespFrame,
};
use bytes::Buf;
use std::ops::{Deref, DerefMut};

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
Expand All @@ -19,6 +22,23 @@ impl RespEncode for RespArray {
}
}

impl RespDecode for RespArray {
const PREFIX: &'static u8 = &b'*';
fn decode(buf: &mut bytes::BytesMut) -> Result<Self, RespError> {
let (end, len) = extract_end_and_length(buf, &[*Self::PREFIX])?;
is_combine_complete(buf, len)?;

buf.advance(end + 2);
let mut frames = Vec::with_capacity(len);
for _ in 0..len {
let v = RespFrame::decode(buf)?;
frames.push(v);
}

Ok(RespArray(frames))
}
}

impl Deref for RespArray {
type Target = Vec<RespFrame>;

Expand Down Expand Up @@ -48,6 +68,7 @@ mod tests {
use super::*;
use crate::resp::bulk_string::BulkString;
use crate::resp::simple_string::SimpleString;
use anyhow::Result;

#[test]
fn test_resp_array() {
Expand All @@ -59,4 +80,38 @@ mod tests {
.into();
assert_eq!(arr.encode(), b"*3\r\n$3\r\nget\r\n$5\r\nhello\r\n+rust\r\n");
}

#[test]
fn test_resp_array_decode() -> Result<()> {
let mut buf = bytes::BytesMut::from(&b"*3\r\n$3\r\nget\r\n$5\r\nhello\r\n+rust\r\n"[..]);
let ret = RespArray::decode(&mut buf)?;
assert_eq!(
ret,
RespArray::new(vec![
BulkString::new("get".into()).into(),
BulkString::new("hello".into()).into(),
SimpleString::new("rust".to_string()).into(),
])
);
Ok(())
}

#[test]
fn test_resp_array_decode_not_complete() -> Result<()> {
let mut buf = bytes::BytesMut::from(&b"*3\r\n$3\r\nget\r\n$5\r\nhello\r\n+rust\r"[..]);
let ret = RespArray::decode(&mut buf);
assert_eq!(ret.unwrap_err(), RespError::NotCompete);

buf.extend_from_slice(b"\n");
let ret = RespArray::decode(&mut buf)?;
assert_eq!(
ret,
RespArray::new(vec![
BulkString::new("get".into()).into(),
BulkString::new("hello".into()).into(),
SimpleString::new("rust".to_string()).into(),
])
);
Ok(())
}
}
29 changes: 28 additions & 1 deletion src/resp/bool.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::RespEncode;
use crate::{extract_simple_frame_data, is_fixed_complete, RespDecode, RespEncode};
use bytes::BytesMut;

// - boolean: "#<t|f>\r\n"
impl RespEncode for bool {
Expand All @@ -9,13 +10,39 @@ impl RespEncode for bool {
}
}

impl RespDecode for bool {
const PREFIX: &'static u8 = &b'#';

fn decode(buf: &mut BytesMut) -> Result<Self, crate::RespError> {
is_fixed_complete(buf)?;

let end = extract_simple_frame_data(buf, &[*Self::PREFIX])?;
let s = buf.split_to(end + 2);
Ok(s[1] == b't')
}
}

#[cfg(test)]
mod tests {
use super::*;
use anyhow::Result;

#[test]
fn test_bool_encode() {
assert_eq!(true.encode(), b"#t\r\n");
assert_eq!(false.encode(), b"#f\r\n");
}

#[test]
fn test_bool_decode() -> Result<()> {
let mut buf = bytes::BytesMut::from(&b"#t\r\n"[..]);
let ret = bool::decode(&mut buf)?;
assert!(ret);

let mut buf = bytes::BytesMut::from(&b"#f\r\n"[..]);
let ret = bool::decode(&mut buf)?;
assert!(!ret);

Ok(())
}
}
29 changes: 26 additions & 3 deletions src/resp/bulk_string.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::RespEncode;
use crate::{extract_end_and_length, is_single_complete, RespDecode, RespEncode, RespError};
use bytes::{Buf, BytesMut};
use std::ops::Deref;

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
Expand All @@ -9,14 +10,27 @@ impl RespEncode for BulkString {
fn encode(&self) -> Vec<u8> {
let length = self.len();
let mut buf = Vec::with_capacity(1 + 2 + 1 + 1 + 1 + length + 2);
buf.push(b'$');
buf.push(*Self::PREFIX);
buf.extend_from_slice(format!("{}\r\n", length).as_bytes());
buf.extend_from_slice(self);
buf.extend_from_slice(b"\r\n");
buf
}
}

impl RespDecode for BulkString {
const PREFIX: &'static u8 = &b'$';
fn decode(buf: &mut BytesMut) -> Result<Self, RespError> {
let (end, len) = extract_end_and_length(buf, &[*Self::PREFIX])?;
is_single_complete(buf, len)?;

buf.advance(end + 2);
let data = buf.split_to(len).to_vec();
buf.advance(2);
Ok(BulkString(data))
}
}

impl Deref for BulkString {
type Target = Vec<u8>;

Expand All @@ -35,10 +49,19 @@ impl BulkString {
mod tests {
use super::*;
use crate::RespFrame;
use anyhow::Result;

#[test]
fn test_bulk_string() {
fn test_bulk_string_encode() {
let bs: RespFrame = BulkString::new(b"hello".to_vec()).into();
assert_eq!(bs.encode(), b"$5\r\nhello\r\n");
}

#[test]
fn test_bulk_string_decode() -> Result<()> {
let mut buf = bytes::BytesMut::from(&b"$5\r\nhello\r\n"[..]);
let bs = BulkString::decode(&mut buf)?;
assert_eq!(bs, BulkString::new(b"hello".to_vec()));
Ok(())
}
}
56 changes: 52 additions & 4 deletions src/resp/f64.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::RespEncode;
use crate::{is_fixed_complete, RespDecode, RespEncode, RespError};

#[derive(Debug, PartialEq)]
pub struct RespF64(f64);
Expand Down Expand Up @@ -32,6 +32,19 @@ impl RespEncode for RespF64 {
}
}

impl RespDecode for RespF64 {
const PREFIX: &'static u8 = &b',';
fn decode(buf: &mut bytes::BytesMut) -> Result<Self, RespError> {
is_fixed_complete(buf)?;

let end = crate::extract_simple_frame_data(buf, &[*Self::PREFIX])?;
let s = buf.split_to(end + 2);
let s = String::from_utf8_lossy(&s[1..end]);

Ok(RespF64(s.parse()?))
}
}

impl RespF64 {
pub fn new(f: f64) -> Self {
RespF64(f)
Expand All @@ -41,14 +54,49 @@ impl RespF64 {
#[cfg(test)]
mod tests {
use super::*;
use anyhow::Result;

#[test]
fn test_f64_encode() {
assert_eq!(RespF64(0.0).encode(), b",+0e0\r\n");
assert_eq!(RespF64(1.0).encode(), b",+1\r\n");
assert_eq!(RespF64(-1.0).encode(), b",-1\r\n");
assert_eq!(RespF64(0f64).encode(), b",+0e0\r\n");
assert_eq!(RespF64(10f64).encode(), b",+10\r\n");
assert_eq!(RespF64(-10f64).encode(), b",-10\r\n");
assert_eq!(RespF64(1.23456e+8).encode(), b",+1.23456e8\r\n");
assert_eq!(RespF64(-1.23456e+8).encode(), b",-1.23456e8\r\n");
assert_eq!(RespF64(1.23456e-9).encode(), b",+1.23456e-9\r\n");
assert_eq!(RespF64(-1.23456e-9).encode(), b",-1.23456e-9\r\n");
}

#[test]
fn test_f64_decode() -> Result<()> {
let mut buf = bytes::BytesMut::from(&b",+0e0\r\n"[..]);
let ret = RespF64::decode(&mut buf)?;
assert_eq!(ret, RespF64(0f64));

let mut buf = bytes::BytesMut::from(&b",+10\r\n"[..]);
let ret = RespF64::decode(&mut buf)?;
assert_eq!(ret, RespF64(10f64));

let mut buf = bytes::BytesMut::from(&b",-10\r\n"[..]);
let ret = RespF64::decode(&mut buf)?;
assert_eq!(ret, RespF64(-10f64));

let mut buf = bytes::BytesMut::from(&b",+1.23456e8\r\n"[..]);
let ret = RespF64::decode(&mut buf)?;
assert_eq!(ret, RespF64(1.23456e+8));

let mut buf = bytes::BytesMut::from(&b",-1.23456e8\r\n"[..]);
let ret = RespF64::decode(&mut buf)?;
assert_eq!(ret, RespF64(-1.23456e+8));

let mut buf = bytes::BytesMut::from(&b",+1.23456e-9\r\n"[..]);
let ret = RespF64::decode(&mut buf)?;
assert_eq!(ret, RespF64(1.23456e-9));

let mut buf = bytes::BytesMut::from(&b",-1.23456e-9\r\n"[..]);
let ret = RespF64::decode(&mut buf)?;
assert_eq!(ret, RespF64(-1.23456e-9));

Ok(())
}
}
Loading

0 comments on commit 8fdfda6

Please sign in to comment.