Skip to content

Commit

Permalink
Merge pull request #3 from upupnoah/main
Browse files Browse the repository at this point in the history
feat(resp-decode): support resp decode for simple redis
  • Loading branch information
upupnoah authored Jul 15, 2024
2 parents 6e996c8 + 58d29a6 commit c619348
Show file tree
Hide file tree
Showing 7 changed files with 625 additions and 13 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ authors = ["Noah <[email protected]>"]
anyhow = "^1.0"
bytes = "^1.6.1"
enum_dispatch = "^0.3.13"
thiserror = "^1.0.62"
15 changes: 15 additions & 0 deletions examples/bytes_mut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use anyhow::Result;
use bytes::BytesMut;
fn main() -> Result<()> {
let a = "hello"; // a.as_bytes() = b"hello"

// bytes_mut
let mut bytes_mut = BytesMut::new();
bytes_mut.extend_from_slice(a.as_bytes());
println!("bytes_mut: {:?}", bytes_mut);

let b = bytes_mut.split_to(3);
println!("b: {:?}", b);
println!("after split_to(3) -> bytes_mut: {:?}", bytes_mut);
Ok(())
}
39 changes: 39 additions & 0 deletions examples/enum_dispatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use enum_dispatch::enum_dispatch;

#[enum_dispatch]
trait DoSomething {
fn do_something(&self);
}

#[enum_dispatch(DoSomething)]
enum Types {
Apple(A),
Banana(B),
}

struct A;
struct B;

impl DoSomething for A {
fn do_something(&self) {
println!("A");
}
}

impl DoSomething for B {
fn do_something(&self) {
println!("B");
}
}
fn main() {
// test enum_dispatch
let apple = Types::Apple(A);
let banana = Types::Banana(B);

let type_apple = apple;
let type_banana = banana;

// 都是 types 类型的, 但是结果不同, enum_dispatch 相当于是主动帮我 match 了
type_apple.do_something();
type_banana.do_something();
}
57 changes: 57 additions & 0 deletions examples/thiserorr_anyhow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use anyhow::{Context, Result};
use std::io::Error as IoError;
use std::num::ParseIntError;
use thiserror::Error;

// 定义自定义错误类型
#[derive(Error, Debug)]
enum MyError {
#[error("An IO error occurred: {0}")]
Io(#[from] IoError),

#[error("A parsing error occurred: {0}")]
Parse(#[from] ParseIntError),

#[error("Custom error: {0}")]
Custom(String),

#[error("Anyhow error: {0}")]
Anyhow(#[from] anyhow::Error),
}

// 一个可能返回错误的函数
fn parse_number(input: &str) -> Result<i32, MyError> {
let trimmed = input.trim();
if trimmed.is_empty() {
return Err(MyError::Custom("Input is empty".into()));
}

let number: i32 = trimmed
.parse()
// .map_err(|e| MyError::Parse(e))
.map_err(MyError::Parse) // 更好的写法
.context("Failed to parse number")?;
Ok(number)
}

fn main() -> Result<(), MyError> {
// 示例一: 正确的输入
match parse_number("42") {
Ok(number) => println!("Parsed number: {}", number),
Err(e) => eprintln!("Error: {}", e),
}

// 示例二: 空输入
match parse_number("") {
Ok(number) => println!("Parsed number: {}", number),
Err(e) => eprintln!("Error: {}", e),
}

// 示例三: 无效输入
match parse_number("abc") {
Ok(number) => println!("Parsed number: {}", number),
Err(e) => eprintln!("Error: {}", e),
}

Ok(())
}
152 changes: 139 additions & 13 deletions src/resp.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,60 @@
use std::collections::BTreeMap;

use bytes::BytesMut;
use bytes::{Buf, BytesMut};
use enum_dispatch::enum_dispatch;
use std::collections::BTreeMap;
use thiserror::Error;

mod decode;
mod encode;

const CRLF: &[u8] = b"\r\n";
const CRLF_LEN: usize = CRLF.len();

#[enum_dispatch]
pub trait RespEncode {
fn encode(self) -> Vec<u8>;
}

pub trait RespDecode {
fn decode(buf: Self) -> Result<RespFrame, String>;
// Sized 表示这个 trait 只能被 [大小确定的类型] 实现
// 因为 decode 方法的返回值是一个 Self, 因此必须将这个 trait 标记为 Sized
pub trait RespDecode: Sized {
const PREFIX: &'static str;
fn decode(buf: &mut BytesMut) -> Result<Self, RespError>;
fn expect_length(buf: &[u8]) -> Result<usize, RespError>;
}

#[derive(Error, Debug, PartialEq, Eq)]
pub enum RespError {
// region: --- thiserror format usage

// #[error("{var}")] ⟶ write!("{}", self.var)
// #[error("{0}")] ⟶ write!("{}", self.0)
// #[error("{var:?}")] ⟶ write!("{:?}", self.var)
// #[error("{0:?}")] ⟶ write!("{:?}", self.0)

// endregion: --- thiserror format usage
#[error("Invalid frame: {0}")] // 这里的 0 表示 self.0。 会转化为 write!
InvalidFrame(String),
#[error("Invalid frame type: {0}")]
InvalidFrameType(String),
#[error("Invalid frame length: {0}")]
InvalidFrameLength(isize),
#[error("Frame is not complete")]
NotComplete,

#[error("Parse error: {0}")]
ParseIntError(#[from] std::num::ParseIntError),
#[error("Utf8 error: {0}")]
Utf8Error(#[from] std::string::FromUtf8Error),
#[error("Parse float error: {0}")]
ParseFloatError(#[from] std::num::ParseFloatError),
}

// pub trait RespDecode: Sized {
// const PREFIX: &'static str;
// fn decode(buf: &mut BytesMut) -> Result<Self, RespError>;
// fn expect_length(buf: &[u8]) -> Result<usize, RespError>;
// }

// 之所以要定义一些新的结构体, 是因为要在实现 trait 的时候, 要区分开这些类型
#[enum_dispatch(RespEncode)]
pub enum RespFrame {
Expand All @@ -35,12 +77,19 @@ pub enum RespFrame {
// 2. 新类型模式:这是 Rust 中常用的一种模式,用于在类型系统层面区分不同用途的相同底层类型。比如,你可能想区分普通的字符串和特定格式的字符串。
// 3. 添加方法:你可以为 SimpleString 实现方法,这些方法特定于这种类型的字符串。
// 4. 语义清晰:在复杂的数据结构中(如你展示的 RespFrame 枚举),使用 SimpleString 而不是直接使用 String 可以使代码的意图更加明确。
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
pub struct SimpleString(String); // Simple String, 用于存储简单字符串
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
pub struct SimpleError(String);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
pub struct BulkString(Vec<u8>); // 单个二进制字符串, 用于存储二进制数据(最大512MB)
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
pub struct RespNullBulkString;

pub struct RespArray(Vec<RespFrame>);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
pub struct RespNullArray;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
pub struct RespNull;
#[derive(Default)]
pub struct RespMap(BTreeMap<String, RespFrame>); // 改为 BTreeMap, 用于有序的 key-value 数据
Expand Down Expand Up @@ -84,14 +133,91 @@ impl RespSet {
}
}

impl RespDecode for BytesMut {
fn decode(_buf: Self) -> Result<RespFrame, String> {
todo!()
// utility functions
fn extract_fixed_data(
buf: &mut BytesMut,
expect: &str,
expect_type: &str,
) -> Result<(), RespError> {
if buf.len() < expect.len() {
return Err(RespError::NotComplete);
}

if !buf.starts_with(expect.as_bytes()) {
return Err(RespError::InvalidFrameType(format!(
"expect: {}, got: {:?}",
expect_type, buf
)));
}

buf.advance(expect.len());
Ok(())
}

// impl RespEncode for RespFrame {
// fn encode(self) -> Vec<u8> {
// todo!()
// }
// }
fn extract_simple_frame_data(buf: &[u8], prefix: &str) -> Result<usize, RespError> {
if buf.len() < 3 {
return Err(RespError::NotComplete);
}

if !buf.starts_with(prefix.as_bytes()) {
return Err(RespError::InvalidFrameType(format!(
"expect: SimpleString({}), got: {:?}",
prefix, buf
)));
}

let end = find_crlf(buf, 1).ok_or(RespError::NotComplete)?;

Ok(end)
}

// find nth CRLF in the buffer
fn find_crlf(buf: &[u8], nth: usize) -> Option<usize> {
let mut count = 0;
for i in 1..buf.len() - 1 {
if buf[i] == b'\r' && buf[i + 1] == b'\n' {
count += 1;
if count == nth {
return Some(i);
}
}
}
None
}

fn parse_length(buf: &[u8], prefix: &str) -> Result<(usize, usize), RespError> {
let end = extract_simple_frame_data(buf, prefix)?;
let s = String::from_utf8_lossy(&buf[prefix.len()..end]);
Ok((end, s.parse()?))
}

fn calc_total_length(buf: &[u8], end: usize, len: usize, prefix: &str) -> Result<usize, RespError> {
let mut total = end + CRLF_LEN;
let mut data = &buf[total..];
match prefix {
"*" | "~" => {
// find nth CRLF in the buffer, for array and set, we need to find 1 CRLF for each element
for _ in 0..len {
let len = RespFrame::expect_length(data)?;
data = &data[len..];
total += len;
}
Ok(total)
}
"%" => {
// find nth CRLF in the buffer. For map, we need to find 2 CRLF for each key-value pair
for _ in 0..len {
let len = SimpleString::expect_length(data)?;

data = &data[len..];
total += len;

let len = RespFrame::expect_length(data)?;
data = &data[len..];
total += len;
}
Ok(total)
}
_ => Ok(len + CRLF_LEN),
}
}
Loading

0 comments on commit c619348

Please sign in to comment.