use core::fmt;
use viz_core::{
BoxHandler, Handler, HandlerExt, IntoResponse, Method, Next, Request, Response, Result,
Transform,
};
macro_rules! export_internal_verb {
($name:ident $verb:tt) => {
#[doc = concat!(" Appends a handler buy the HTTP `", stringify!($verb), "` verb into the route.")]
#[must_use]
pub fn $name<H, O>(self, handler: H) -> Self
where
H: Handler<Request, Output = Result<O>> + Clone,
O: IntoResponse,
{
self.on(Method::$verb, handler)
}
};
}
macro_rules! export_verb {
($name:ident $verb:ty) => {
#[doc = concat!(" Creates a route with a handler and HTTP `", stringify!($verb), "` verb pair.")]
#[must_use]
pub fn $name<H, O>(handler: H) -> Route
where
H: Handler<Request, Output = Result<O>> + Clone,
O: IntoResponse,
{
Route::new().$name(handler)
}
};
}
#[derive(Clone, Default)]
pub struct Route {
pub(crate) methods: Vec<(Method, BoxHandler)>,
}
impl Route {
#[must_use]
pub const fn new() -> Self {
Self {
methods: Vec::new(),
}
}
#[must_use]
pub fn push(mut self, method: Method, handler: BoxHandler) -> Self {
match self
.methods
.iter_mut()
.find(|(m, _)| m == method)
.map(|(_, e)| e)
{
Some(h) => *h = handler,
None => self.methods.push((method, handler)),
}
self
}
#[must_use]
pub fn on<H, O>(self, method: Method, handler: H) -> Self
where
H: Handler<Request, Output = Result<O>> + Clone,
O: IntoResponse,
{
self.push(method, handler.map_into_response().boxed())
}
#[must_use]
pub fn any<H, O>(self, handler: H) -> Self
where
H: Handler<Request, Output = Result<O>> + Clone,
O: IntoResponse,
{
[
Method::GET,
Method::POST,
Method::PUT,
Method::DELETE,
Method::HEAD,
Method::OPTIONS,
Method::CONNECT,
Method::PATCH,
Method::TRACE,
]
.into_iter()
.fold(self, |route, method| route.on(method, handler.clone()))
}
repeat!(
export_internal_verb
get GET
post POST
put PUT
delete DELETE
head HEAD
options OPTIONS
connect CONNECT
patch PATCH
trace TRACE
);
#[must_use]
pub fn map_handler<F>(self, f: F) -> Self
where
F: Fn(BoxHandler) -> BoxHandler,
{
self.into_iter()
.map(|(method, handler)| (method, f(handler)))
.collect()
}
#[must_use]
pub fn with<T>(self, t: T) -> Self
where
T: Transform<BoxHandler>,
T::Output: Handler<Request, Output = Result<Response>> + Clone,
{
self.map_handler(|handler| t.transform(handler).boxed())
}
#[must_use]
pub fn with_handler<H>(self, f: H) -> Self
where
H: Handler<Next<Request, BoxHandler>, Output = Result<Response>> + Clone,
{
self.map_handler(|handler| handler.around(f.clone()).boxed())
}
}
impl IntoIterator for Route {
type Item = (Method, BoxHandler);
type IntoIter = std::vec::IntoIter<(Method, BoxHandler)>;
fn into_iter(self) -> Self::IntoIter {
self.methods.into_iter()
}
}
impl FromIterator<(Method, BoxHandler)> for Route {
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = (Method, BoxHandler)>,
{
Self {
methods: iter.into_iter().collect(),
}
}
}
pub fn on<H, O>(method: Method, handler: H) -> Route
where
H: Handler<Request, Output = Result<O>> + Clone,
O: IntoResponse,
{
Route::new().on(method, handler)
}
repeat!(
export_verb
get GET
post POST
put PUT
delete DELETE
head HEAD
options OPTIONS
connect CONNECT
patch PATCH
trace TRACE
);
pub fn any<H, O>(handler: H) -> Route
where
H: Handler<Request, Output = Result<O>> + Clone,
O: IntoResponse,
{
Route::new().any(handler)
}
impl fmt::Debug for Route {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Route")
.field(
"methods",
&self
.methods
.iter()
.map(|(m, _)| m)
.collect::<Vec<&Method>>(),
)
.finish()
}
}
#[cfg(test)]
#[allow(dead_code)]
#[allow(clippy::unused_async)]
mod tests {
use super::Route;
use http_body_util::BodyExt;
use serde::Deserialize;
use std::sync::Arc;
use viz_core::{
async_trait,
handler::Transform,
types::{Query, State},
Handler, HandlerExt, IntoHandler, IntoResponse, Method, Next, Request, RequestExt,
Response, Result,
};
#[tokio::test]
async fn route() -> anyhow::Result<()> {
async fn handler(_: Request) -> Result<impl IntoResponse> {
Ok(())
}
struct Logger;
impl Logger {
const fn new() -> Self {
Self
}
}
impl<H: Clone> Transform<H> for Logger {
type Output = LoggerHandler<H>;
fn transform(&self, h: H) -> Self::Output {
LoggerHandler(h)
}
}
#[derive(Clone)]
struct LoggerHandler<H>(H);
#[async_trait]
impl<H> Handler<Request> for LoggerHandler<H>
where
H: Handler<Request>,
{
type Output = H::Output;
async fn call(&self, req: Request) -> Self::Output {
self.0.call(req).await
}
}
async fn before(req: Request) -> Result<Request> {
Ok(req)
}
async fn after(res: Result<Response>) -> Result<Response> {
res
}
async fn around<H, O>((req, handler): Next<Request, H>) -> Result<Response>
where
H: Handler<Request, Output = Result<O>>,
O: IntoResponse,
{
handler.call(req).await.map(IntoResponse::into_response)
}
async fn around_1<H, O>((req, handler): Next<Request, H>) -> Result<Response>
where
H: Handler<Request, Output = Result<O>>,
O: IntoResponse,
{
handler.call(req).await.map(IntoResponse::into_response)
}
async fn around_2<H>((req, handler): Next<Request, H>) -> Result<Response>
where
H: Handler<Request, Output = Result<Response>>,
{
handler.call(req).await
}
#[derive(Clone)]
struct Around2 {
name: String,
}
#[async_trait]
impl<H, I, O> Handler<Next<I, H>> for Around2
where
I: Send + 'static,
H: Handler<I, Output = Result<O>>,
{
type Output = H::Output;
async fn call(&self, (i, h): Next<I, H>) -> Self::Output {
h.call(i).await
}
}
#[derive(Clone)]
struct Around3 {
name: String,
}
#[async_trait]
impl<H, O> Handler<Next<Request, H>> for Around3
where
H: Handler<Request, Output = Result<O>> + Clone,
O: IntoResponse,
{
type Output = Result<Response>;
async fn call(&self, (i, h): Next<Request, H>) -> Self::Output {
h.call(i).await.map(IntoResponse::into_response)
}
}
#[derive(Clone)]
struct Around4 {
name: String,
}
#[async_trait]
impl<H> Handler<Next<Request, H>> for Around4
where
H: Handler<Request, Output = Result<Response>> + Clone,
{
type Output = Result<Response>;
async fn call(&self, (i, h): Next<Request, H>) -> Self::Output {
h.call(i).await
}
}
#[derive(Deserialize)]
struct Counter {
c: u8,
}
async fn ext(q: Query<Counter>, s: State<Arc<String>>) -> Result<impl IntoResponse> {
let mut a = s.to_string().as_bytes().to_vec();
a.push(q.c);
Ok(a)
}
let route = Route::new()
.any(ext.into_handler())
.on(Method::GET, handler.map_into_response().before(before))
.on(Method::POST, handler.map_into_response().after(after))
.put(handler.around(Around2 {
name: "handler around".to_string(),
}))
.with(Logger::new())
.map_handler(|handler| {
handler
.before(|mut req: Request| async {
req.set_state(Arc::new("before".to_string()));
Ok(req)
})
.before(before)
.around(around_2)
.after(after)
.around(Around4 {
name: "4".to_string(),
})
.around(Around2 {
name: "2".to_string(),
})
.around(around)
.around(around_1)
.around(Around3 {
name: "3".to_string(),
})
.with(Logger::new())
.boxed()
})
.with_handler(around)
.with_handler(around_1)
.with_handler(around_2)
.with_handler(Around2 {
name: "2 with handler".to_string(),
})
.with_handler(Around3 {
name: "3 with handler".to_string(),
})
.with_handler(Around4 {
name: "4 with handler".to_string(),
})
.into_iter()
.collect::<Route>();
let (_, h) = route
.methods
.iter()
.find(|(m, _)| m == Method::GET)
.unwrap();
let resp = match h.call(Request::default()).await {
Ok(r) => r,
Err(e) => e.into_response(),
};
assert_eq!(resp.into_body().collect().await?.to_bytes(), "");
let (_, h) = route
.methods
.iter()
.find(|(m, _)| m == Method::DELETE)
.unwrap();
let mut req = Request::default();
*req.uri_mut() = "/?c=1".parse().unwrap();
let resp = match h.call(req).await {
Ok(r) => r,
Err(e) => e.into_response(),
};
assert_eq!(
resp.into_body().collect().await?.to_bytes().to_vec(),
vec![98, 101, 102, 111, 114, 101, 1]
);
Ok(())
}
}