# Redust RESP
[](https://crates.io/crates/redust-resp) [](https://docs.rs/redust-resp)
A simple RESP parser for Rust.
This crate implements [RESP2](https://redis.io/docs/reference/protocol-spec/), the protocol Redis
uses to communicate with clients, on top of [serde](https://serde.rs). It powers the
[redust](https://crates.io/crates/redust) Redis client but has no dependency on any particular
network stack.
- **serde integration**: deserialize RESP directly into your own types with `from_bytes`, or
serialize them into RESP with `to_bytes`.
- **Dynamic data**: the `Data` enum represents any RESP value when the shape of a response isn't
known ahead of time. Convert between `Data` and Rust types with `from_data` and `to_data`.
- **Zero-copy**: parsed strings borrow from the input wherever possible. Owned data from the
codec shares the read buffer instead of copying out of it.
- **Errors as results**: RESP errors are returned as `Error::Redis` rather than as data.
## Usage
Deserialize RESP bytes into Rust types:
```rust
use serde::Deserialize;
#[derive(Debug, Deserialize, PartialEq)]
struct Reply<'a> {
#[serde(borrow, with = "serde_bytes")]
name: &'a [u8],
count: i64,
}
let bytes = b"*4\r\n+name\r\n$5\r\nredis\r\n+count\r\n:42\r\n";
let (reply, remaining) = redust_resp::from_bytes::<Reply>(bytes).unwrap();
assert_eq!(reply, Reply { name: b"redis", count: 42 });
assert!(remaining.is_empty());
```
Or work with dynamic `Data`:
```rust
use redust_resp::{from_bytes, from_data, to_bytes, Data};
let (data, _) = from_bytes::<Data>(b"*2\r\n$3\r\nGET\r\n$3\r\nfoo\r\n").unwrap();
assert_eq!(data, Data::Array(vec![Data::bulk_string("GET"), Data::bulk_string("foo")]));
let parts: Vec<&[u8]> = from_data(data.clone()).unwrap();
assert_eq!(parts, [b"GET", b"foo"]);
let mut buf = Vec::new();
to_bytes(&data, &mut buf).unwrap();
assert_eq!(buf, b"*2\r\n$3\r\nGET\r\n$3\r\nfoo\r\n");
```
The `util` module contains serde helpers for common Redis encodings, such as numbers stored in bulk
strings.
## Features
- `codec`: a [tokio-util](https://crates.io/crates/tokio-util) `Codec` which incrementally decodes
RESP from a byte stream into `Data`.
## Benchmarks
In-memory comparison with the parser built into [redis-rs](https://crates.io/crates/redis) 1.8.0
and [redis-protocol](https://crates.io/crates/redis-protocol) 6.0.0 (the parser used by fred).
Times are criterion's median estimate; lower is better.
### Parsing into dynamic data
`redust` borrows from the input. `redust (owned)` also calls `Data::into_owned`, as the codec does,
which is the fairer comparison since the other parsers return owned values.
| `+OK` | 18.5 ns | **34.1 ns** | 390 ns | 80.2 ns |
| `:1234567890` | 18.5 ns | **20.5 ns** | 429 ns | 58.7 ns |
| bulk string, 1 KiB | 14.3 ns | **91.0 ns** | 545 ns | 119 ns |
| bulk string, 64 KiB | 15.1 ns | **1.31 µs** | 8.51 µs | 1.49 µs |
| array of 100 × 16 B | 1.93 µs | 9.80 µs | 23.4 µs | **9.40 µs** |
| array of 1000 × 16 B | 18.8 µs | 90.9 µs | 217 µs | **87.5 µs** |
### Encoding a `SET` command
| 16 B | **210 ns** | 362 ns | 400 ns |
| 1 KiB | **219 ns** | 384 ns | 440 ns |
| 64 KiB | **1.21 µs** | 3.08 µs | 3.57 µs |
Measured on an AMD Ryzen 9 3900X running Windows 10 and Rust 1.95. To reproduce, see the
[`benches`](https://github.com/appellation/redust/tree/master/benches) crate.