Skip to content

Commit

Permalink
Merge pull request #2 from upupnoah/main
Browse files Browse the repository at this point in the history
feat(resp-encode): support resp-encode
  • Loading branch information
upupnoah authored Jul 14, 2024
2 parents 50e309d + a615abe commit 6e996c8
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 18 deletions.
56 changes: 46 additions & 10 deletions src/resp.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::collections::{HashMap, HashSet};
use std::collections::BTreeMap;

use bytes::BytesMut;
use enum_dispatch::enum_dispatch;

mod encode;

#[enum_dispatch]
pub trait RespEncode {
fn encode(self) -> Vec<u8>;
}
Expand All @@ -12,6 +14,7 @@ pub trait RespDecode {
fn decode(buf: Self) -> Result<RespFrame, String>;
}
// 之所以要定义一些新的结构体, 是因为要在实现 trait 的时候, 要区分开这些类型
#[enum_dispatch(RespEncode)]
pub enum RespFrame {
SimpleString(SimpleString),
Error(SimpleError),
Expand All @@ -32,30 +35,63 @@ pub enum RespFrame {
// 2. 新类型模式:这是 Rust 中常用的一种模式,用于在类型系统层面区分不同用途的相同底层类型。比如,你可能想区分普通的字符串和特定格式的字符串。
// 3. 添加方法:你可以为 SimpleString 实现方法,这些方法特定于这种类型的字符串。
// 4. 语义清晰:在复杂的数据结构中(如你展示的 RespFrame 枚举),使用 SimpleString 而不是直接使用 String 可以使代码的意图更加明确。
pub struct SimpleString(String);
pub struct SimpleString(String); // Simple String, 用于存储简单字符串
pub struct SimpleError(String);
pub struct BulkString(Vec<u8>);
pub struct BulkString(Vec<u8>); // 单个二进制字符串, 用于存储二进制数据(最大512MB)
pub struct RespNullBulkString;
pub struct RespArray(Vec<RespFrame>);
pub struct RespNullArray;
pub struct RespNull;
pub struct RespMap(HashMap<String, RespFrame>);
pub struct RespSet(HashSet<RespFrame>);
#[derive(Default)]
pub struct RespMap(BTreeMap<String, RespFrame>); // 改为 BTreeMap, 用于有序的 key-value 数据

// pub struct RespSet(HashSet<RespFrame>);
pub struct RespSet(Vec<RespFrame>); // 改为 Vec, 用于有序的集合数据

impl SimpleString {
pub fn new(s: impl Into<String>) -> Self {
SimpleString(s.into())
}
}

impl RespDecode for BytesMut {
fn decode(_buf: Self) -> Result<RespFrame, String> {
todo!()
impl SimpleError {
pub fn new(s: impl Into<String>) -> Self {
SimpleError(s.into())
}
}

impl BulkString {
pub fn new(s: impl Into<Vec<u8>>) -> Self {
BulkString(s.into())
}
}

impl RespArray {
pub fn new(s: impl Into<Vec<RespFrame>>) -> Self {
RespArray(s.into())
}
}

impl RespMap {
pub fn new() -> Self {
RespMap(BTreeMap::new())
}
}

impl RespSet {
pub fn new(s: impl Into<Vec<RespFrame>>) -> Self {
RespSet(s.into())
}
}

impl RespEncode for RespFrame {
fn encode(self) -> Vec<u8> {
impl RespDecode for BytesMut {
fn decode(_buf: Self) -> Result<RespFrame, String> {
todo!()
}
}

// impl RespEncode for RespFrame {
// fn encode(self) -> Vec<u8> {
// todo!()
// }
// }
147 changes: 139 additions & 8 deletions src/resp/encode.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{
collections::{HashMap, HashSet},
ops::Deref,
collections::BTreeMap,
ops::{Deref, DerefMut},
};

use crate::{
Expand All @@ -27,7 +27,7 @@ impl RespEncode for SimpleError {
// - integer: ":[<+|->]<value>\r\n"
impl RespEncode for i64 {
fn encode(self) -> Vec<u8> {
let sign = if self < 0 { "-" } else { "+" };
let sign = if self < 0 { "" } else { "+" }; // -1 => -1, 1 => +1
format!(":{}{}\r\n", sign, self).into_bytes()
}
}
Expand Down Expand Up @@ -91,9 +91,16 @@ impl RespEncode for bool {
// - double: ",[<+|->]<integral>[.<fractional>][<E|e>[sign]<exponent>]\r\n"
impl RespEncode for f64 {
fn encode(self) -> Vec<u8> {
// scientific natation
// format: {:+e}
format!(",{:+e}\r\n", self).into_bytes()
let mut buf = Vec::with_capacity(32);
let ret = if self.abs() > 1e+8 || self.abs() < 1e-8 {
format!(",{:+e}\r\n", self)
} else {
let sign = if self < 0.0 { "" } else { "+" };
format!(",{}{}\r\n", sign, self)
};

buf.extend_from_slice(&ret.into_bytes());
buf
}
}

Expand Down Expand Up @@ -169,17 +176,141 @@ impl Deref for RespArray {
}

impl Deref for RespMap {
type Target = HashMap<String, RespFrame>;
type Target = BTreeMap<String, RespFrame>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for RespMap {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

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

fn deref(&self) -> &Self::Target {
&self.0
}
}

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

#[test]
fn test_simple_string_encode() {
let frame: RespFrame = SimpleString::new("OK".to_string()).into();
assert_eq!(frame.encode(), b"+OK\r\n");
}

#[test]
fn test_error_encode() {
let frame: RespFrame = SimpleError::new("Error message".to_string()).into();
assert_eq!(frame.encode(), b"-Error message\r\n");
}

#[test]
fn test_integer_encode() {
let frame: RespFrame = 123.into();
assert_eq!(frame.encode(), b":+123\r\n");

// println!("{:?}", String::from_utf8_lossy(&frame.encode()));

let frame: RespFrame = (-123).into();
assert_eq!(frame.encode(), b":-123\r\n");
}

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

#[test]
fn test_null_bulk_string_encode() {
let frame: RespFrame = RespNullBulkString.into();
assert_eq!(frame.encode(), b"$-1\r\n");
}

#[test]
fn test_array_encode() {
let frame: RespFrame = RespArray::new(vec![
SimpleString::new("OK".to_string()).into(),
123.into(),
BulkString::new(b"hello".to_vec()).into(),
])
.into();
assert_eq!(frame.encode(), b"*3\r\n+OK\r\n:+123\r\n$5\r\nhello\r\n");
}

#[test]
fn test_null_array_encode() {
let frame: RespFrame = RespNullArray.into();
assert_eq!(frame.encode(), b"*-1\r\n");
}

#[test]
fn test_null_encode() {
let frame: RespFrame = RespNull.into();
assert_eq!(frame.encode(), b"_\r\n");
}

#[test]
fn test_boolean_encode() {
// into 和 from 是互斥的
// let frame: RespFrame = true.into();
let frame = RespFrame::from(true);
assert_eq!(frame.encode(), b"#t\r\n");

let frame: RespFrame = false.into();
assert_eq!(frame.encode(), b"#f\r\n");
}

#[test]
fn test_double_encode() {
let frame: RespFrame = (123.456).into();
assert_eq!(frame.encode(), b",+123.456\r\n");

let frame: RespFrame = (-123.456).into();
assert_eq!(frame.encode(), b",-123.456\r\n");

let frame: RespFrame = 1.23456e+8.into();
assert_eq!(frame.encode(), b",+1.23456e8\r\n");

let frame: RespFrame = (-1.23456e-9).into();
assert_eq!(&frame.encode(), b",-1.23456e-9\r\n");
}

#[test]
fn test_map_encode() {
let mut map = RespMap::new();
map.insert(
"hello".to_string(),
BulkString::new("world".to_string()).into(),
);
map.insert("foo".to_string(), (-123456.789).into());

let frame: RespFrame = map.into();
assert_eq!(
&frame.encode(),
b"%2\r\n+foo\r\n,-123456.789\r\n+hello\r\n$5\r\nworld\r\n"
);
}

#[test]
fn test_set_encode() {
let frame: RespFrame = RespSet::new([
RespArray::new([1234.into(), true.into()]).into(),
BulkString::new("world".to_string()).into(),
])
.into();
assert_eq!(
frame.encode(),
b"~2\r\n*2\r\n:+1234\r\n#t\r\n$5\r\nworld\r\n"
);
}
}

0 comments on commit 6e996c8

Please sign in to comment.