garage_api_common/
common_error.rs1use std::convert::TryFrom;
2
3use err_derive::Error;
4use hyper::StatusCode;
5
6use garage_util::error::Error as GarageError;
7
8use garage_model::helper::error::Error as HelperError;
9
10#[derive(Debug, Error)]
12pub enum CommonError {
13 #[error(display = "Internal error: {}", _0)]
16 InternalError(#[error(source)] GarageError),
17
18 #[error(display = "Internal error (Hyper error): {}", _0)]
20 Hyper(#[error(source)] hyper::Error),
21
22 #[error(display = "Internal error (HTTP error): {}", _0)]
24 Http(#[error(source)] http::Error),
25
26 #[error(display = "Forbidden: {}", _0)]
29 Forbidden(String),
30
31 #[error(display = "Bad request: {}", _0)]
33 BadRequest(String),
34
35 #[error(display = "Invalid header value: {}", _0)]
37 InvalidHeader(#[error(source)] hyper::header::ToStrError),
38
39 #[error(display = "Bucket not found: {}", _0)]
44 NoSuchBucket(String),
45
46 #[error(display = "Bucket already exists")]
48 BucketAlreadyExists,
49
50 #[error(display = "Tried to delete a non-empty bucket")]
52 BucketNotEmpty,
53
54 #[error(display = "Invalid bucket name: {}", _0)]
57 InvalidBucketName(String),
58}
59
60#[macro_export]
61macro_rules! commonErrorDerivative {
62 ( $error_struct: ident ) => {
63 impl From<garage_util::error::Error> for $error_struct {
64 fn from(err: garage_util::error::Error) -> Self {
65 Self::Common(CommonError::InternalError(err))
66 }
67 }
68 impl From<http::Error> for $error_struct {
69 fn from(err: http::Error) -> Self {
70 Self::Common(CommonError::Http(err))
71 }
72 }
73 impl From<hyper::Error> for $error_struct {
74 fn from(err: hyper::Error) -> Self {
75 Self::Common(CommonError::Hyper(err))
76 }
77 }
78 impl From<hyper::header::ToStrError> for $error_struct {
79 fn from(err: hyper::header::ToStrError) -> Self {
80 Self::Common(CommonError::InvalidHeader(err))
81 }
82 }
83 impl CommonErrorDerivative for $error_struct {}
84 };
85}
86
87pub use commonErrorDerivative;
88
89impl CommonError {
90 pub fn http_status_code(&self) -> StatusCode {
91 match self {
92 CommonError::InternalError(
93 GarageError::Timeout | GarageError::RemoteError(_) | GarageError::Quorum(..),
94 ) => StatusCode::SERVICE_UNAVAILABLE,
95 CommonError::InternalError(_) | CommonError::Hyper(_) | CommonError::Http(_) => {
96 StatusCode::INTERNAL_SERVER_ERROR
97 }
98 CommonError::BadRequest(_) => StatusCode::BAD_REQUEST,
99 CommonError::Forbidden(_) => StatusCode::FORBIDDEN,
100 CommonError::NoSuchBucket(_) => StatusCode::NOT_FOUND,
101 CommonError::BucketNotEmpty | CommonError::BucketAlreadyExists => StatusCode::CONFLICT,
102 CommonError::InvalidBucketName(_) | CommonError::InvalidHeader(_) => {
103 StatusCode::BAD_REQUEST
104 }
105 }
106 }
107
108 pub fn aws_code(&self) -> &'static str {
109 match self {
110 CommonError::Forbidden(_) => "AccessDenied",
111 CommonError::InternalError(
112 GarageError::Timeout | GarageError::RemoteError(_) | GarageError::Quorum(..),
113 ) => "ServiceUnavailable",
114 CommonError::InternalError(_) | CommonError::Hyper(_) | CommonError::Http(_) => {
115 "InternalError"
116 }
117 CommonError::BadRequest(_) => "InvalidRequest",
118 CommonError::NoSuchBucket(_) => "NoSuchBucket",
119 CommonError::BucketAlreadyExists => "BucketAlreadyExists",
120 CommonError::BucketNotEmpty => "BucketNotEmpty",
121 CommonError::InvalidBucketName(_) => "InvalidBucketName",
122 CommonError::InvalidHeader(_) => "InvalidHeaderValue",
123 }
124 }
125
126 pub fn bad_request<M: ToString>(msg: M) -> Self {
127 CommonError::BadRequest(msg.to_string())
128 }
129}
130
131impl TryFrom<HelperError> for CommonError {
132 type Error = HelperError;
133
134 fn try_from(err: HelperError) -> Result<Self, HelperError> {
135 match err {
136 HelperError::Internal(i) => Ok(Self::InternalError(i)),
137 HelperError::BadRequest(b) => Ok(Self::BadRequest(b)),
138 HelperError::InvalidBucketName(n) => Ok(Self::InvalidBucketName(n)),
139 HelperError::NoSuchBucket(n) => Ok(Self::NoSuchBucket(n)),
140 e => Err(e),
141 }
142 }
143}
144
145pub fn pass_helper_error(err: HelperError) -> CommonError {
151 match CommonError::try_from(err) {
152 Ok(e) => e,
153 Err(e) => panic!("Helper error `{}` should hot have happenned here", e),
154 }
155}
156
157pub fn helper_error_as_internal(err: HelperError) -> CommonError {
158 match err {
159 HelperError::Internal(e) => CommonError::InternalError(e),
160 e => CommonError::InternalError(GarageError::Message(e.to_string())),
161 }
162}
163
164pub trait CommonErrorDerivative: From<CommonError> {
165 fn internal_error<M: ToString>(msg: M) -> Self {
166 Self::from(CommonError::InternalError(GarageError::Message(
167 msg.to_string(),
168 )))
169 }
170
171 fn bad_request<M: ToString>(msg: M) -> Self {
172 Self::from(CommonError::BadRequest(msg.to_string()))
173 }
174
175 fn forbidden<M: ToString>(msg: M) -> Self {
176 Self::from(CommonError::Forbidden(msg.to_string()))
177 }
178}
179
180pub trait OkOrBadRequest {
182 type S;
183 fn ok_or_bad_request<M: AsRef<str>>(self, reason: M) -> Result<Self::S, CommonError>;
184}
185
186impl<T, E> OkOrBadRequest for Result<T, E>
187where
188 E: std::fmt::Display,
189{
190 type S = T;
191 fn ok_or_bad_request<M: AsRef<str>>(self, reason: M) -> Result<T, CommonError> {
192 match self {
193 Ok(x) => Ok(x),
194 Err(e) => Err(CommonError::BadRequest(format!(
195 "{}: {}",
196 reason.as_ref(),
197 e
198 ))),
199 }
200 }
201}
202
203impl<T> OkOrBadRequest for Option<T> {
204 type S = T;
205 fn ok_or_bad_request<M: AsRef<str>>(self, reason: M) -> Result<T, CommonError> {
206 match self {
207 Some(x) => Ok(x),
208 None => Err(CommonError::BadRequest(reason.as_ref().to_string())),
209 }
210 }
211}
212
213pub trait OkOrInternalError {
215 type S;
216 fn ok_or_internal_error<M: AsRef<str>>(self, reason: M) -> Result<Self::S, CommonError>;
217}
218
219impl<T, E> OkOrInternalError for Result<T, E>
220where
221 E: std::fmt::Display,
222{
223 type S = T;
224 fn ok_or_internal_error<M: AsRef<str>>(self, reason: M) -> Result<T, CommonError> {
225 match self {
226 Ok(x) => Ok(x),
227 Err(e) => Err(CommonError::InternalError(GarageError::Message(format!(
228 "{}: {}",
229 reason.as_ref(),
230 e
231 )))),
232 }
233 }
234}
235
236impl<T> OkOrInternalError for Option<T> {
237 type S = T;
238 fn ok_or_internal_error<M: AsRef<str>>(self, reason: M) -> Result<T, CommonError> {
239 match self {
240 Some(x) => Ok(x),
241 None => Err(CommonError::InternalError(GarageError::Message(
242 reason.as_ref().to_string(),
243 ))),
244 }
245 }
246}