1use http::StatusCode;
2use rskit_errors::{AppError, ErrorCode};
3
4pub type HttpRequest<B = Vec<u8>> = http::Request<B>;
6
7pub type HttpResponse<B = Vec<u8>> = http::Response<B>;
9
10pub type HttpHeaders = http::HeaderMap;
12
13pub type HttpStatusCode = http::StatusCode;
15
16#[must_use]
18pub fn status_to_error_code(status: StatusCode) -> ErrorCode {
19 match status.as_u16() {
20 400 => ErrorCode::InvalidInput,
21 401 => ErrorCode::Unauthorized,
22 403 => ErrorCode::Forbidden,
23 404 => ErrorCode::NotFound,
24 409 => ErrorCode::Conflict,
25 429 => ErrorCode::RateLimited,
26 408 => ErrorCode::Cancelled,
27 500 => ErrorCode::Internal,
28 502 => ErrorCode::ExternalService,
29 503 => ErrorCode::ServiceUnavailable,
30 504 => ErrorCode::Timeout,
31 _ if status.is_client_error() || status.is_server_error() => ErrorCode::ExternalService,
32 _ => ErrorCode::Internal,
33 }
34}
35
36#[must_use]
38pub fn is_success_status(status: StatusCode) -> bool {
39 status.is_success()
40}
41
42#[must_use]
44pub fn app_error_status(error: &AppError) -> StatusCode {
45 error.http_status()
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn maps_http_status_to_error_code() {
54 assert_eq!(
55 status_to_error_code(StatusCode::BAD_REQUEST),
56 ErrorCode::InvalidInput
57 );
58 assert_eq!(
59 status_to_error_code(StatusCode::NOT_FOUND),
60 ErrorCode::NotFound
61 );
62 assert_eq!(
63 status_to_error_code(StatusCode::TOO_MANY_REQUESTS),
64 ErrorCode::RateLimited
65 );
66 assert_eq!(
67 status_to_error_code(StatusCode::BAD_GATEWAY),
68 ErrorCode::ExternalService
69 );
70 assert_eq!(
71 status_to_error_code(StatusCode::SERVICE_UNAVAILABLE),
72 ErrorCode::ServiceUnavailable
73 );
74 assert_eq!(
75 status_to_error_code(StatusCode::GATEWAY_TIMEOUT),
76 ErrorCode::Timeout
77 );
78 assert_eq!(
79 status_to_error_code(StatusCode::INTERNAL_SERVER_ERROR),
80 ErrorCode::Internal
81 );
82 }
83
84 #[test]
85 fn canonical_server_statuses_round_trip_through_error_code() {
86 for code in [
87 ErrorCode::ServiceUnavailable,
88 ErrorCode::Timeout,
89 ErrorCode::ExternalService,
90 ErrorCode::Internal,
91 ] {
92 assert_eq!(status_to_error_code(code.http_status()), code);
93 }
94 }
95
96 #[test]
97 fn cancelled_status_round_trips_through_error_code() {
98 let status = ErrorCode::Cancelled.http_status();
99 assert_eq!(status_to_error_code(status), ErrorCode::Cancelled);
100 }
101
102 #[test]
103 fn exposes_protocol_types_without_framework_coupling() {
104 let request: HttpRequest = http::Request::builder()
105 .uri("/healthz")
106 .body(Vec::new())
107 .unwrap();
108 let response: HttpResponse = http::Response::builder()
109 .status(StatusCode::OK)
110 .body(Vec::new())
111 .unwrap();
112
113 assert_eq!(request.uri(), "/healthz");
114 assert!(is_success_status(response.status()));
115 }
116}