tide_disco/
error.rs

1// Copyright (c) 2022 Espresso Systems (espressosys.com)
2// This file is part of the tide-disco library.
3
4// You should have received a copy of the MIT License
5// along with the tide-disco library. If not, see <https://mit-license.org/>.
6
7use crate::{request::RequestError, route::RouteError, socket::SocketError, StatusCode};
8use config::ConfigError;
9use serde::{de::DeserializeOwned, Deserialize, Serialize};
10use snafu::Snafu;
11use std::fmt::Display;
12use std::io::Error as IoError;
13
14/// Errors which can be serialized in a response body.
15///
16/// This trait can be used to define a standard error type returned by all API endpoints. When a
17/// request fails for any reason, the body of the response will contain a serialization of
18/// the error that caused the failure, upcasted into an anyhow::Error. If the error is an instance
19/// of the standard error type for that particular API, it can be deserialized and downcasted to
20/// this type on the client.
21///
22/// Other errors (those which don't downcast to the API's error type, such as errors generated from
23/// the [tide] framework) will be serialized as strings using their [Display] instance and encoded
24/// as an API error using the [catch_all](Error::catch_all) function.
25pub trait Error: std::error::Error + Serialize + DeserializeOwned + Send + Sync + 'static {
26    fn catch_all(status: StatusCode, msg: String) -> Self;
27    fn status(&self) -> StatusCode;
28
29    fn from_io_error(source: IoError) -> Self {
30        Self::catch_all(StatusCode::INTERNAL_SERVER_ERROR, source.to_string())
31    }
32
33    fn from_config_error(source: ConfigError) -> Self {
34        Self::catch_all(StatusCode::INTERNAL_SERVER_ERROR, source.to_string())
35    }
36
37    fn from_route_error<E: Display>(source: RouteError<E>) -> Self {
38        Self::catch_all(source.status(), source.to_string())
39    }
40
41    fn from_request_error(source: RequestError) -> Self {
42        Self::catch_all(StatusCode::BAD_REQUEST, source.to_string())
43    }
44
45    fn from_socket_error<E: Display>(source: SocketError<E>) -> Self {
46        Self::catch_all(source.status(), source.to_string())
47    }
48
49    fn into_tide_error(self) -> tide::Error {
50        tide::Error::new(self.status(), self)
51    }
52
53    fn from_server_error(source: tide::Error) -> Self {
54        match source.downcast::<Self>() {
55            Ok(err) => err,
56            Err(source) => Self::catch_all(source.status().into(), source.to_string()),
57        }
58    }
59}
60
61/// The simplest possible implementation of [Error].
62///
63/// You can use this to get up and running quickly if you don't want to create your own error type.
64/// However, we strongly reccommend creating a custom error type and implementing [Error] for it, so
65/// that you can provide more informative and structured error responses specific to your API.
66#[derive(Clone, Debug, Snafu, Serialize, Deserialize, PartialEq, Eq)]
67#[snafu(display("Error {}: {}", status, message))]
68pub struct ServerError {
69    pub status: StatusCode,
70    pub message: String,
71}
72
73impl Error for ServerError {
74    fn catch_all(status: StatusCode, message: String) -> Self {
75        Self { status, message }
76    }
77
78    fn status(&self) -> StatusCode {
79        self.status
80    }
81}
82
83impl From<IoError> for ServerError {
84    fn from(source: IoError) -> Self {
85        Self::from_io_error(source)
86    }
87}
88
89impl From<ConfigError> for ServerError {
90    fn from(source: ConfigError) -> Self {
91        Self::from_config_error(source)
92    }
93}
94
95impl<E: Display> From<RouteError<E>> for ServerError {
96    fn from(source: RouteError<E>) -> Self {
97        Self::from_route_error(source)
98    }
99}
100
101impl From<RequestError> for ServerError {
102    fn from(source: RequestError) -> Self {
103        Self::from_request_error(source)
104    }
105}
106
107impl<E: Display> From<SocketError<E>> for ServerError {
108    fn from(source: SocketError<E>) -> Self {
109        Self::from_socket_error(source)
110    }
111}