Skip to main content

resp_proto/
error.rs

1//! Error types for RESP protocol parsing.
2
3/// Error type for RESP parsing operations.
4#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
5pub enum ParseError {
6    /// Need more data to complete parsing.
7    /// This is not a fatal error - the caller should buffer more data and retry.
8    #[error("incomplete data")]
9    Incomplete,
10
11    /// Invalid type prefix byte.
12    #[error("invalid prefix byte: {0:#04x}")]
13    InvalidPrefix(u8),
14
15    /// Invalid integer format.
16    #[error("invalid integer: {0}")]
17    InvalidInteger(String),
18
19    /// Invalid bulk string length.
20    #[error("invalid bulk string length")]
21    InvalidLength,
22
23    /// Protocol violation.
24    #[error("protocol error: {0}")]
25    Protocol(String),
26
27    /// Unknown or unsupported command.
28    #[error("unknown command: {0}")]
29    UnknownCommand(String),
30
31    /// Wrong number of arguments for command.
32    #[error("wrong number of arguments: {0}")]
33    WrongArity(String),
34
35    /// Collection size exceeds maximum allowed limit.
36    #[error("collection too large: {0} elements exceeds limit")]
37    CollectionTooLarge(usize),
38
39    /// Nesting depth exceeds maximum allowed limit.
40    #[error("nesting too deep: depth {0} exceeds limit")]
41    NestingTooDeep(usize),
42
43    /// Bulk string exceeds maximum allowed size.
44    #[error("bulk string too long: {len} bytes exceeds {max} byte limit")]
45    BulkStringTooLong { len: usize, max: usize },
46
47    /// Invalid floating point number (RESP3).
48    #[cfg(feature = "resp3")]
49    #[error("invalid double: {0}")]
50    InvalidDouble(String),
51
52    /// Invalid boolean value (RESP3).
53    #[cfg(feature = "resp3")]
54    #[error("invalid boolean: expected 't' or 'f'")]
55    InvalidBoolean,
56
57    /// Invalid verbatim string format (RESP3).
58    #[cfg(feature = "resp3")]
59    #[error("invalid verbatim string format")]
60    InvalidVerbatimFormat,
61}
62
63impl ParseError {
64    /// Returns true if this error indicates more data is needed.
65    #[inline]
66    pub fn is_incomplete(&self) -> bool {
67        matches!(self, ParseError::Incomplete)
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn test_is_incomplete() {
77        assert!(ParseError::Incomplete.is_incomplete());
78        assert!(!ParseError::InvalidPrefix(0x00).is_incomplete());
79        assert!(!ParseError::InvalidInteger("test".to_string()).is_incomplete());
80        assert!(!ParseError::InvalidLength.is_incomplete());
81        assert!(!ParseError::Protocol("test".to_string()).is_incomplete());
82        assert!(!ParseError::UnknownCommand("test".to_string()).is_incomplete());
83        assert!(!ParseError::WrongArity("test".to_string()).is_incomplete());
84        assert!(!ParseError::CollectionTooLarge(100).is_incomplete());
85        assert!(!ParseError::BulkStringTooLong { len: 100, max: 50 }.is_incomplete());
86    }
87
88    #[test]
89    fn test_error_display() {
90        assert_eq!(format!("{}", ParseError::Incomplete), "incomplete data");
91        assert_eq!(
92            format!("{}", ParseError::InvalidPrefix(0x42)),
93            "invalid prefix byte: 0x42"
94        );
95        assert_eq!(
96            format!("{}", ParseError::InvalidInteger("bad".to_string())),
97            "invalid integer: bad"
98        );
99        assert_eq!(
100            format!("{}", ParseError::InvalidLength),
101            "invalid bulk string length"
102        );
103        assert_eq!(
104            format!("{}", ParseError::Protocol("error".to_string())),
105            "protocol error: error"
106        );
107        assert_eq!(
108            format!("{}", ParseError::UnknownCommand("FOO".to_string())),
109            "unknown command: FOO"
110        );
111        assert_eq!(
112            format!("{}", ParseError::WrongArity("wrong".to_string())),
113            "wrong number of arguments: wrong"
114        );
115        assert_eq!(
116            format!("{}", ParseError::CollectionTooLarge(999999)),
117            "collection too large: 999999 elements exceeds limit"
118        );
119    }
120
121    #[test]
122    fn test_error_debug() {
123        let err = ParseError::Incomplete;
124        let debug_str = format!("{:?}", err);
125        assert!(debug_str.contains("Incomplete"));
126    }
127
128    #[test]
129    fn test_error_clone() {
130        let err1 = ParseError::InvalidPrefix(0x42);
131        let err2 = err1.clone();
132        assert_eq!(err1, err2);
133    }
134
135    #[test]
136    fn test_error_eq() {
137        assert_eq!(ParseError::Incomplete, ParseError::Incomplete);
138        assert_ne!(ParseError::Incomplete, ParseError::InvalidLength);
139        assert_eq!(
140            ParseError::InvalidPrefix(0x42),
141            ParseError::InvalidPrefix(0x42)
142        );
143        assert_ne!(
144            ParseError::InvalidPrefix(0x42),
145            ParseError::InvalidPrefix(0x43)
146        );
147    }
148
149    #[cfg(feature = "resp3")]
150    #[test]
151    fn test_resp3_errors() {
152        assert!(!ParseError::InvalidDouble("bad".to_string()).is_incomplete());
153        assert!(!ParseError::InvalidBoolean.is_incomplete());
154        assert!(!ParseError::InvalidVerbatimFormat.is_incomplete());
155
156        assert_eq!(
157            format!("{}", ParseError::InvalidDouble("bad".to_string())),
158            "invalid double: bad"
159        );
160        assert_eq!(
161            format!("{}", ParseError::InvalidBoolean),
162            "invalid boolean: expected 't' or 'f'"
163        );
164        assert_eq!(
165            format!("{}", ParseError::InvalidVerbatimFormat),
166            "invalid verbatim string format"
167        );
168    }
169}