Skip to main content

kevy_resp/
error.rs

1//! Protocol-level parsing error shared by request + reply parsers,
2//! plus the command-layer error frame type.
3
4/// Why a buffer could not be parsed into a command (or reply). A frame
5/// that is merely incomplete is not an error: the parsers answer
6/// `Ok(None)` for it.
7///
8/// ```
9/// let e = kevy_resp::parse_command(b"*x\r\n").unwrap_err();
10/// assert_eq!(e.to_string(), "malformed frame: bad multibulk count");
11/// ```
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13#[non_exhaustive]
14pub enum ProtocolError {
15    /// A malformed frame that can never become valid (e.g. bad length prefix).
16    ///
17    /// ```
18    /// use kevy_resp::{ProtocolError, parse_command};
19    ///
20    /// let err = parse_command(b"*1\r\n$x\r\n").unwrap_err();
21    /// assert!(matches!(err, ProtocolError::Malformed(_)));
22    /// ```
23    Malformed(&'static str),
24}
25
26impl std::fmt::Display for ProtocolError {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        match self {
29            Self::Malformed(why) => write!(f, "malformed frame: {why}"),
30        }
31    }
32}
33
34impl std::error::Error for ProtocolError {}
35
36/// A command-layer error destined for the wire as a RESP error frame.
37///
38/// Carries the complete, already-prefixed message text (`ERR …` /
39/// `WRONGTYPE …` / `INDEXBUILDING …`); the dispatch layer encodes it
40/// verbatim into a `-<text>\r\n` reply. The dedicated type keeps parse
41/// and dispatch helpers from using bare `&'static str` as an error
42/// currency.
43///
44/// ```
45/// let e = kevy_resp::CmdError::from("ERR syntax error");
46/// assert_eq!(e.as_wire(), "ERR syntax error");
47/// ```
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49#[non_exhaustive]
50pub enum CmdError {
51    /// The complete wire message for the error frame.
52    ///
53    /// ```
54    /// use kevy_resp::{CmdError, encode_error};
55    ///
56    /// let CmdError::Wire(text) = CmdError::from("WRONGTYPE bad key") else { unreachable!() };
57    /// let mut out = Vec::new();
58    /// encode_error(&mut out, text);
59    /// assert_eq!(out, b"-WRONGTYPE bad key\r\n");
60    /// ```
61    Wire(&'static str),
62}
63
64impl CmdError {
65    /// The wire text encoded into the RESP error frame.
66    pub fn as_wire(self) -> &'static str {
67        match self {
68            Self::Wire(s) => s,
69        }
70    }
71}
72
73impl std::fmt::Display for CmdError {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        f.write_str(self.as_wire())
76    }
77}
78
79impl std::error::Error for CmdError {}
80
81impl From<&'static str> for CmdError {
82    fn from(s: &'static str) -> Self {
83        Self::Wire(s)
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn a_malformed_frame_renders_its_reason_and_has_no_source() {
93        let e = ProtocolError::Malformed("bad bulk length");
94        assert_eq!(e.to_string(), "malformed frame: bad bulk length");
95        assert!(std::error::Error::source(&e).is_none());
96    }
97}