#[cfg(test)]
mod tests;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use crate::app::App;
use crate::application::Application;
use crate::core::New;
use crate::request::Request;
use crate::response::Response;
use crate::router::matcher::{self, Segment};
use crate::router::PathParams;
use crate::server::ConnectionInfo;
#[cfg(feature = "openapi")]
use crate::mime_type::MimeType;
#[cfg(feature = "openapi")]
use crate::range::Range;
#[cfg(feature = "openapi")]
use crate::response::STATUS_CODE_REASON_PHRASE;
type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send + 'static>>;
type AsyncHandlerFn<S> = Arc<
dyn Fn(Request, PathParams, ConnectionInfo, Arc<S>) -> BoxFuture<Response> + Send + Sync,
>;
#[derive(Clone)]
struct AsyncRoute<S> {
method: String,
segments: Vec<Segment>,
handler: AsyncHandlerFn<S>,
}
#[derive(Clone)]
pub struct AsyncAppWithState<S> {
state: Arc<S>,
routes: Vec<AsyncRoute<S>>,
config: Option<Arc<crate::server_config::ServerConfig>>,
}
impl<S: Send + Sync + 'static> AsyncAppWithState<S> {
pub fn new(state: S) -> Self {
AsyncAppWithState { state: Arc::new(state), routes: Vec::new(), config: None }
}
pub fn with_config(mut self, config: crate::server_config::ServerConfig) -> Self {
self.config = Some(Arc::new(config));
self
}
pub fn state(&self) -> &S {
&self.state
}
pub fn route_entries(&self) -> Vec<crate::router::RouteInfo> {
self.routes
.iter()
.map(|r| crate::router::RouteInfo {
method: r.method.clone(),
pattern: matcher::segments_to_pattern(&r.segments),
})
.collect()
}
fn fallback_app(&self) -> App {
match &self.config {
Some(c) => App::with_config((**c).clone()),
None => App::new(),
}
}
fn add<F, Fut>(mut self, method: &str, pattern: &str, handler: F) -> Self
where
F: Fn(Request, PathParams, ConnectionInfo, Arc<S>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.routes.push(AsyncRoute {
method: method.to_string(),
segments: matcher::parse_pattern(pattern),
handler: Arc::new(move |req, params, conn, state| Box::pin(handler(req, params, conn, state))),
});
if let Some((last, earlier)) = self.routes.split_last() {
let earlier = earlier.iter().map(|r| (r.method.as_str(), r.segments.as_slice()));
if let Some(conflict) = crate::router::conflict_with(earlier, &last.method, &last.segments) {
conflict.warn_once();
}
}
self
}
pub fn route_conflicts(&self) -> Vec<crate::router::RouteConflict> {
crate::router::conflicts(self.routes.iter().map(|r| (r.method.as_str(), r.segments.as_slice())))
}
pub fn route<H: crate::handler::AsyncHandler<S>>(self, handler: H) -> Self {
let handler = Arc::new(handler);
self.add(H::METHOD, H::PATTERN, move |request, params, connection, state| {
handler.handle_async(request, params, connection, state)
})
}
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.add("GET", pattern, 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.add("POST", pattern, 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.add("PUT", pattern, 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.add("PATCH", pattern, 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.add("DELETE", pattern, handler)
}
#[cfg(feature = "openapi")]
pub fn openapi(self, config: crate::openapi::OpenApiConfig) -> Self {
let spec_json = Arc::new(crate::openapi::build_spec(&config, &self.route_entries()));
let html = Arc::new(crate::openapi::swagger_ui_html("/openapi.json"));
let spec_for_route = Arc::clone(&spec_json);
self.get("/openapi.json", move |_req, _params, _conn, _state| {
let spec_for_route = Arc::clone(&spec_for_route);
async move {
let mut r = Response::new();
r.status_code = *STATUS_CODE_REASON_PHRASE.n200_ok.status_code;
r.reason_phrase = STATUS_CODE_REASON_PHRASE.n200_ok.reason_phrase.to_string();
r.content_range_list = vec![Range::get_content_range(
spec_for_route.as_bytes().to_vec(),
MimeType::APPLICATION_JSON.to_string(),
)];
r
}
})
.get("/docs", move |_req, _params, _conn, _state| {
let html = Arc::clone(&html);
async move {
let mut r = Response::new();
r.status_code = *STATUS_CODE_REASON_PHRASE.n200_ok.status_code;
r.reason_phrase = STATUS_CODE_REASON_PHRASE.n200_ok.reason_phrase.to_string();
r.content_range_list = vec![Range::get_content_range(
html.as_bytes().to_vec(),
MimeType::TEXT_HTML.to_string(),
)];
r
}
})
}
fn dispatch(&self, request: &Request, connection: &ConnectionInfo) -> Option<BoxFuture<Response>> {
let path = request.request_uri.split('?').next().unwrap_or(&request.request_uri);
let path_segs: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
for route in &self.routes {
if route.method != request.method {
continue;
}
if let Some(params_map) = matcher::try_match(&route.segments, &path_segs) {
let params = PathParams::from_map(params_map);
return Some((route.handler)(request.clone(), params, connection.clone(), Arc::clone(&self.state)));
}
}
None
}
pub(crate) async fn respond(&self, request: &Request, connection: &ConnectionInfo) -> Result<Response, String> {
match self.dispatch(request, connection) {
Some(handler) => Ok(crate::async_bridge::run_async(handler).await),
None => self.fallback_app().execute(request, connection),
}
}
}
impl<S: Send + Sync + 'static> Application for AsyncAppWithState<S> {
fn execute(&self, request: &Request, connection: &ConnectionInfo) -> Result<Response, String> {
match self.dispatch(request, connection) {
Some(handler) => Ok(crate::async_bridge::block_on_shared(handler)),
None => self.fallback_app().execute(request, connection),
}
}
fn execute_async<'a>(&'a self, request: &'a Request, connection: &'a ConnectionInfo) -> Option<crate::application::AsyncResponse<'a>> {
let handler = self.dispatch(request, connection)?;
Some(Box::pin(async move { Ok(crate::async_bridge::run_async(handler).await) }))
}
fn registered_routes(&self) -> Vec<crate::router::RouteInfo> {
self.route_entries()
}
}