aboutsummaryrefslogtreecommitdiff
path: root/src/api/error.rs
blob: 42a7ab10dbdef2493f3a01c4138c76b736a0c434 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use err_derive::Error;
use hyper::StatusCode;

use garage_util::error::Error as GarageError;

#[derive(Debug, Error)]
pub enum Error {
	// Category: internal error
	#[error(display = "Internal error: {}", _0)]
	InternalError(#[error(source)] GarageError),

	#[error(display = "Internal error (Hyper error): {}", _0)]
	Hyper(#[error(source)] hyper::Error),

	#[error(display = "Internal error (HTTP error): {}", _0)]
	HTTP(#[error(source)] http::Error),

	// Category: cannot process
	#[error(display = "Forbidden: {}", _0)]
	Forbidden(String),

	#[error(display = "Not found")]
	NotFound,

	// Category: bad request
	#[error(display = "Invalid UTF-8: {}", _0)]
	InvalidUTF8Str(#[error(source)] std::str::Utf8Error),

	#[error(display = "Invalid UTF-8: {}", _0)]
	InvalidUTF8String(#[error(source)] std::string::FromUtf8Error),

	#[error(display = "Invalid base64: {}", _0)]
	InvalidBase64(#[error(source)] base64::DecodeError),

	#[error(display = "Invalid XML: {}", _0)]
	InvalidXML(String),

	#[error(display = "Invalid header value: {}", _0)]
	InvalidHeader(#[error(source)] hyper::header::ToStrError),

	#[error(display = "Invalid HTTP range: {:?}", _0)]
	InvalidRange(#[error(from)] http_range::HttpRangeParseError),

	#[error(display = "Bad request: {}", _0)]
	BadRequest(String),
}

impl From<roxmltree::Error> for Error {
	fn from(err: roxmltree::Error) -> Self {
		Self::InvalidXML(format!("{}", err))
	}
}

impl Error {
	pub fn http_status_code(&self) -> StatusCode {
		match self {
			Error::NotFound => StatusCode::NOT_FOUND,
			Error::Forbidden(_) => StatusCode::FORBIDDEN,
			Error::InternalError(GarageError::RPC(_)) => StatusCode::SERVICE_UNAVAILABLE,
			Error::InternalError(_) | Error::Hyper(_) | Error::HTTP(_) => {
				StatusCode::INTERNAL_SERVER_ERROR
			}
			_ => StatusCode::BAD_REQUEST,
		}
	}
}

pub trait OkOrBadRequest {
	type S2;
	fn ok_or_bad_request(self, reason: &'static str) -> Self::S2;
}

impl<T, E> OkOrBadRequest for Result<T, E>
where
	E: std::fmt::Display,
{
	type S2 = Result<T, Error>;
	fn ok_or_bad_request(self, reason: &'static str) -> Result<T, Error> {
		match self {
			Ok(x) => Ok(x),
			Err(e) => Err(Error::BadRequest(format!("{}: {}", reason, e))),
		}
	}
}

impl<T> OkOrBadRequest for Option<T> {
	type S2 = Result<T, Error>;
	fn ok_or_bad_request(self, reason: &'static str) -> Result<T, Error> {
		match self {
			Some(x) => Ok(x),
			None => Err(Error::BadRequest(format!("{}", reason))),
		}
	}
}

pub trait OkOrInternalError {
	type S2;
	fn ok_or_internal_error(self, reason: &'static str) -> Self::S2;
}

impl<T, E> OkOrInternalError for Result<T, E>
where
	E: std::fmt::Display,
{
	type S2 = Result<T, Error>;
	fn ok_or_internal_error(self, reason: &'static str) -> Result<T, Error> {
		match self {
			Ok(x) => Ok(x),
			Err(e) => Err(Error::InternalError(GarageError::Message(format!(
				"{}: {}",
				reason, e
			)))),
		}
	}
}

impl<T> OkOrInternalError for Option<T> {
	type S2 = Result<T, Error>;
	fn ok_or_internal_error(self, reason: &'static str) -> Result<T, Error> {
		match self {
			Some(x) => Ok(x),
			None => Err(Error::InternalError(GarageError::Message(format!(
				"{}",
				reason
			)))),
		}
	}
}