RESP (Redis Serialization Protocol)

The Data Structure

I represented the protocol using a Rust Enum.

// src/resp.rs
#[derive(Debug, Clone, PartialEq)]
pub enum RespValue {
    SimpleString(String),  // +OK\r\n
    SimpleError(String),   // -Error message\r\n
    Integer(i64),          // :1000\r\n
    BulkString(String),    // $5\r\nhello\r\n
    Array(Vec<RespValue>), // *2\r\n...
    Null,
}

Serializing

Each variant knows how to turn itself back into raw bytes. The format is straightforward — prefix byte, content, \r\n terminator.

// src/resp.rs
impl RespValue {
    pub fn serialize(&self) -> Vec<u8> {
        match self {
            RespValue::SimpleString(s) => format!("+{}\r\n", s).into_bytes(),
            RespValue::SimpleError(s) => format!("-{}\r\n", s).into_bytes(),
            RespValue::Integer(i) => format!(":{}\r\n", i).into_bytes(),
            RespValue::BulkString(s) => format!("${}\r\n{}\r\n", s.len(), s).into_bytes(),
            RespValue::Null => b"$-1\r\n".to_vec(),
            RespValue::Array(arr) => {
                let mut buf = Vec::new();
                buf.extend_from_slice(format!("*{}\r\n", arr.len()).as_bytes());
                for item in arr {
                    buf.extend_from_slice(item.serialize().as_ref());
                }
                buf
            }
        }
    }
}

Arrays are the interesting one here — they serialize recursively, writing the element count first and then each item's own serialization.

A SET key value command ends up looking like *3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n.

Parsing Logic

I used std::io::Cursor to walk through the raw byte buffer. The first byte tells us the type, then we dispatch to a type-specific parser.

pub fn parse_resp(cursor: &mut Cursor<&[u8]>) -> Result<RespValue, String> {
    let mut type_byte = [0; 1];

    if cursor
        .read(&mut type_byte)
        .map_err(|_| "Failed to read type byte")?
        == 0
    {
        return Err("EOF".to_string());
    }

    match type_byte[0] {
        b'+' => parse_simple_string(cursor),
        b'-' => parse_error(cursor),
        b':' => parse_integer(cursor),
        b'$' => parse_bulk_string(cursor),
        b'*' => parse_array(cursor),
        _ => Err(format!("Unknown RESP type: {}", type_byte[0] as char)),
    }
}