resp_rs/lib.rs
1//! Zero-copy RESP2 and RESP3 protocol parser and serializer.
2//!
3//! `resp-rs` provides high-performance parsing and serialization for the
4//! [Redis Serialization Protocol](https://redis.io/docs/latest/develop/reference/protocol-spec/)
5//! (RESP), supporting both RESP2 and RESP3.
6//!
7//! # Features
8//!
9//! - **Zero-copy parsing** using [`bytes::Bytes`] -- parsing a bulk string is an O(1)
10//! slice operation, not a copy
11//! - **RESP2 and RESP3** support with separate [`resp2::Frame`] and [`resp3::Frame`] types
12//! - **Streaming parser** ([`resp2::Parser`] / [`resp3::Parser`]) for incremental data --
13//! handles partial reads and pipelining
14//! - **Serialization** via [`resp2::frame_to_bytes`] and [`resp3::frame_to_bytes`]
15//! - **Minimal dependencies** -- only [`bytes`] and [`thiserror`]
16//! - **No async runtime required** -- pure sync parsing that works in any context
17//!
18//! # Modules
19//!
20//! | Module | Description |
21//! |--------|-------------|
22//! | [`resp2`] | RESP2 types, [`resp2::parse_frame`], [`resp2::frame_to_bytes`], [`resp2::Parser`] |
23//! | [`resp3`] | RESP3 types, [`resp3::parse_frame`], [`resp3::frame_to_bytes`], [`resp3::Parser`], [`resp3::parse_streaming_sequence`] |
24//!
25//! # Quick Start
26//!
27//! ## Parsing a RESP2 command
28//!
29//! ```
30//! use bytes::Bytes;
31//! use resp_rs::resp2::{self, Frame};
32//!
33//! // A Redis SET command on the wire
34//! let data = Bytes::from("*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n");
35//! let (frame, remaining) = resp2::parse_frame(data).unwrap();
36//!
37//! assert_eq!(frame, Frame::Array(Some(vec![
38//! Frame::BulkString(Some(Bytes::from("SET"))),
39//! Frame::BulkString(Some(Bytes::from("key"))),
40//! Frame::BulkString(Some(Bytes::from("value"))),
41//! ])));
42//! assert!(remaining.is_empty());
43//! ```
44//!
45//! ## Parsing RESP3 types
46//!
47//! RESP3 adds null, booleans, doubles, maps, sets, and more:
48//!
49//! ```
50//! use bytes::Bytes;
51//! use resp_rs::resp3::{self, Frame};
52//!
53//! // Simple string
54//! let (frame, _) = resp3::parse_frame(Bytes::from("+OK\r\n")).unwrap();
55//! assert_eq!(frame, Frame::SimpleString(Bytes::from("OK")));
56//!
57//! // Null
58//! let (frame, _) = resp3::parse_frame(Bytes::from("_\r\n")).unwrap();
59//! assert_eq!(frame, Frame::Null);
60//!
61//! // Boolean
62//! let (frame, _) = resp3::parse_frame(Bytes::from("#t\r\n")).unwrap();
63//! assert_eq!(frame, Frame::Boolean(true));
64//!
65//! // Double
66//! let (frame, _) = resp3::parse_frame(Bytes::from(",3.14\r\n")).unwrap();
67//! assert_eq!(frame, Frame::Double(3.14));
68//!
69//! // Integer
70//! let (frame, _) = resp3::parse_frame(Bytes::from(":-42\r\n")).unwrap();
71//! assert_eq!(frame, Frame::Integer(-42));
72//!
73//! // Bulk string
74//! let (frame, _) = resp3::parse_frame(Bytes::from("$5\r\nhello\r\n")).unwrap();
75//! assert_eq!(frame, Frame::BulkString(Some(Bytes::from("hello"))));
76//!
77//! // Null bulk string
78//! let (frame, _) = resp3::parse_frame(Bytes::from("$-1\r\n")).unwrap();
79//! assert_eq!(frame, Frame::BulkString(None));
80//!
81//! // Array
82//! let (frame, _) = resp3::parse_frame(Bytes::from("*2\r\n:1\r\n:2\r\n")).unwrap();
83//! assert_eq!(frame, Frame::Array(Some(vec![Frame::Integer(1), Frame::Integer(2)])));
84//!
85//! // Map
86//! let data = Bytes::from("%2\r\n+name\r\n$5\r\nAlice\r\n+age\r\n:30\r\n");
87//! let (frame, _) = resp3::parse_frame(data).unwrap();
88//! assert_eq!(frame, Frame::Map(vec![
89//! (Frame::SimpleString(Bytes::from("name")), Frame::BulkString(Some(Bytes::from("Alice")))),
90//! (Frame::SimpleString(Bytes::from("age")), Frame::Integer(30)),
91//! ]));
92//!
93//! // Set
94//! let (frame, _) = resp3::parse_frame(Bytes::from("~3\r\n:1\r\n:2\r\n:3\r\n")).unwrap();
95//! assert_eq!(frame, Frame::Set(vec![Frame::Integer(1), Frame::Integer(2), Frame::Integer(3)]));
96//!
97//! // Big number
98//! let (frame, _) = resp3::parse_frame(Bytes::from("(12345678901234567890\r\n")).unwrap();
99//! assert_eq!(frame, Frame::BigNumber(Bytes::from("12345678901234567890")));
100//!
101//! // Verbatim string
102//! let (frame, _) = resp3::parse_frame(Bytes::from("=15\r\ntxt:hello world\r\n")).unwrap();
103//! assert_eq!(frame, Frame::VerbatimString(Bytes::from("txt"), Bytes::from("hello world")));
104//!
105//! // Blob error
106//! let (frame, _) = resp3::parse_frame(Bytes::from("!5\r\nOOPS!\r\n")).unwrap();
107//! assert_eq!(frame, Frame::BlobError(Bytes::from("OOPS!")));
108//!
109//! // Error
110//! let (frame, _) = resp3::parse_frame(Bytes::from("-ERR unknown\r\n")).unwrap();
111//! assert_eq!(frame, Frame::Error(Bytes::from("ERR unknown")));
112//! ```
113//!
114//! ## Serialization
115//!
116//! Convert any [`resp2::Frame`] or [`resp3::Frame`] back to wire format:
117//!
118//! ```
119//! use bytes::Bytes;
120//! use resp_rs::resp2::{Frame, frame_to_bytes};
121//!
122//! let frame = Frame::Array(Some(vec![
123//! Frame::BulkString(Some(Bytes::from("GET"))),
124//! Frame::BulkString(Some(Bytes::from("mykey"))),
125//! ]));
126//! let wire = frame_to_bytes(&frame);
127//! assert_eq!(wire, Bytes::from("*2\r\n$3\r\nGET\r\n$5\r\nmykey\r\n"));
128//! ```
129//!
130//! Roundtrip is guaranteed: `parse_frame(frame_to_bytes(&frame)) == Ok((frame, empty))`.
131//!
132//! ## Streaming parser
133//!
134//! The [`resp2::Parser`] and [`resp3::Parser`] types buffer incremental data
135//! and yield frames as they become complete -- ideal for reading from TCP sockets.
136//!
137//! ```
138//! use bytes::Bytes;
139//! use resp_rs::resp3::{Parser, Frame};
140//!
141//! let mut parser = Parser::new();
142//!
143//! // Simulate receiving data in chunks (e.g., from TCP)
144//! parser.feed(Bytes::from("+HEL"));
145//! assert!(parser.next_frame().unwrap().is_none()); // not enough data yet
146//!
147//! parser.feed(Bytes::from("LO\r\n:42\r\n"));
148//! // Now we have two complete frames buffered
149//!
150//! let frame1 = parser.next_frame().unwrap().unwrap();
151//! assert_eq!(frame1, Frame::SimpleString(Bytes::from("HELLO")));
152//!
153//! let frame2 = parser.next_frame().unwrap().unwrap();
154//! assert_eq!(frame2, Frame::Integer(42));
155//!
156//! assert!(parser.next_frame().unwrap().is_none()); // buffer exhausted
157//! ```
158//!
159//! ## Pipelined commands
160//!
161//! Multiple frames in a single buffer parse naturally in sequence:
162//!
163//! ```
164//! use bytes::Bytes;
165//! use resp_rs::resp2::{self, Frame};
166//!
167//! let wire = Bytes::from("+OK\r\n$5\r\nhello\r\n:42\r\n");
168//!
169//! let (f1, rest) = resp2::parse_frame(wire).unwrap();
170//! assert_eq!(f1, Frame::SimpleString(Bytes::from("OK")));
171//!
172//! let (f2, rest) = resp2::parse_frame(rest).unwrap();
173//! assert_eq!(f2, Frame::BulkString(Some(Bytes::from("hello"))));
174//!
175//! let (f3, rest) = resp2::parse_frame(rest).unwrap();
176//! assert_eq!(f3, Frame::Integer(42));
177//!
178//! assert!(rest.is_empty());
179//! ```
180//!
181//! ## RESP3 streaming sequences
182//!
183//! RESP3 supports chunked/streaming data. Use [`resp3::parse_streaming_sequence`]
184//! to accumulate chunks into a complete frame:
185//!
186//! ```
187//! use bytes::Bytes;
188//! use resp_rs::resp3::{self, Frame};
189//!
190//! // Streaming string: $?\r\n followed by chunks, terminated by ;0\r\n
191//! let data = Bytes::from("$?\r\n;5\r\nHello\r\n;6\r\n World\r\n;0\r\n\r\n");
192//! let (frame, _) = resp3::parse_streaming_sequence(data).unwrap();
193//!
194//! if let Frame::StreamedString(chunks) = frame {
195//! assert_eq!(chunks.len(), 2);
196//! assert_eq!(chunks[0], Bytes::from("Hello"));
197//! assert_eq!(chunks[1], Bytes::from(" World"));
198//! }
199//! ```
200//!
201//! # Performance
202//!
203//! The parser uses offset-based internal parsing to minimize allocations. Bulk string
204//! parsing is an O(1) slice into the input buffer, not a copy.
205//!
206//! ## `parse_frame` vs `Parser`
207//!
208//! [`resp2::parse_frame`] and [`resp3::parse_frame`] parse directly from a [`bytes::Bytes`]
209//! buffer with no overhead. The [`resp2::Parser`] and [`resp3::Parser`] wrappers add
210//! incremental buffering for TCP streams, but have roughly 2x overhead per frame due to
211//! internal `BytesMut` split/unsplit operations.
212//!
213//! **If you already have a complete buffer** (e.g., a full response read from a socket),
214//! call `parse_frame` directly in a loop rather than going through `Parser`:
215//!
216//! ```
217//! use bytes::Bytes;
218//! use resp_rs::resp2;
219//!
220//! let wire = Bytes::from("+OK\r\n:42\r\n$5\r\nhello\r\n");
221//! let mut input = wire;
222//! while !input.is_empty() {
223//! match resp2::parse_frame(input) {
224//! Ok((frame, rest)) => {
225//! // process frame
226//! input = rest;
227//! }
228//! Err(resp_rs::ParseError::Incomplete) => break, // need more data
229//! Err(e) => panic!("parse error: {e}"),
230//! }
231//! }
232//! ```
233//!
234//! **Use `Parser` when** data arrives incrementally (e.g., reading from a TCP socket in
235//! chunks) and you need to buffer partial frames across reads.
236//!
237//! ## RESP2 vs RESP3
238//!
239//! RESP2 parsing is roughly 3x faster than RESP3 for simple types due to RESP2's smaller
240//! type tag match (5 variants vs 16+). The gap narrows for collection-heavy workloads
241//! where per-element parsing dominates. If your application only needs RESP2, prefer the
242//! [`resp2`] module for best performance.
243//!
244//! ## Representative timings
245//!
246//! Measured on Apple M4 (single core, criterion):
247//!
248//! | Operation | RESP2 | RESP3 |
249//! |-----------|-------|-------|
250//! | Simple string | 12 ns | 39 ns |
251//! | Bulk string | 13 ns | 39 ns |
252//! | Integer | 25 ns | 47 ns |
253//! | 3-element array | 43 ns | 82 ns |
254//! | 100-element array | 822 ns | 2.0 us |
255//! | 5-frame pipeline (direct) | 107 ns | 129 ns |
256//! | 5-frame pipeline (Parser) | 242 ns | 286 ns |
257//! | Streaming string (2 chunks) | -- | 124 ns |
258//! | Streaming array (5 elements) | -- | 226 ns |
259//!
260//! Run `cargo bench` to reproduce on your hardware.
261//!
262//! # Error Handling
263//!
264//! All parsing functions return [`Result<_, ParseError>`]. The [`ParseError::Incomplete`]
265//! variant signals that more data is needed (not a protocol error), while other
266//! variants indicate malformed input.
267//!
268//! ```
269//! use bytes::Bytes;
270//! use resp_rs::{ParseError, resp2};
271//!
272//! // Not enough data
273//! assert_eq!(resp2::parse_frame(Bytes::from("$5\r\nhel")), Err(ParseError::Incomplete));
274//!
275//! // Unknown type tag
276//! assert_eq!(resp2::parse_frame(Bytes::from("X\r\n")), Err(ParseError::InvalidTag(b'X')));
277//!
278//! // Empty input
279//! assert_eq!(resp2::parse_frame(Bytes::new()), Err(ParseError::Incomplete));
280//! ```
281
282pub mod resp2;
283pub mod resp3;
284
285/// Errors that can occur during RESP parsing.
286#[derive(Debug, Clone, PartialEq, thiserror::Error)]
287pub enum ParseError {
288 /// Not enough data to parse a complete frame.
289 #[error("incomplete data")]
290 Incomplete,
291
292 /// Invalid type tag byte at the start of a frame.
293 #[error("invalid tag byte: 0x{0:02x}")]
294 InvalidTag(u8),
295
296 /// Invalid length in a bulk string or collection.
297 #[error("invalid length")]
298 BadLength,
299
300 /// Invalid UTF-8 in a string value.
301 #[error("invalid UTF-8")]
302 Utf8Error,
303
304 /// Invalid frame format.
305 #[error("invalid format")]
306 InvalidFormat,
307
308 /// Invalid boolean value (not 't' or 'f').
309 #[error("invalid boolean value")]
310 InvalidBoolean,
311
312 /// Invalid special float (not 'inf', '-inf', or 'nan').
313 #[error("invalid special float")]
314 InvalidSpecialFloat,
315
316 /// Integer value overflowed i64 range.
317 #[error("integer overflow")]
318 Overflow,
319}