redis_protocol/
types.rs

1use crate::resp2::types::Frame as Resp2Frame;
2use crate::resp3::types::Frame as Resp3Frame;
3use bytes_utils::string::Utf8Error as BytesUtf8Error;
4use cookie_factory::GenError;
5use nom::error::{ErrorKind, FromExternalError, ParseError};
6use nom::{Err as NomError, Needed};
7use alloc::borrow::Cow;
8use alloc::format;
9use alloc::string::String;
10use core::borrow::Borrow;
11use core::fmt;
12use core::fmt::Debug;
13use core::str::Utf8Error;
14
15#[cfg(feature = "std")]
16use std::io::Error as IoError;
17
18/// Terminating bytes between frames.
19pub const CRLF: &'static str = "\r\n";
20
21/// The kind of error without any associated data.
22#[derive(Debug)]
23pub enum RedisProtocolErrorKind {
24  /// An error that occurred while encoding data.
25  EncodeError,
26  /// An error indicating that the provided buffer needs to be extended by the inner `usize` bytes before encoding can continue.
27  BufferTooSmall(usize),
28  /// An error that occurred while decoding data.
29  DecodeError,
30  #[cfg(feature = "std")]
31  /// An IO error.
32  IO(IoError),
33  /// An unknown error, or an error that can occur during encoding or decoding.
34  Unknown,
35}
36
37impl PartialEq for RedisProtocolErrorKind {
38  fn eq(&self, other: &Self) -> bool {
39    use self::RedisProtocolErrorKind::*;
40
41    match *self {
42      EncodeError => match *other {
43        EncodeError => true,
44        _ => false,
45      },
46      DecodeError => match *other {
47        DecodeError => true,
48        _ => false,
49      },
50      BufferTooSmall(amt) => match *other {
51        BufferTooSmall(_amt) => amt == amt,
52        _ => false,
53      },
54      #[cfg(feature = "std")]
55      IO(_) => match *other {
56        IO(_) => true,
57        _ => false,
58      },
59      Unknown => match *other {
60        Unknown => true,
61        _ => false,
62      },
63    }
64  }
65}
66
67impl Eq for RedisProtocolErrorKind {}
68
69impl RedisProtocolErrorKind {
70  pub fn to_str(&self) -> &'static str {
71    use self::RedisProtocolErrorKind::*;
72
73    match *self {
74      EncodeError => "Encode Error",
75      DecodeError => "Decode Error",
76      Unknown => "Unknown Error",
77      #[cfg(feature = "std")]
78      IO(_) => "IO Error",
79      BufferTooSmall(_) => "Buffer too small",
80    }
81  }
82}
83
84/// The default error type used with all external functions in this library.
85#[derive(Debug, Eq, PartialEq)]
86pub struct RedisProtocolError {
87  desc: Cow<'static, str>,
88  kind: RedisProtocolErrorKind,
89}
90
91impl RedisProtocolError {
92  pub fn buffer_too_small(amt: usize) -> Self {
93    RedisProtocolError::new(RedisProtocolErrorKind::BufferTooSmall(amt), "")
94  }
95
96  pub fn new<S: Into<Cow<'static, str>>>(kind: RedisProtocolErrorKind, desc: S) -> Self {
97    RedisProtocolError {
98      kind,
99      desc: desc.into(),
100    }
101  }
102
103  pub fn description(&self) -> &str {
104    self.desc.borrow()
105  }
106
107  pub fn new_empty() -> Self {
108    RedisProtocolError {
109      kind: RedisProtocolErrorKind::Unknown,
110      desc: "".into(),
111    }
112  }
113
114  pub fn to_string(&self) -> String {
115    format!("{}: {}", self.kind.to_str(), self.desc)
116  }
117
118  pub fn kind(&self) -> &RedisProtocolErrorKind {
119    &self.kind
120  }
121}
122
123impl fmt::Display for RedisProtocolError {
124  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
125    write!(f, "{}: {}", self.kind.to_str(), self.desc)
126  }
127}
128
129impl From<GenError> for RedisProtocolError {
130  fn from(e: GenError) -> Self {
131    match e {
132      GenError::CustomError(i) => match i {
133        1 => RedisProtocolError::new(RedisProtocolErrorKind::EncodeError, "Invalid frame kind."),
134        2 => RedisProtocolError::new(RedisProtocolErrorKind::EncodeError, "Cannot encode NaN."),
135        3 => RedisProtocolError::new(RedisProtocolErrorKind::EncodeError, "Cannot stream non aggregate type."),
136        _ => RedisProtocolError::new_empty(),
137      },
138      GenError::InvalidOffset => RedisProtocolError::new(RedisProtocolErrorKind::EncodeError, "Invalid offset."),
139      GenError::BufferTooSmall(b) => RedisProtocolError::buffer_too_small(b),
140      _ => RedisProtocolError::new_empty(),
141    }
142  }
143}
144
145impl From<NomError<nom::error::Error<&[u8]>>> for RedisProtocolError {
146  fn from(e: NomError<nom::error::Error<&[u8]>>) -> Self {
147    if let NomError::Incomplete(Needed::Size(ref s)) = e {
148      RedisProtocolError {
149        kind: RedisProtocolErrorKind::BufferTooSmall(s.get()),
150        desc: Cow::Borrowed(""),
151      }
152    } else {
153      let desc = match e {
154        NomError::Failure(inner) => format!("Failure: {:?}", inner.code),
155        NomError::Error(inner) => format!("Error: {:?}", inner.code),
156        _ => format!("{:?}", e),
157      };
158
159      RedisProtocolError {
160        kind: RedisProtocolErrorKind::DecodeError,
161        desc: Cow::Owned(desc),
162      }
163    }
164  }
165}
166
167impl From<NomError<&[u8]>> for RedisProtocolError {
168  fn from(e: NomError<&[u8]>) -> Self {
169    if let NomError::Incomplete(Needed::Size(ref s)) = e {
170      RedisProtocolError {
171        kind: RedisProtocolErrorKind::BufferTooSmall(s.get()),
172        desc: Cow::Borrowed(""),
173      }
174    } else {
175      RedisProtocolError {
176        kind: RedisProtocolErrorKind::DecodeError,
177        desc: Cow::Owned(format!("{:?}", e)),
178      }
179    }
180  }
181}
182
183#[cfg(feature = "std")]
184impl From<IoError> for RedisProtocolError {
185  fn from(e: IoError) -> Self {
186    RedisProtocolError::new(RedisProtocolErrorKind::IO(e), "IO Error")
187  }
188}
189
190impl<I> From<RedisParseError<I>> for RedisProtocolError
191where
192  I: Debug,
193{
194  fn from(e: RedisParseError<I>) -> Self {
195    RedisProtocolError::new(RedisProtocolErrorKind::DecodeError, format!("{:?}", e))
196  }
197}
198
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 {
228        ref context,
229        ref message,
230      } => write!(f, "{}: {}", context, message),
231      RedisParseError::Nom(input, kind) => write!(f, "{:?} at {:?}", kind, input),
232      RedisParseError::Incomplete(ref needed) => write!(f, "Incomplete({:?})", needed),
233    }
234  }
235}
236
237impl<I> RedisParseError<I> {
238  pub fn new_custom<S: Into<Cow<'static, str>>>(ctx: &'static str, message: S) -> Self {
239    RedisParseError::Custom {
240      context: ctx,
241      message: message.into(),
242    }
243  }
244
245  pub fn into_nom_error(self) -> nom::Err<RedisParseError<I>> {
246    match self {
247      RedisParseError::Incomplete(n) => nom::Err::Incomplete(n),
248      _ => nom::Err::Failure(self),
249    }
250  }
251}
252
253impl<I> ParseError<I> for RedisParseError<I> {
254  fn from_error_kind(input: I, kind: ErrorKind) -> Self {
255    RedisParseError::Nom(input, kind)
256  }
257
258  fn append(_: I, _: ErrorKind, other: Self) -> Self {
259    other
260  }
261}
262
263impl<I, O> ParseError<(I, O)> for RedisParseError<I> {
264  fn from_error_kind(input: (I, O), kind: ErrorKind) -> Self {
265    RedisParseError::Nom(input.0, kind)
266  }
267
268  fn append(_: (I, O), _: ErrorKind, other: Self) -> Self {
269    other
270  }
271}
272
273impl<I, E> FromExternalError<I, E> for RedisParseError<I> {
274  fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self {
275    RedisParseError::Nom(input, kind)
276  }
277}
278
279impl<I, O, E> FromExternalError<(I, O), E> for RedisParseError<I> {
280  fn from_external_error(input: (I, O), kind: ErrorKind, _e: E) -> Self {
281    RedisParseError::Nom(input.0, kind)
282  }
283}
284
285impl<I> From<nom::Err<RedisParseError<I>>> for RedisParseError<I> {
286  fn from(e: NomError<RedisParseError<I>>) -> Self {
287    match e {
288      NomError::Incomplete(n) => RedisParseError::Incomplete(n),
289      NomError::Failure(e) | NomError::Error(e) => e,
290    }
291  }
292}
293
294impl<B, I> From<BytesUtf8Error<B>> for RedisParseError<I> {
295  fn from(e: BytesUtf8Error<B>) -> Self {
296    e.utf8_error().into()
297  }
298}
299
300impl<I> From<Utf8Error> for RedisParseError<I> {
301  fn from(e: Utf8Error) -> Self {
302    RedisParseError::new_custom("parse_utf8", format!("{}", e))
303  }
304}
305
306/// A cluster redirection message.
307///
308/// <https://redis.io/topics/cluster-spec#redirection-and-resharding>
309#[derive(Clone, Debug, Eq, PartialEq)]
310pub enum Redirection {
311  Moved { slot: u16, server: String },
312  Ask { slot: u16, server: String },
313}
314
315impl Redirection {
316  pub fn to_resp2_frame(&self) -> Resp2Frame {
317    let inner = match *self {
318      Redirection::Moved { ref slot, ref server } => format!("MOVED {} {}", slot, server),
319      Redirection::Ask { ref slot, ref server } => format!("ASK {} {}", slot, server),
320    };
321
322    Resp2Frame::Error(inner.into())
323  }
324
325  pub fn to_resp3_frame(&self) -> Resp3Frame {
326    let inner = match *self {
327      Redirection::Moved { ref slot, ref server } => format!("MOVED {} {}", slot, server),
328      Redirection::Ask { ref slot, ref server } => format!("ASK {} {}", slot, server),
329    };
330
331    Resp3Frame::SimpleError {
332      data: inner.into(),
333      attributes: None,
334    }
335  }
336}
337
338#[cfg(test)]
339mod tests {
340  use super::*;
341  use std::num::NonZeroUsize;
342
343  #[test]
344  fn should_create_empty_error() {
345    let e = RedisProtocolError::new_empty();
346
347    assert_eq!(e.description(), "");
348    assert_eq!(e.kind(), &RedisProtocolErrorKind::Unknown);
349  }
350
351  #[test]
352  fn should_create_encode_error() {
353    let e = RedisProtocolError::new(RedisProtocolErrorKind::EncodeError, "foo");
354
355    assert_eq!(e.description(), "foo");
356    assert_eq!(e.kind(), &RedisProtocolErrorKind::EncodeError);
357  }
358
359  #[test]
360  fn should_create_decode_error() {
361    let e = RedisProtocolError::new(RedisProtocolErrorKind::DecodeError, "foo");
362
363    assert_eq!(e.description(), "foo");
364    assert_eq!(e.kind(), &RedisProtocolErrorKind::DecodeError);
365  }
366
367  #[test]
368  fn should_create_buf_too_small_error() {
369    let e = RedisProtocolError::new(RedisProtocolErrorKind::BufferTooSmall(10), "foo");
370
371    assert_eq!(e.description(), "foo");
372    assert_eq!(e.kind(), &RedisProtocolErrorKind::BufferTooSmall(10));
373  }
374
375  #[test]
376  fn should_cast_from_nom_incomplete() {
377    let n: NomError<&[u8]> = NomError::Incomplete(Needed::Size(NonZeroUsize::new(10).unwrap()));
378    let e = RedisProtocolError::from(n);
379
380    assert_eq!(e.kind(), &RedisProtocolErrorKind::BufferTooSmall(10));
381  }
382
383  #[test]
384  fn should_print_error_kinds() {
385    assert_eq!(RedisProtocolErrorKind::EncodeError.to_str(), "Encode Error");
386    assert_eq!(RedisProtocolErrorKind::DecodeError.to_str(), "Decode Error");
387    assert_eq!(RedisProtocolErrorKind::Unknown.to_str(), "Unknown Error");
388    assert_eq!(RedisProtocolErrorKind::BufferTooSmall(10).to_str(), "Buffer too small");
389  }
390}