Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(resp-decode): support resp decode for simple redis #3

Merged
merged 1 commit into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <upupqi.cs@gmail.com>"]
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