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
129
130
131
132
133
134
135
136
use hyper::Body;
use hyper::Response;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct ErrorResponse {
pub code: u16,
pub kind: ErrorResponseKind,
}
pub trait ToErrorResponse {
fn to_error_response(self) -> ErrorResponse;
}
#[derive(Debug, Serialize, Deserialize)]
pub enum ErrorResponseKind {
Service(ServiceError),
Runtime(RuntimeError),
}
#[derive(Debug, Serialize, Deserialize)]
pub enum ServiceError {
Authentication,
Authorization,
Internal(String),
}
#[derive(Debug, Serialize, Deserialize)]
pub enum RuntimeError {
NoServiceMounted,
ServiceMountsAmbiguous,
NoRouteMountedInService {
service: String,
},
RouteMountsAmbiguous {
service: String,
},
RouteParamInvalid {
param_name: String,
parse_error: String,
},
QueryInvalid(String),
PostBodyReadError(String),
PostBodyInvalid(String),
SerializeHandlerResponse(String),
SerializeErrorResponse(String),
}
impl ErrorResponse {
pub fn to_hyper_response(&self) -> Response<Body> {
hyper::Response::builder()
.status(self.code)
.body(
serde_json::to_string_pretty(self)
.expect("runtime responses must be JSON-serializable")
.into(),
)
.expect("runtime responses must always be buildable")
}
}
impl ToErrorResponse for ServiceError {
fn to_error_response(self) -> ErrorResponse {
ErrorResponse {
code: self.status_code(),
kind: ErrorResponseKind::Service(self),
}
}
}
impl ToErrorResponse for RuntimeError {
fn to_error_response(self) -> ErrorResponse {
ErrorResponse {
code: self.status_code(),
kind: ErrorResponseKind::Runtime(self),
}
}
}
impl RuntimeError {
fn status_code(&self) -> u16 {
match self {
RuntimeError::NoServiceMounted => 404,
RuntimeError::NoRouteMountedInService { .. } => 404,
RuntimeError::RouteMountsAmbiguous { .. } => 500,
RuntimeError::ServiceMountsAmbiguous => 500,
RuntimeError::RouteParamInvalid { .. } => 400,
RuntimeError::QueryInvalid(_) => 400,
RuntimeError::PostBodyReadError(_) => 400,
RuntimeError::PostBodyInvalid(_) => 400,
RuntimeError::SerializeHandlerResponse(_) => 500,
RuntimeError::SerializeErrorResponse(_) => 500,
}
}
}
impl ServiceError {
pub fn status_code(&self) -> u16 {
match self {
ServiceError::Authentication => 401,
ServiceError::Authorization => 403,
ServiceError::Internal(_) => 500,
}
}
}
impl From<super::handler::ServiceError> for ServiceError {
fn from(e: super::handler::ServiceError) -> Self {
match e {
super::handler::ServiceError::Authentication => ServiceError::Authentication,
super::handler::ServiceError::Authorization => ServiceError::Authorization,
super::handler::ServiceError::Internal(e) => ServiceError::Internal(format!("{}", e)),
}
}
}