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//! # Error Handling
202//!
203//! All parsing functions return [`Result<_, ParseError>`]. The [`ParseError::Incomplete`]
204//! variant signals that more data is needed (not a protocol error), while other
205//! variants indicate malformed input.
206//!
207//! ```
208//! use bytes::Bytes;
209//! use resp_rs::{ParseError, resp2};
210//!
211//! // Not enough data
212//! assert_eq!(resp2::parse_frame(Bytes::from("$5\r\nhel")), Err(ParseError::Incomplete));
213//!
214//! // Unknown type tag
215//! assert_eq!(resp2::parse_frame(Bytes::from("X\r\n")), Err(ParseError::InvalidTag(b'X')));
216//!
217//! // Empty input
218//! assert_eq!(resp2::parse_frame(Bytes::new()), Err(ParseError::Incomplete));
219//! ```
220
221pub mod resp2;
222pub mod resp3;
223
224/// Errors that can occur during RESP parsing.
225#[derive(Debug, Clone, PartialEq, thiserror::Error)]
226pub enum ParseError {
227 /// Not enough data to parse a complete frame.
228 #[error("incomplete data")]
229 Incomplete,
230
231 /// Invalid type tag byte at the start of a frame.
232 #[error("invalid tag byte: 0x{0:02x}")]
233 InvalidTag(u8),
234
235 /// Invalid length in a bulk string or collection.
236 #[error("invalid length")]
237 BadLength,
238
239 /// Invalid UTF-8 in a string value.
240 #[error("invalid UTF-8")]
241 Utf8Error,
242
243 /// Invalid frame format.
244 #[error("invalid format")]
245 InvalidFormat,
246
247 /// Invalid boolean value (not 't' or 'f').
248 #[error("invalid boolean value")]
249 InvalidBoolean,
250
251 /// Invalid special float (not 'inf', '-inf', or 'nan').
252 #[error("invalid special float")]
253 InvalidSpecialFloat,
254
255 /// Integer value overflowed i64 range.
256 #[error("integer overflow")]
257 Overflow,
258}