Skip to main content

ember_protocol/
types.rs

1//! RESP3 frame types.
2//!
3//! The [`Frame`] enum represents a single parsed RESP3 value.
4//! Blob strings use `Bytes` for efficient, reference-counted storage
5//! that avoids unnecessary copies when moving data through the pipeline.
6
7use bytes::Bytes;
8
9/// A single RESP3 protocol frame.
10///
11/// Covers the core types needed for basic Redis-compatible commands:
12/// strings, errors, integers, bulk data, arrays, null, and maps.
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum Frame {
15    /// Simple string response, e.g. `+OK\r\n`.
16    /// Used for short, non-binary status replies.
17    Simple(String),
18
19    /// Error response, e.g. `-ERR unknown command\r\n`.
20    Error(String),
21
22    /// 64-bit signed integer, e.g. `:42\r\n`.
23    Integer(i64),
24
25    /// Bulk (binary-safe) string, e.g. `$5\r\nhello\r\n`.
26    /// Uses `Bytes` for zero-copy-friendly handling.
27    Bulk(Bytes),
28
29    /// Ordered array of frames, e.g. `*2\r\n+hello\r\n+world\r\n`.
30    Array(Vec<Frame>),
31
32    /// Null value, e.g. `_\r\n`.
33    Null,
34
35    /// Ordered map of key-value frame pairs, e.g. `%1\r\n+key\r\n+val\r\n`.
36    ///
37    /// Uses `Vec` instead of `HashMap` to preserve insertion order (RESP3
38    /// maps are ordered) and because typical maps in Redis responses are
39    /// small enough that linear scan beats hashing overhead.
40    Map(Vec<(Frame, Frame)>),
41}
42
43/// Pre-computed wire bytes for the most common responses.
44/// Writing these directly to the output buffer avoids per-field
45/// serialization overhead.
46pub mod wire {
47    /// `+OK\r\n`
48    pub const OK: &[u8] = b"+OK\r\n";
49    /// `+PONG\r\n`
50    pub const PONG: &[u8] = b"+PONG\r\n";
51    /// `_\r\n`
52    pub const NULL: &[u8] = b"_\r\n";
53    /// `:0\r\n`
54    pub const ZERO: &[u8] = b":0\r\n";
55    /// `:1\r\n`
56    pub const ONE: &[u8] = b":1\r\n";
57    /// `:-1\r\n`
58    pub const NEG_ONE: &[u8] = b":-1\r\n";
59}
60
61impl Frame {
62    /// Returns `true` if this frame is a null value.
63    pub fn is_null(&self) -> bool {
64        matches!(self, Frame::Null)
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn frame_equality() {
74        assert_eq!(Frame::Simple("OK".into()), Frame::Simple("OK".into()));
75        assert_ne!(Frame::Simple("OK".into()), Frame::Simple("ERR".into()));
76        assert_eq!(Frame::Integer(42), Frame::Integer(42));
77        assert_eq!(Frame::Null, Frame::Null);
78    }
79
80    #[test]
81    fn is_null() {
82        assert!(Frame::Null.is_null());
83        assert!(!Frame::Simple("OK".into()).is_null());
84        assert!(!Frame::Integer(0).is_null());
85    }
86
87    #[test]
88    fn clone_bulk() {
89        let frame = Frame::Bulk(Bytes::from_static(b"hello"));
90        let cloned = frame.clone();
91        assert_eq!(frame, cloned);
92    }
93}