eva_common/
hyper_tools.rs

1use crate::value::{to_value, Value};
2use crate::ErrorKind;
3use hyper::{http, Body, HeaderMap, Response, StatusCode};
4use serde::Serialize;
5use std::error::Error;
6
7pub const DEFAULT_MIME: &str = "application/octet-stream";
8
9#[macro_export]
10macro_rules! hyper_response {
11    ($code: expr, $message: expr) => {
12        hyper::Response::builder()
13            .status($code)
14            .body(Body::from($message))
15    };
16    ($code: expr) => {
17        hyper::Response::builder().status($code).body(Body::empty())
18    };
19}
20
21pub type HResult = std::result::Result<HContent, crate::Error>;
22
23pub trait HResultX {
24    fn into_hyper_response(self) -> Result<Response<Body>, http::Error>;
25}
26
27impl HResultX for HResult {
28    fn into_hyper_response(self) -> Result<Response<Body>, http::Error> {
29        match self {
30            Ok(resp) => match resp {
31                HContent::Data(v, mime, header_map) => {
32                    let mut r = Response::builder();
33                    if let Some(mt) = mime {
34                        if mt.starts_with("text/") {
35                            r = r.header(
36                                hyper::header::CONTENT_TYPE,
37                                &format!("{};charset=utf-8", mt),
38                            );
39                        } else {
40                            r = r.header(hyper::header::CONTENT_TYPE, mt);
41                        }
42                    } else {
43                        r = r.header(hyper::header::CONTENT_TYPE, DEFAULT_MIME);
44                    }
45                    let mut result = r.status(StatusCode::OK).body(Body::from(v))?;
46                    if let Some(h) = header_map {
47                        result.headers_mut().extend(h);
48                    }
49                    Ok(result)
50                }
51                HContent::Value(val) => match serde_json::to_vec(&val) {
52                    Ok(v) => Response::builder()
53                        .header(hyper::header::CONTENT_TYPE, "application/json")
54                        .status(StatusCode::OK)
55                        .body(Body::from(v)),
56                    Err(e) => {
57                        hyper_response!(StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
58                    }
59                },
60                HContent::Redirect(l) => Response::builder()
61                    .status(StatusCode::MOVED_PERMANENTLY)
62                    .header(hyper::header::LOCATION, l)
63                    .body(Body::empty()),
64                HContent::HyperResult(r) => r,
65            },
66            Err(e) if e.kind() == ErrorKind::ResourceNotFound => {
67                hyper_response!(StatusCode::NOT_FOUND, e.to_string())
68            }
69            Err(e) if e.kind() == ErrorKind::AccessDenied => {
70                hyper_response!(StatusCode::FORBIDDEN, e.to_string())
71            }
72            Err(e) if e.kind() == ErrorKind::InvalidParameter => {
73                hyper_response!(StatusCode::BAD_REQUEST, e.to_string())
74            }
75            Err(e) => {
76                hyper_response!(StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
77            }
78        }
79    }
80}
81
82pub enum HContent {
83    Data(Vec<u8>, Option<&'static str>, Option<HeaderMap>),
84    Value(Value),
85    Redirect(String),
86    HyperResult(Result<Response<Body>, http::Error>),
87}
88
89impl HContent {
90    /// # Panics
91    ///
92    /// Should not panic
93    pub fn ok() -> Self {
94        #[derive(Serialize)]
95        struct OK {
96            ok: bool,
97        }
98        HContent::Value(to_value(OK { ok: true }).unwrap())
99    }
100    /// # Panics
101    ///
102    /// Should not panic
103    pub fn not_ok() -> Self {
104        #[derive(Serialize)]
105        struct OK {
106            ok: bool,
107        }
108        HContent::Value(to_value(OK { ok: false }).unwrap())
109    }
110}
111
112impl From<hyper_static::serve::Error> for crate::Error {
113    fn from(e: hyper_static::serve::Error) -> Self {
114        match e.kind() {
115            hyper_static::serve::ErrorKind::Internal => {
116                Self::newc(ErrorKind::FunctionFailed, e.source())
117            }
118            hyper_static::serve::ErrorKind::NotFound => {
119                Self::newc(ErrorKind::ResourceNotFound, e.source())
120            }
121            hyper_static::serve::ErrorKind::Forbidden => {
122                Self::newc(ErrorKind::AccessDenied, e.source())
123            }
124            hyper_static::serve::ErrorKind::BadRequest => {
125                Self::newc(ErrorKind::InvalidParameter, e.source())
126            }
127        }
128    }
129}