Skip to main content

disco_types/
error.rs

1use crate::{request::RequestError, status::StatusCode};
2use config::ConfigError;
3use serde::{Deserialize, Serialize, de::DeserializeOwned};
4use snafu::Snafu;
5use std::fmt::{self, Display, Formatter};
6use std::io::Error as IoError;
7
8/// Errors which can be serialized in a response body.
9///
10/// This trait can be used to define a standard error type returned by all API endpoints. When a
11/// request fails for any reason, the body of the response will contain a serialization of
12/// the error that caused the failure, upcasted into an anyhow::Error. If the error is an instance
13/// of the standard error type for that particular API, it can be deserialized and downcasted to
14/// this type on the client.
15///
16/// Other errors (those which don't downcast to the API's error type, such as errors generated from
17/// the [tide] framework) will be serialized as strings using their [Display] instance and encoded
18/// as an API error using the [catch_all](Error::catch_all) function.
19pub trait Error: std::error::Error + Serialize + DeserializeOwned + Send + Sync + 'static {
20    fn catch_all(status: StatusCode, msg: String) -> Self;
21    fn status(&self) -> StatusCode;
22
23    fn message(&self) -> String {
24        self.to_string()
25    }
26
27    fn from_io_error(source: IoError) -> Self {
28        Self::catch_all(StatusCode::INTERNAL_SERVER_ERROR, source.to_string())
29    }
30
31    fn from_config_error(source: ConfigError) -> Self {
32        Self::catch_all(StatusCode::INTERNAL_SERVER_ERROR, source.to_string())
33    }
34
35    fn from_route_error<E: Display>(source: RouteError<E>) -> Self {
36        Self::catch_all(source.status(), source.to_string())
37    }
38
39    fn from_request_error(source: RequestError) -> Self {
40        Self::catch_all(StatusCode::BAD_REQUEST, source.to_string())
41    }
42
43    fn from_socket_error<E: Display>(source: SocketError<E>) -> Self {
44        Self::catch_all(source.status(), source.to_string())
45    }
46}
47
48/// The simplest possible implementation of [Error].
49///
50/// You can use this to get up and running quickly if you don't want to create your own error type.
51/// However, we strongly reccommend creating a custom error type and implementing [Error] for it, so
52/// that you can provide more informative and structured error responses specific to your API.
53#[derive(Clone, Debug, Snafu, Serialize, Deserialize, PartialEq, Eq)]
54#[snafu(display("Error {}: {}", status, message))]
55pub struct ServerError {
56    pub status: StatusCode,
57    pub message: String,
58}
59
60impl Error for ServerError {
61    fn catch_all(status: StatusCode, message: String) -> Self {
62        Self { status, message }
63    }
64
65    fn status(&self) -> StatusCode {
66        self.status
67    }
68
69    fn message(&self) -> String {
70        self.message.clone()
71    }
72}
73
74impl From<IoError> for ServerError {
75    fn from(source: IoError) -> Self {
76        Self::from_io_error(source)
77    }
78}
79
80impl From<ConfigError> for ServerError {
81    fn from(source: ConfigError) -> Self {
82        Self::from_config_error(source)
83    }
84}
85
86impl<E: Display> From<RouteError<E>> for ServerError {
87    fn from(source: RouteError<E>) -> Self {
88        Self::from_route_error(source)
89    }
90}
91
92impl From<RequestError> for ServerError {
93    fn from(source: RequestError) -> Self {
94        Self::from_request_error(source)
95    }
96}
97
98impl<E: Display> From<SocketError<E>> for ServerError {
99    fn from(source: SocketError<E>) -> Self {
100        Self::from_socket_error(source)
101    }
102}
103
104/// An error returned by a route handler.
105///
106/// [RouteError] encapsulates application specific errors `E` returned by the user-installed handler
107/// itself. It also includes errors in the route dispatching logic, such as failures to turn the
108/// result of the user-installed handler into an HTTP response.
109#[derive(Debug)]
110pub enum RouteError<E> {
111    AppSpecific(E),
112    Request(RequestError),
113    UnsupportedContentType,
114    Binary(anyhow::Error),
115    Json(serde_json::Error),
116    Tide { status: StatusCode, message: String },
117    ExportMetrics(String),
118    IncorrectMethod { expected: String },
119}
120
121impl<E: Display> Display for RouteError<E> {
122    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
123        match self {
124            Self::AppSpecific(err) => write!(f, "{}", err),
125            Self::Request(err) => write!(f, "{}", err),
126            Self::UnsupportedContentType => write!(f, "requested content type is not supported"),
127            Self::Binary(err) => write!(f, "error creating byte stream: {}", err),
128            Self::Json(err) => write!(f, "error creating JSON response: {}", err),
129            Self::Tide { status, message } => write!(f, "{status}: {message}"),
130            Self::ExportMetrics(msg) => write!(f, "error exporting metrics: {msg}"),
131            Self::IncorrectMethod { expected } => {
132                write!(f, "route may only be called as {}", expected)
133            }
134        }
135    }
136}
137
138impl<E> RouteError<E> {
139    pub fn status(&self) -> StatusCode {
140        match self {
141            Self::Request(_) | Self::UnsupportedContentType | Self::IncorrectMethod { .. } => {
142                StatusCode::BAD_REQUEST
143            }
144            _ => StatusCode::INTERNAL_SERVER_ERROR,
145        }
146    }
147
148    pub fn map_app_specific<E2>(self, f: impl Fn(E) -> E2) -> RouteError<E2> {
149        match self {
150            RouteError::AppSpecific(e) => RouteError::AppSpecific(f(e)),
151            RouteError::Request(e) => RouteError::Request(e),
152            RouteError::UnsupportedContentType => RouteError::UnsupportedContentType,
153            RouteError::Binary(err) => RouteError::Binary(err),
154            RouteError::Json(err) => RouteError::Json(err),
155            RouteError::Tide { status, message } => RouteError::Tide { status, message },
156            RouteError::ExportMetrics(msg) => RouteError::ExportMetrics(msg),
157            Self::IncorrectMethod { expected } => RouteError::IncorrectMethod { expected },
158        }
159    }
160}
161
162impl<E> From<RequestError> for RouteError<E> {
163    fn from(err: RequestError) -> Self {
164        Self::Request(err)
165    }
166}
167
168/// An error returned by a socket handler.
169///
170/// [SocketError] encapsulates application specific errors `E` returned by the user-installed
171/// handler itself. It also includes errors in the socket protocol, such as failures to turn
172/// messages sent by the user-installed handler into WebSockets messages.
173#[derive(Debug)]
174pub enum SocketError<E> {
175    AppSpecific(E),
176    Request(RequestError),
177    Binary(anyhow::Error),
178    Json(serde_json::Error),
179    WebSockets(String),
180    UnsupportedMessageType,
181    Closed,
182    IncorrectMethod { expected: String, actual: String },
183}
184
185impl<E> SocketError<E> {
186    pub fn status(&self) -> StatusCode {
187        match self {
188            Self::Request(_) | Self::UnsupportedMessageType | Self::IncorrectMethod { .. } => {
189                StatusCode::BAD_REQUEST
190            }
191            _ => StatusCode::INTERNAL_SERVER_ERROR,
192        }
193    }
194
195    pub fn map_app_specific<E2>(self, f: &impl Fn(E) -> E2) -> SocketError<E2> {
196        match self {
197            Self::AppSpecific(e) => SocketError::AppSpecific(f(e)),
198            Self::Request(e) => SocketError::Request(e),
199            Self::Binary(e) => SocketError::Binary(e),
200            Self::Json(e) => SocketError::Json(e),
201            Self::WebSockets(e) => SocketError::WebSockets(e),
202            Self::UnsupportedMessageType => SocketError::UnsupportedMessageType,
203            Self::Closed => SocketError::Closed,
204            Self::IncorrectMethod { expected, actual } => {
205                SocketError::IncorrectMethod { expected, actual }
206            }
207        }
208    }
209}
210
211impl<E: Display> Display for SocketError<E> {
212    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
213        match self {
214            Self::AppSpecific(e) => write!(f, "{}", e),
215            Self::Request(e) => write!(f, "{}", e),
216            Self::Binary(e) => write!(f, "error creating byte stream: {}", e),
217            Self::Json(e) => write!(f, "error creating JSON message: {}", e),
218            Self::WebSockets(e) => write!(f, "WebSockets protocol error: {}", e),
219            Self::UnsupportedMessageType => {
220                write!(f, "unsupported content type for WebSockets message")
221            }
222            Self::Closed => write!(f, "connection closed"),
223            Self::IncorrectMethod { expected, actual } => write!(
224                f,
225                "endpoint must be called as {}, but was called as {}",
226                expected, actual
227            ),
228        }
229    }
230}
231
232impl<E> From<RequestError> for SocketError<E> {
233    fn from(err: RequestError) -> Self {
234        Self::Request(err)
235    }
236}
237
238impl<E> From<anyhow::Error> for SocketError<E> {
239    fn from(err: anyhow::Error) -> Self {
240        Self::Binary(err)
241    }
242}
243
244impl<E> From<serde_json::Error> for SocketError<E> {
245    fn from(err: serde_json::Error) -> Self {
246        Self::Json(err)
247    }
248}