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}