#![warn(missing_docs)]
use crate::helpers::{box_future, ExtractFromOwned};
use crate::next::Next;
use crate::req::HttpRequest;
use crate::res::HttpResponse;
#[cfg(feature = "with-wynd")]
use bytes::Bytes;
#[cfg(feature = "with-wynd")]
use http_body_util::Full;
use hyper::Method;
use std::collections::HashMap;
use std::fmt::Display;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
pub(crate) type RouteHandlerReturnType =
Pin<Box<dyn Future<Output = HttpResponse> + Send + 'static>>;
pub(crate) type RouteHandler =
Arc<dyn Fn(HttpRequest, HttpResponse) -> RouteHandlerReturnType + Send + Sync + 'static>;
#[derive(Eq, Hash, PartialEq, Clone, Debug)]
pub enum HttpMethods {
GET,
POST,
PUT,
HEAD,
DELETE,
PATCH,
OPTIONS,
}
impl From<&Method> for HttpMethods {
fn from(method: &Method) -> Self {
match method {
&Method::GET => HttpMethods::GET,
&Method::POST => HttpMethods::POST,
&Method::PUT => HttpMethods::PUT,
&Method::DELETE => HttpMethods::DELETE,
&Method::PATCH => HttpMethods::PATCH,
&Method::HEAD => HttpMethods::HEAD,
&Method::OPTIONS => HttpMethods::OPTIONS,
_ => HttpMethods::GET,
}
}
}
impl Display for HttpMethods {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let method = match self {
HttpMethods::GET => "GET",
HttpMethods::PUT => "PUT",
HttpMethods::POST => "POST",
HttpMethods::DELETE => "DELETE",
HttpMethods::PATCH => "PATCH",
HttpMethods::HEAD => "HEAD",
HttpMethods::OPTIONS => "OPTIONS",
};
write!(f, "{}", method)
}
}
pub(crate) type Routes = HashMap<String, HashMap<HttpMethods, RouteHandler>>;
pub(crate) type MiddlewareOutput =
Pin<Box<dyn Future<Output = (HttpRequest, Option<HttpResponse>)> + Send + 'static>>;
pub(crate) type MiddlewareHandler =
Arc<dyn Fn(HttpRequest, HttpResponse, Next) -> MiddlewareOutput + Send + Sync + 'static>;
#[cfg(feature = "with-wynd")]
pub(crate) type WyndHandler = Arc<
dyn Fn(
hyper::Request<Full<Bytes>>,
) -> Pin<
Box<
dyn Future<Output = hyper::Result<hyper::Response<Full<hyper::body::Bytes>>>>
+ Send,
>,
> + Send
+ Sync,
>;
pub trait RouterFns {
fn routes(&mut self) -> &mut Routes;
fn add_route<F, HFut>(&mut self, method: HttpMethods, path: &str, handler: F)
where
F: Fn(HttpRequest, HttpResponse) -> HFut + Send + Sync + 'static,
HFut: Future<Output = HttpResponse> + Send + 'static,
{
let routes = self.routes();
let wrapped_handler =
Arc::new(move |req: HttpRequest, res| box_future(handler(req, res))) as RouteHandler;
use std::collections::hash_map::Entry;
match routes.entry(path.to_string()) {
Entry::Occupied(mut e) => {
e.get_mut().insert(method, wrapped_handler);
}
Entry::Vacant(e) => {
let mut map = HashMap::new();
map.insert(method, wrapped_handler);
e.insert(map);
}
}
}
fn get<F, HFut, P>(&mut self, path: &str, handler: F) -> &mut Self
where
F: Fn(P, HttpResponse) -> HFut + Send + Sync + 'static,
HFut: Future<Output = HttpResponse> + Send + 'static,
P: ExtractFromOwned + Send + 'static,
{
self.add_route_with_extraction(HttpMethods::GET, path, handler);
self
}
fn options<F, HFut, P>(&mut self, path: &str, handler: F) -> &mut Self
where
F: Fn(P, HttpResponse) -> HFut + Send + Sync + 'static,
HFut: Future<Output = HttpResponse> + Send + 'static,
P: ExtractFromOwned + Send + 'static,
{
self.add_route_with_extraction(HttpMethods::OPTIONS, path, handler);
self
}
fn post<F, HFut, P>(&mut self, path: &str, handler: F) -> &mut Self
where
F: Fn(P, HttpResponse) -> HFut + Send + Sync + 'static,
HFut: Future<Output = HttpResponse> + Send + 'static,
P: ExtractFromOwned + Send + 'static,
{
self.add_route_with_extraction(HttpMethods::POST, path, handler);
self
}
fn put<F, HFut, P>(&mut self, path: &str, handler: F) -> &mut Self
where
F: Fn(P, HttpResponse) -> HFut + Send + Sync + 'static,
HFut: Future<Output = HttpResponse> + Send + 'static,
P: ExtractFromOwned + Send + 'static,
{
self.add_route_with_extraction(HttpMethods::PUT, path, handler);
self
}
fn delete<F, HFut, P>(&mut self, path: &str, handler: F) -> &mut Self
where
F: Fn(P, HttpResponse) -> HFut + Send + Sync + 'static,
HFut: Future<Output = HttpResponse> + Send + 'static,
P: ExtractFromOwned + Send + 'static,
{
self.add_route_with_extraction(HttpMethods::DELETE, path, handler);
self
}
fn head<F, HFut, P>(&mut self, path: &str, handler: F) -> &mut Self
where
F: Fn(P, HttpResponse) -> HFut + Send + Sync + 'static,
HFut: Future<Output = HttpResponse> + Send + 'static,
P: ExtractFromOwned + Send + 'static,
{
self.add_route_with_extraction(HttpMethods::HEAD, path, handler);
self
}
fn patch<F, HFut, P>(&mut self, path: &str, handler: F) -> &mut Self
where
F: Fn(P, HttpResponse) -> HFut + Send + Sync + 'static,
HFut: Future<Output = HttpResponse> + Send + 'static,
P: ExtractFromOwned + Send + 'static,
{
self.add_route_with_extraction(HttpMethods::PATCH, path, handler);
self
}
fn get_routes(&mut self, path: &str, method: HttpMethods) -> Option<&RouteHandler> {
let routes = self.routes();
routes.get(path).and_then(|handlers| handlers.get(&method))
}
fn add_route_with_extraction<F, HFut, P>(&mut self, method: HttpMethods, path: &str, handler: F)
where
F: Fn(P, HttpResponse) -> HFut + Send + Sync + 'static,
HFut: Future<Output = HttpResponse> + Send + 'static,
P: ExtractFromOwned + Send + 'static,
{
let handler = std::sync::Arc::new(handler);
let wrapped = move |req: HttpRequest, res: HttpResponse| {
let handler = handler.clone();
async move {
let extracted = match P::extract_from_owned(req) {
Ok(v) => v,
Err(e) => {
return res.bad_request().text(format!("Extraction failed: {}", e));
}
};
handler(extracted, res).await
}
};
self.add_route(method, path, wrapped);
}
}
pub type Middlewares = Vec<(
&'static str,
Box<
dyn Fn(
HttpRequest,
HttpResponse,
Next
)
-> Pin<Box<dyn Future<Output = (HttpRequest, Option<HttpResponse>)> + Send>>
+ Send
+ Sync,
>,
)>;