redis_protocol/
error.rs

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/// The kind of error without any associated data.
18#[derive(Debug)]
19pub enum RedisProtocolErrorKind {
20  /// An error that occurred while encoding data.
21  EncodeError,
22  /// An error indicating that the provided buffer needs to be extended by the inner `usize` bytes before encoding
23  /// can continue.
24  BufferTooSmall(usize),
25  /// An error that occurred while decoding data.
26  DecodeError,
27  #[cfg(feature = "std")]
28  /// An IO error.
29  IO(IoError),
30  /// An unknown error, or an error that can occur during encoding or decoding.
31  Unknown,
32  /// An error parsing a value or converting between types.
33  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/// The default error type used with all external functions in this library.
74#[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
211/// A struct defining parse errors when decoding frames.
212pub 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}