Skip to main content

redust_resp/
error.rs

1use std::{borrow::Cow, str::Utf8Error};
2
3use serde::{de, ser};
4use thiserror::Error;
5
6/// The amount of additional data needed to finish parsing.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum Needed {
9	/// The amount of data needed is unknown.
10	Unknown,
11	/// At least this many more bytes are needed.
12	Size(usize),
13}
14
15/// An error encountered while parsing RESP.
16#[derive(Debug, Clone, PartialEq, Eq, Error)]
17pub enum ParseError {
18	/// The data ended before a complete value was parsed.
19	#[error("incomplete data")]
20	Incomplete(Needed),
21	/// A byte other than the expected type marker was found.
22	#[error("expected {:?}, found {:?}", char::from(*.expected), char::from(*.found))]
23	Unexpected {
24		/// The expected byte.
25		expected: u8,
26		/// The byte which was found.
27		found: u8,
28	},
29	/// A value started with an unknown type marker.
30	#[error("unknown type {:?}", char::from(*.0))]
31	Type(u8),
32	/// A line was not terminated by CRLF.
33	#[error("expected CRLF")]
34	Crlf,
35	/// An integer was missing, malformed, or out of range.
36	#[error("invalid integer")]
37	Integer,
38	/// A bulk string had an invalid length.
39	#[error("invalid length {0}")]
40	Length(i64),
41	/// A string was not valid UTF-8.
42	#[error("invalid UTF-8: {0}")]
43	Utf8(#[from] Utf8Error),
44}
45
46/// Errors that can be encountered when interacting with RESP.
47#[derive(Debug, Error)]
48pub enum Error<'a> {
49	/// Serialization error.
50	#[error("{0}")]
51	Message(Cow<'a, str>),
52	/// An IO error occured when writing to the buffer.
53	#[error("io error: {0}")]
54	Io(#[from] std::io::Error),
55	/// Invalid RESP syntax.
56	#[error("parse error: {0}")]
57	Parse(#[from] ParseError),
58	/// An error was indicated by the data.
59	#[error("Redis error: {0}")]
60	Redis(Cow<'a, str>),
61}
62
63impl Error<'_> {
64	/// Convert this error into an owned error.
65	pub fn into_owned(self) -> Error<'static> {
66		match self {
67			Self::Message(msg) => Error::Message(msg.into_owned().into()),
68			Self::Io(err) => Error::Io(err),
69			Self::Parse(err) => Error::Parse(err),
70			Self::Redis(msg) => Error::Redis(msg.into_owned().into()),
71		}
72	}
73
74	/// Whether this error is transient (i.e. the source is still valid)
75	pub fn is_transient(&self) -> bool {
76		matches!(self, Self::Redis(_))
77	}
78}
79
80impl ser::Error for Error<'_> {
81	fn custom<T>(msg: T) -> Self
82	where
83		T: std::fmt::Display,
84	{
85		Self::Message(msg.to_string().into())
86	}
87}
88
89impl de::Error for Error<'_> {
90	fn custom<T>(msg: T) -> Self
91	where
92		T: std::fmt::Display,
93	{
94		Self::Message(msg.to_string().into())
95	}
96}
97
98/// Result with an error type defaulting to [enum@Error].
99pub type Result<'a, T, E = Error<'a>> = std::result::Result<T, E>;