#[cfg(test)]
mod tests;
use std::sync::Arc;
use crate::application::Application;
use crate::middleware::Middleware;
use crate::request::Request;
use crate::response::Response;
use crate::router::PathParams;
use crate::server::ConnectionInfo;
pub type Guard<S> = Arc<dyn Fn(&Request, &ConnectionInfo, &S) -> Result<(), Response> + Send + Sync>;
pub(crate) type SyncHandler<S> = Arc<dyn Fn(&Request, &PathParams, &ConnectionInfo, &Arc<S>) -> Response + Send + Sync>;
pub(crate) struct GroupRoute<H> {
pub(crate) method: &'static str,
pub(crate) pattern: String,
pub(crate) name: Option<String>,
pub(crate) handler: H,
}
pub fn join_path(prefix: &str, pattern: &str) -> String {
let prefix = prefix.trim_end_matches('/');
let pattern = pattern.trim_start_matches('/');
let joined = match (prefix.is_empty(), pattern.is_empty()) {
(_, true) => prefix.to_string(),
(true, false) => format!("/{pattern}"),
(false, false) => format!("{prefix}/{pattern}"),
};
if joined.is_empty() {
"/".to_string()
} else if joined.starts_with('/') {
joined
} else {
format!("/{joined}")
}
}
fn resource_name(path: &str) -> String {
path.rsplit('/').find(|s| !s.is_empty() && !s.starts_with([':', '*', '{'])).unwrap_or("resource").to_string()
}
struct Endpoint<'a, S> {
handler: &'a SyncHandler<S>,
params: &'a PathParams,
state: &'a Arc<S>,
}
impl<S> Application for Endpoint<'_, S> {
fn execute(&self, request: &Request, connection: &ConnectionInfo) -> Result<Response, String> {
Ok((self.handler)(request, self.params, connection, self.state))
}
}
struct Rest<'a> {
layers: &'a [Arc<dyn Middleware>],
endpoint: &'a dyn Application,
}
impl Application for Rest<'_> {
fn execute(&self, request: &Request, connection: &ConnectionInfo) -> Result<Response, String> {
match self.layers.split_first() {
Some((layer, layers)) => layer.handle(request, connection, &Rest { layers, endpoint: self.endpoint }),
None => self.endpoint.execute(request, connection),
}
}
}
fn internal_error(message: String) -> Response {
use crate::error::{AppError, IntoResponse};
AppError::Internal(message).into_response()
}
fn wrap_sync<S: Send + Sync + 'static>(handler: SyncHandler<S>, guards: Arc<[Guard<S>]>, layers: Arc<[Arc<dyn Middleware>]>) -> SyncHandler<S> {
if guards.is_empty() && layers.is_empty() {
return handler;
}
Arc::new(move |request, params, connection, state| {
for guard in guards.iter() {
if let Err(response) = guard(request, connection, state) {
return response;
}
}
if layers.is_empty() {
return handler(request, params, connection, state);
}
let endpoint = Endpoint { handler: &handler, params, state };
match (Rest { layers: &layers, endpoint: &endpoint }).execute(request, connection) {
Ok(response) => response,
Err(message) => internal_error(message),
}
})
}
pub struct Group<S> {
prefix: String,
guards: Vec<Guard<S>>,
layers: Vec<Arc<dyn Middleware>>,
routes: Vec<GroupRoute<SyncHandler<S>>>,
}
impl<S: Send + Sync + 'static> Group<S> {
pub(crate) fn new(prefix: &str) -> Self {
Group { prefix: prefix.to_string(), guards: Vec::new(), layers: Vec::new(), routes: Vec::new() }
}
fn push(mut self, method: &'static str, pattern: &str, handler: SyncHandler<S>) -> Self {
self.routes.push(GroupRoute { method, pattern: join_path(&self.prefix, pattern), name: None, handler });
self
}
pub fn guard<G>(mut self, guard: G) -> Self
where
G: Fn(&Request, &ConnectionInfo, &S) -> Result<(), Response> + Send + Sync + 'static,
{
self.guards.push(Arc::new(guard));
self
}
pub fn wrap<M: Middleware + 'static>(mut self, layer: M) -> Self {
self.layers.push(Arc::new(layer));
self
}
pub fn name(mut self, name: &str) -> Self {
if let Some(route) = self.routes.last_mut() {
route.name = Some(name.to_string());
}
self
}
pub fn get<F>(self, pattern: &str, handler: F) -> Self
where
F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,
{
self.push("GET", pattern, Arc::new(move |r, p, c, s| handler(r, p, c, s)))
}
pub fn post<F>(self, pattern: &str, handler: F) -> Self
where
F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,
{
self.push("POST", pattern, Arc::new(move |r, p, c, s| handler(r, p, c, s)))
}
pub fn put<F>(self, pattern: &str, handler: F) -> Self
where
F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,
{
self.push("PUT", pattern, Arc::new(move |r, p, c, s| handler(r, p, c, s)))
}
pub fn patch<F>(self, pattern: &str, handler: F) -> Self
where
F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,
{
self.push("PATCH", pattern, Arc::new(move |r, p, c, s| handler(r, p, c, s)))
}
pub fn delete<F>(self, pattern: &str, handler: F) -> Self
where
F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,
{
self.push("DELETE", pattern, Arc::new(move |r, p, c, s| handler(r, p, c, s)))
}
pub fn route<H: crate::handler::Handler<S>>(self, handler: H) -> Self {
self.push(H::METHOD, H::PATTERN, Arc::new(move |r, p, c, s| handler.handle(r, p, c, s)))
}
pub fn group(mut self, pattern: &str, build: impl FnOnce(Group<S>) -> Group<S>) -> Self {
let inner = build(Group::new(&join_path(&self.prefix, pattern)));
self.routes.extend(inner.finish());
self
}
pub fn resource(mut self, path: &str, build: impl FnOnce(Resource<S>) -> Resource<S>) -> Self {
let resource = build(Resource::new(&join_path(&self.prefix, path)));
self.routes.extend(resource.finish());
self
}
pub(crate) fn finish(self) -> Vec<GroupRoute<SyncHandler<S>>> {
let guards: Arc<[Guard<S>]> = self.guards.into();
let layers: Arc<[Arc<dyn Middleware>]> = self.layers.into();
self.routes
.into_iter()
.map(|route| GroupRoute { handler: wrap_sync(route.handler, Arc::clone(&guards), Arc::clone(&layers)), ..route })
.collect()
}
}
pub struct Resource<S> {
group: Group<S>,
name: String,
actions: Vec<(&'static str, &'static str, &'static str, SyncHandler<S>)>,
}
macro_rules! resource_action {
($(#[$doc:meta])* $fn:ident, $action:literal, $method:literal, $suffix:literal) => {
$(#[$doc])*
pub fn $fn<F>(mut self, handler: F) -> Self
where
F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,
{
self.actions.push(($action, $method, $suffix, Arc::new(move |r, p, c, s| handler(r, p, c, s))));
self
}
};
}
impl<S: Send + Sync + 'static> Resource<S> {
fn new(path: &str) -> Self {
Resource { name: resource_name(path), group: Group::new(path), actions: Vec::new() }
}
pub fn name(mut self, name: &str) -> Self {
self.name = name.to_string();
self
}
pub fn guard<G>(mut self, guard: G) -> Self
where
G: Fn(&Request, &ConnectionInfo, &S) -> Result<(), Response> + Send + Sync + 'static,
{
self.group = self.group.guard(guard);
self
}
pub fn wrap<M: Middleware + 'static>(mut self, layer: M) -> Self {
self.group = self.group.wrap(layer);
self
}
resource_action!( index, "index", "GET", "");
resource_action!( new_form, "new", "GET", "/new");
resource_action!( create, "create", "POST", "");
resource_action!( show, "show", "GET", "/{id}");
resource_action!( edit, "edit", "GET", "/{id}/edit");
resource_action!( update, "update", "PUT", "/{id}");
resource_action!( destroy, "destroy", "DELETE", "/{id}");
pub fn extra(mut self, build: impl FnOnce(Group<S>) -> Group<S>) -> Self {
let prefix = self.group.prefix.clone();
let inner = build(Group::new(&prefix));
self.group.routes.extend(inner.finish());
self
}
fn finish(mut self) -> Vec<GroupRoute<SyncHandler<S>>> {
let order = ["index", "new", "create", "show", "edit", "update", "destroy"];
self.actions.sort_by_key(|(action, ..)| order.iter().position(|a| a == action));
let mut group = self.group;
let mut extra = std::mem::take(&mut group.routes);
for (action, method, suffix, handler) in self.actions {
let pattern = join_path(&group.prefix, suffix);
let name = Some(format!("{}.{}", self.name, action));
if action == "update" {
group.routes.push(GroupRoute { method: "PATCH", pattern: pattern.clone(), name: None, handler: Arc::clone(&handler) });
}
group.routes.push(GroupRoute { method, pattern, name, handler });
}
group.routes.append(&mut extra);
group.finish()
}
}
#[cfg(feature = "http2")]
pub use self::r#async::{AsyncGroup, AsyncResource};
#[cfg(feature = "http2")]
pub(crate) use self::r#async::AsyncHandlerFn;
#[cfg(feature = "http2")]
mod r#async {
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use super::{join_path, resource_name, Guard, GroupRoute};
use crate::request::Request;
use crate::response::Response;
use crate::router::PathParams;
use crate::server::ConnectionInfo;
pub(crate) type BoxFuture = Pin<Box<dyn Future<Output = Response> + Send + 'static>>;
pub(crate) type AsyncHandlerFn<S> = Arc<dyn Fn(Request, PathParams, ConnectionInfo, Arc<S>) -> BoxFuture + Send + Sync>;
fn wrap_async<S: Send + Sync + 'static>(handler: AsyncHandlerFn<S>, guards: Arc<[Guard<S>]>) -> AsyncHandlerFn<S> {
if guards.is_empty() {
return handler;
}
Arc::new(move |request, params, connection, state| {
for guard in guards.iter() {
if let Err(response) = guard(&request, &connection, &state) {
return Box::pin(async move { response });
}
}
handler(request, params, connection, state)
})
}
pub struct AsyncGroup<S> {
prefix: String,
guards: Vec<Guard<S>>,
routes: Vec<GroupRoute<AsyncHandlerFn<S>>>,
}
impl<S: Send + Sync + 'static> AsyncGroup<S> {
pub(crate) fn new(prefix: &str) -> Self {
AsyncGroup { prefix: prefix.to_string(), guards: Vec::new(), routes: Vec::new() }
}
fn push(mut self, method: &'static str, pattern: &str, handler: AsyncHandlerFn<S>) -> Self {
self.routes.push(GroupRoute { method, pattern: join_path(&self.prefix, pattern), name: None, handler });
self
}
fn boxed<F, Fut>(handler: F) -> AsyncHandlerFn<S>
where
F: Fn(Request, PathParams, ConnectionInfo, Arc<S>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
Arc::new(move |r, p, c, s| Box::pin(handler(r, p, c, s)))
}
pub fn guard<G>(mut self, guard: G) -> Self
where
G: Fn(&Request, &ConnectionInfo, &S) -> Result<(), Response> + Send + Sync + 'static,
{
self.guards.push(Arc::new(guard));
self
}
pub fn name(mut self, name: &str) -> Self {
if let Some(route) = self.routes.last_mut() {
route.name = Some(name.to_string());
}
self
}
pub fn get<F, Fut>(self, pattern: &str, handler: F) -> Self
where
F: Fn(Request, PathParams, ConnectionInfo, Arc<S>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.push("GET", pattern, Self::boxed(handler))
}
pub fn post<F, Fut>(self, pattern: &str, handler: F) -> Self
where
F: Fn(Request, PathParams, ConnectionInfo, Arc<S>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.push("POST", pattern, Self::boxed(handler))
}
pub fn put<F, Fut>(self, pattern: &str, handler: F) -> Self
where
F: Fn(Request, PathParams, ConnectionInfo, Arc<S>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.push("PUT", pattern, Self::boxed(handler))
}
pub fn patch<F, Fut>(self, pattern: &str, handler: F) -> Self
where
F: Fn(Request, PathParams, ConnectionInfo, Arc<S>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.push("PATCH", pattern, Self::boxed(handler))
}
pub fn delete<F, Fut>(self, pattern: &str, handler: F) -> Self
where
F: Fn(Request, PathParams, ConnectionInfo, Arc<S>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.push("DELETE", pattern, Self::boxed(handler))
}
pub fn route<H: crate::handler::AsyncHandler<S>>(self, handler: H) -> Self {
let handler = Arc::new(handler);
self.push(H::METHOD, H::PATTERN, Arc::new(move |r, p, c, s| handler.handle_async(r, p, c, s)))
}
pub fn group(mut self, pattern: &str, build: impl FnOnce(AsyncGroup<S>) -> AsyncGroup<S>) -> Self {
let inner = build(AsyncGroup::new(&join_path(&self.prefix, pattern)));
self.routes.extend(inner.finish());
self
}
pub fn resource(mut self, path: &str, build: impl FnOnce(AsyncResource<S>) -> AsyncResource<S>) -> Self {
let resource = build(AsyncResource::new(&join_path(&self.prefix, path)));
self.routes.extend(resource.finish());
self
}
pub(crate) fn finish(self) -> Vec<GroupRoute<AsyncHandlerFn<S>>> {
let guards: Arc<[Guard<S>]> = self.guards.into();
self.routes
.into_iter()
.map(|route| GroupRoute { handler: wrap_async(route.handler, Arc::clone(&guards)), ..route })
.collect()
}
}
pub struct AsyncResource<S> {
group: AsyncGroup<S>,
name: String,
actions: Vec<(&'static str, &'static str, &'static str, AsyncHandlerFn<S>)>,
}
macro_rules! async_resource_action {
($(#[$doc:meta])* $fn:ident, $action:literal, $method:literal, $suffix:literal) => {
$(#[$doc])*
pub fn $fn<F, Fut>(mut self, handler: F) -> Self
where
F: Fn(Request, PathParams, ConnectionInfo, Arc<S>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.actions.push(($action, $method, $suffix, AsyncGroup::<S>::boxed(handler)));
self
}
};
}
impl<S: Send + Sync + 'static> AsyncResource<S> {
fn new(path: &str) -> Self {
AsyncResource { name: resource_name(path), group: AsyncGroup::new(path), actions: Vec::new() }
}
pub fn name(mut self, name: &str) -> Self {
self.name = name.to_string();
self
}
pub fn guard<G>(mut self, guard: G) -> Self
where
G: Fn(&Request, &ConnectionInfo, &S) -> Result<(), Response> + Send + Sync + 'static,
{
self.group = self.group.guard(guard);
self
}
async_resource_action!( index, "index", "GET", "");
async_resource_action!( new_form, "new", "GET", "/new");
async_resource_action!( create, "create", "POST", "");
async_resource_action!( show, "show", "GET", "/{id}");
async_resource_action!( edit, "edit", "GET", "/{id}/edit");
async_resource_action!( update, "update", "PUT", "/{id}");
async_resource_action!( destroy, "destroy", "DELETE", "/{id}");
pub fn extra(mut self, build: impl FnOnce(AsyncGroup<S>) -> AsyncGroup<S>) -> Self {
let prefix = self.group.prefix.clone();
let inner = build(AsyncGroup::new(&prefix));
self.group.routes.extend(inner.finish());
self
}
fn finish(mut self) -> Vec<GroupRoute<AsyncHandlerFn<S>>> {
let order = ["index", "new", "create", "show", "edit", "update", "destroy"];
self.actions.sort_by_key(|(action, ..)| order.iter().position(|a| a == action));
let mut group = self.group;
let mut extra = std::mem::take(&mut group.routes);
for (action, method, suffix, handler) in self.actions {
let pattern = join_path(&group.prefix, suffix);
let name = Some(format!("{}.{}", self.name, action));
if action == "update" {
group.routes.push(GroupRoute { method: "PATCH", pattern: pattern.clone(), name: None, handler: Arc::clone(&handler) });
}
group.routes.push(GroupRoute { method, pattern, name, handler });
}
group.routes.append(&mut extra);
group.finish()
}
}
}