1use alloc::{borrow::Cow, format, string::FromUtf8Error};
2use cookie_factory::GenError;
3use core::{borrow::Borrow, fmt, fmt::Debug, str::Utf8Error};
4use nom::{
5 error::{ErrorKind, FromExternalError, ParseError},
6 Err as NomError,
7 Needed,
8};
9
10#[cfg(feature = "bytes")]
11use bytes_utils::string::Utf8Error as BytesUtf8Error;
12#[cfg(feature = "std")]
13use std::error::Error;
14#[cfg(feature = "std")]
15use std::io::Error as IoError;
16
17#[derive(Debug)]
19pub enum RedisProtocolErrorKind {
20 EncodeError,
22 BufferTooSmall(usize),
25 DecodeError,
27 #[cfg(feature = "std")]
28 IO(IoError),
30 Unknown,
32 Parse,
34}
35
36impl PartialEq for RedisProtocolErrorKind {
37 fn eq(&self, other: &Self) -> bool {
38 use self::RedisProtocolErrorKind::*;
39
40 match *self {
41 EncodeError => matches!(other, EncodeError),
42 DecodeError => matches!(other, DecodeError),
43 BufferTooSmall(amt) => match other {
44 BufferTooSmall(_amt) => amt == *_amt,
45 _ => false,
46 },
47 #[cfg(feature = "std")]
48 IO(_) => matches!(other, IO(_)),
49 Unknown => matches!(other, Unknown),
50 Parse => matches!(other, Parse),
51 }
52 }
53}
54
55impl Eq for RedisProtocolErrorKind {}
56
57impl RedisProtocolErrorKind {
58 pub fn to_str(&self) -> &'static str {
59 use self::RedisProtocolErrorKind::*;
60
61 match *self {
62 EncodeError => "Encode Error",
63 DecodeError => "Decode Error",
64 Unknown => "Unknown Error",
65 Parse => "Parse Error",
66 #[cfg(feature = "std")]
67 IO(_) => "IO Error",
68 BufferTooSmall(_) => "Buffer too small",
69 }
70 }
71}
72
73#[derive(Debug, Eq, PartialEq)]
75pub struct RedisProtocolError {
76 details: Cow<'static, str>,
77 kind: RedisProtocolErrorKind,
78}
79
80impl RedisProtocolError {
81 pub fn buffer_too_small(amt: usize) -> Self {
82 RedisProtocolError::new(RedisProtocolErrorKind::BufferTooSmall(amt), "")
83 }
84
85 pub fn new<S: Into<Cow<'static, str>>>(kind: RedisProtocolErrorKind, desc: S) -> Self {
86 RedisProtocolError {
87 kind,
88 details: desc.into(),
89 }
90 }
91
92 #[cfg(feature = "convert")]
93 pub(crate) fn new_parse<S: Into<Cow<'static, str>>>(desc: S) -> Self {
94 RedisProtocolError {
95 kind: RedisProtocolErrorKind::Parse,
96 details: desc.into(),
97 }
98 }
99
100 pub fn details(&self) -> &str {
101 self.details.borrow()
102 }
103
104 pub fn new_empty() -> Self {
105 RedisProtocolError {
106 kind: RedisProtocolErrorKind::Unknown,
107 details: "".into(),
108 }
109 }
110
111 pub fn kind(&self) -> &RedisProtocolErrorKind {
112 &self.kind
113 }
114}
115
116impl fmt::Display for RedisProtocolError {
117 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118 write!(f, "{}: {}", self.kind.to_str(), self.details)
119 }
120}
121
122impl From<GenError> for RedisProtocolError {
123 fn from(e: GenError) -> Self {
124 match e {
125 GenError::CustomError(i) => match i {
126 1 => RedisProtocolError::new(RedisProtocolErrorKind::EncodeError, "Invalid frame kind."),
127 2 => RedisProtocolError::new(RedisProtocolErrorKind::EncodeError, "Cannot encode NaN."),
128 3 => RedisProtocolError::new(RedisProtocolErrorKind::EncodeError, "Cannot stream non aggregate type."),
129 _ => RedisProtocolError::new_empty(),
130 },
131 GenError::InvalidOffset => RedisProtocolError::new(RedisProtocolErrorKind::EncodeError, "Invalid offset."),
132 GenError::BufferTooSmall(b) => RedisProtocolError::buffer_too_small(b),
133 _ => RedisProtocolError::new_empty(),
134 }
135 }
136}
137
138impl From<NomError<nom::error::Error<&[u8]>>> for RedisProtocolError {
139 fn from(e: NomError<nom::error::Error<&[u8]>>) -> Self {
140 if let NomError::Incomplete(Needed::Size(s)) = e {
141 RedisProtocolError {
142 kind: RedisProtocolErrorKind::BufferTooSmall(s.get()),
143 details: Cow::Borrowed(""),
144 }
145 } else {
146 let desc = match e {
147 NomError::Failure(inner) => format!("Failure: {:?}", inner.code),
148 NomError::Error(inner) => format!("Error: {:?}", inner.code),
149 _ => format!("{:?}", e),
150 };
151
152 RedisProtocolError {
153 kind: RedisProtocolErrorKind::DecodeError,
154 details: Cow::Owned(desc),
155 }
156 }
157 }
158}
159
160impl From<NomError<&[u8]>> for RedisProtocolError {
161 fn from(e: NomError<&[u8]>) -> Self {
162 if let NomError::Incomplete(Needed::Size(s)) = e {
163 RedisProtocolError {
164 kind: RedisProtocolErrorKind::BufferTooSmall(s.get()),
165 details: Cow::Borrowed(""),
166 }
167 } else {
168 RedisProtocolError {
169 kind: RedisProtocolErrorKind::DecodeError,
170 details: Cow::Owned(format!("{:?}", e)),
171 }
172 }
173 }
174}
175
176#[cfg(feature = "std")]
177impl From<IoError> for RedisProtocolError {
178 fn from(e: IoError) -> Self {
179 RedisProtocolError::new(RedisProtocolErrorKind::IO(e), "IO Error")
180 }
181}
182
183impl<I> From<RedisParseError<I>> for RedisProtocolError
184where
185 I: Debug,
186{
187 fn from(e: RedisParseError<I>) -> Self {
188 RedisProtocolError::new(RedisProtocolErrorKind::DecodeError, format!("{:?}", e))
189 }
190}
191
192impl From<FromUtf8Error> for RedisProtocolError {
193 fn from(e: FromUtf8Error) -> Self {
194 RedisProtocolError::new(RedisProtocolErrorKind::Unknown, format!("{:?}", e))
195 }
196}
197
198#[cfg(feature = "bytes")]
199impl<B> From<BytesUtf8Error<B>> for RedisProtocolError {
200 fn from(e: BytesUtf8Error<B>) -> Self {
201 e.utf8_error().into()
202 }
203}
204
205impl From<Utf8Error> for RedisProtocolError {
206 fn from(e: Utf8Error) -> Self {
207 RedisProtocolError::new(RedisProtocolErrorKind::DecodeError, format!("{}", e))
208 }
209}
210
211pub enum RedisParseError<I> {
213 Custom {
214 context: &'static str,
215 message: Cow<'static, str>,
216 },
217 Incomplete(Needed),
218 Nom(I, ErrorKind),
219}
220
221impl<I> fmt::Debug for RedisParseError<I>
222where
223 I: Debug,
224{
225 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
226 match self {
227 RedisParseError::Custom { context, message } => write!(f, "{}: {}", context, message),
228 RedisParseError::Nom(input, kind) => write!(f, "{:?} at {:?}", kind, input),
229 RedisParseError::Incomplete(needed) => write!(f, "Incomplete({:?})", needed),
230 }
231 }
232}
233
234impl<I> RedisParseError<I> {
235 pub fn new_custom<S: Into<Cow<'static, str>>>(ctx: &'static str, message: S) -> Self {
236 RedisParseError::Custom {
237 context: ctx,
238 message: message.into(),
239 }
240 }
241
242 pub fn into_nom_error(self) -> nom::Err<RedisParseError<I>> {
243 match self {
244 RedisParseError::Incomplete(n) => nom::Err::Incomplete(n),
245 _ => nom::Err::Failure(self),
246 }
247 }
248}
249
250impl<I> ParseError<I> for RedisParseError<I> {
251 fn from_error_kind(input: I, kind: ErrorKind) -> Self {
252 RedisParseError::Nom(input, kind)
253 }
254
255 fn append(_: I, _: ErrorKind, other: Self) -> Self {
256 other
257 }
258}
259
260impl<I, O> ParseError<(I, O)> for RedisParseError<I> {
261 fn from_error_kind(input: (I, O), kind: ErrorKind) -> Self {
262 RedisParseError::Nom(input.0, kind)
263 }
264
265 fn append(_: (I, O), _: ErrorKind, other: Self) -> Self {
266 other
267 }
268}
269
270impl<I, E> FromExternalError<I, E> for RedisParseError<I> {
271 fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self {
272 RedisParseError::Nom(input, kind)
273 }
274}
275
276impl<I, O, E> FromExternalError<(I, O), E> for RedisParseError<I> {
277 fn from_external_error(input: (I, O), kind: ErrorKind, _e: E) -> Self {
278 RedisParseError::Nom(input.0, kind)
279 }
280}
281
282impl<I> From<nom::Err<RedisParseError<I>>> for RedisParseError<I> {
283 fn from(e: NomError<RedisParseError<I>>) -> Self {
284 match e {
285 NomError::Incomplete(n) => RedisParseError::Incomplete(n),
286 NomError::Failure(e) | NomError::Error(e) => e,
287 }
288 }
289}
290
291#[cfg(feature = "bytes")]
292impl<B, I> From<BytesUtf8Error<B>> for RedisParseError<I> {
293 fn from(e: BytesUtf8Error<B>) -> Self {
294 e.utf8_error().into()
295 }
296}
297
298impl<I> From<Utf8Error> for RedisParseError<I> {
299 fn from(e: Utf8Error) -> Self {
300 RedisParseError::new_custom("parse_utf8", format!("{}", e))
301 }
302}
303
304#[cfg(feature = "std")]
305impl std::error::Error for RedisProtocolError {
306 fn description(&self) -> &str {
307 self.details()
308 }
309
310 fn source(&self) -> Option<&(dyn Error + 'static)> {
311 match self.kind {
312 RedisProtocolErrorKind::IO(ref e) => Some(e),
313 _ => None,
314 }
315 }
316}
317
318#[cfg(feature = "convert")]
319impl From<core::num::ParseIntError> for RedisProtocolError {
320 fn from(value: core::num::ParseIntError) -> Self {
321 RedisProtocolError::new_parse(format!("{:?}", value))
322 }
323}
324
325#[cfg(feature = "convert")]
326impl From<core::num::ParseFloatError> for RedisProtocolError {
327 fn from(value: core::num::ParseFloatError) -> Self {
328 RedisProtocolError::new_parse(format!("{:?}", value))
329 }
330}