use crate::default_functions::{
default_error_handler, default_method_not_allowed_handler, default_not_acceptable_handler,
default_not_found_handler, default_pre_routing_filter, default_unsupported_media_type_handler,
};
use crate::functional_traits::{
HttpEndpoint, RequestFilter, ResponseFilter, RouterFilter, StatefulEntityHttpEndpoint,
WebsocketEndpoint,
};
use crate::tii_builder::EntityHttpEndpoint;
use crate::{AcceptMimeType, RequestBody};
use crate::{AcceptMimeTypeWithCharset, MimeCharset, MimeTypeWithCharset, TiiResult};
use crate::{AsRequestState, RequestContext};
use crate::{DefaultRouter, Response, Router};
use crate::{EntityDeserializer, HttpMethod};
use crate::{ErrorHandler, NotRouteableHandler};
use crate::{HttpRoute, WebSocketRoute};
use crate::{WebsocketReceiver, WebsocketSender};
use std::any::Any;
use std::collections::HashSet;
use std::sync::Arc;
pub struct RouterBuilder {
router_filter: Box<dyn RouterFilter>,
pre_routing_filters: Vec<Box<dyn RequestFilter>>,
routing_filters: Vec<Box<dyn RequestFilter>>,
response_filters: Vec<Box<dyn ResponseFilter>>,
routes: Vec<HttpRoute>,
websocket_routes: Vec<WebSocketRoute>,
not_found_handler: NotRouteableHandler,
not_acceptable_handler: NotRouteableHandler,
method_not_allowed_handler: NotRouteableHandler,
unsupported_media_type_handler: NotRouteableHandler,
error_handler: ErrorHandler,
}
#[derive(Debug)]
struct RouteWrapper<T: HttpEndpoint + 'static>(Arc<T>);
impl<T: HttpEndpoint + 'static> HttpEndpoint for RouteWrapper<T> {
fn serve(&self, request: &RequestContext) -> TiiResult<Response> {
self.0.serve(request)
}
fn parse_entity(
&self,
mime: &MimeTypeWithCharset,
request: &RequestBody,
) -> TiiResult<Option<Box<dyn Any + Send + Sync>>> {
self.0.parse_entity(mime, request)
}
}
impl<T: HttpEndpoint + 'static> Clone for RouteWrapper<T> {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
#[derive(Debug)]
struct WSRouteWrapper<T: WebsocketEndpoint + 'static>(Arc<T>);
impl<T: WebsocketEndpoint + 'static> WebsocketEndpoint for WSRouteWrapper<T> {
fn serve(
&self,
request: &RequestContext,
receiver: WebsocketReceiver,
sender: WebsocketSender,
) -> TiiResult<()> {
self.0.serve(request, receiver, sender)
}
}
impl<T: WebsocketEndpoint + 'static> Clone for WSRouteWrapper<T> {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
pub struct RouteBuilder {
inner: RouterBuilder,
route: String,
method: HttpMethod,
consumes: HashSet<AcceptMimeTypeWithCharset>,
produces: HashSet<AcceptMimeTypeWithCharset>,
}
impl RouteBuilder {
pub(crate) fn new(
router_builder: RouterBuilder,
method: HttpMethod,
route: String,
) -> RouteBuilder {
RouteBuilder {
inner: router_builder,
route,
method,
consumes: Default::default(),
produces: Default::default(),
}
}
pub fn consumes(mut self, mime: impl Into<AcceptMimeTypeWithCharset>) -> Self {
self.consumes.insert(mime.into());
self
}
pub fn produces(mut self, mime: impl Into<AcceptMimeTypeWithCharset>) -> Self {
self.produces.insert(mime.into());
self
}
pub fn endpoint<T: HttpEndpoint + 'static>(mut self, handler: T) -> TiiResult<RouterBuilder> {
self.inner.routes.push(HttpRoute::new(
self.route,
self.method,
self.consumes,
self.produces,
handler,
)?);
Ok(self.inner)
}
pub fn entity_endpoint<T, R, F, D>(self, handler: F, deserializer: D) -> TiiResult<RouterBuilder>
where
T: Any + Send + Sync + 'static,
R: Into<TiiResult<Response>> + Send + 'static,
F: Fn(&RequestContext, &T) -> R + Send + Sync + 'static,
D: EntityDeserializer<T> + Send + Sync + 'static,
{
let ehp = EntityHttpEndpoint {
endpoint: handler,
deserializer,
_p1: Default::default(),
_p2: Default::default(),
};
self.endpoint(ehp)
}
pub fn stateful_entity_endpoint<T, S, SR, R, F, D>(
self,
state: S,
handler: F,
deserializer: D,
) -> TiiResult<RouterBuilder>
where
T: Any + Send + Sync + 'static,
R: Into<TiiResult<Response>> + Send + 'static,
F: Fn(&SR, &RequestContext, &T) -> R + Send + Sync + 'static,
D: EntityDeserializer<T> + Send + Sync + 'static,
S: AsRequestState<Target = SR> + Send + Sync + 'static,
SR: Send + Sync + 'static,
{
let ehp = StatefulEntityHttpEndpoint {
endpoint: handler,
state,
deserializer,
_p1: Default::default(),
_p2: Default::default(),
_p4: Default::default(),
};
self.endpoint(ehp)
}
}
impl Default for RouterBuilder {
fn default() -> Self {
RouterBuilder {
router_filter: Box::new(default_pre_routing_filter),
pre_routing_filters: Vec::default(),
routing_filters: Vec::default(),
response_filters: Vec::default(),
routes: Vec::new(),
websocket_routes: Vec::new(),
not_found_handler: default_not_found_handler,
not_acceptable_handler: default_not_acceptable_handler,
method_not_allowed_handler: default_method_not_allowed_handler,
unsupported_media_type_handler: default_unsupported_media_type_handler,
error_handler: default_error_handler,
}
}
}
impl RouterBuilder {
pub fn new() -> Self {
RouterBuilder::default()
}
pub fn with_pre_routing_request_filter<T>(mut self, filter: T) -> TiiResult<Self>
where
T: RequestFilter + 'static,
{
self.pre_routing_filters.push(Box::new(filter));
Ok(self)
}
pub fn with_request_filter<T>(mut self, filter: T) -> TiiResult<Self>
where
T: RequestFilter + 'static,
{
self.routing_filters.push(Box::new(filter));
Ok(self)
}
pub fn with_response_filter<T>(mut self, filter: T) -> TiiResult<Self>
where
T: ResponseFilter + 'static,
{
self.response_filters.push(Box::new(filter));
Ok(self)
}
pub fn route_any<T>(self, route: &str, handler: T) -> TiiResult<Self>
where
T: HttpEndpoint + 'static,
{
let wrapped = RouteWrapper(Arc::new(handler));
self
.route_get(route, wrapped.clone())?
.route_put(route, wrapped.clone())?
.route_post(route, wrapped.clone())?
.route_patch(route, wrapped.clone())?
.route_delete(route, wrapped.clone())?
.route_options(route, wrapped.clone())?
.route_head(route, wrapped)
}
pub const fn ok(self) -> TiiResult<Self> {
Ok(self)
}
pub fn route_method<T: HttpEndpoint + 'static>(
mut self,
method: HttpMethod,
route: &str,
handler: T,
) -> TiiResult<Self> {
self.routes.push(HttpRoute::new(
route,
method,
HashSet::from([AcceptMimeTypeWithCharset::new(
AcceptMimeType::Wildcard,
MimeCharset::Unspecified,
)]),
HashSet::new(),
handler,
)?);
Ok(self)
}
pub fn route_get<T: HttpEndpoint + 'static>(self, route: &str, handler: T) -> TiiResult<Self> {
self.route_method(HttpMethod::Get, route, handler)
}
pub fn route_post<T: HttpEndpoint + 'static>(self, route: &str, handler: T) -> TiiResult<Self> {
self.route_method(HttpMethod::Post, route, handler)
}
pub fn route_put<T: HttpEndpoint + 'static>(self, route: &str, handler: T) -> TiiResult<Self> {
self.route_method(HttpMethod::Put, route, handler)
}
pub fn route_patch<T: HttpEndpoint + 'static>(self, route: &str, handler: T) -> TiiResult<Self> {
self.route_method(HttpMethod::Patch, route, handler)
}
pub fn route_delete<T: HttpEndpoint + 'static>(self, route: &str, handler: T) -> TiiResult<Self> {
self.route_method(HttpMethod::Delete, route, handler)
}
pub fn route_options<T: HttpEndpoint + 'static>(
self,
route: &str,
handler: T,
) -> TiiResult<Self> {
self.route_method(HttpMethod::Options, route, handler)
}
pub fn route_head<T: HttpEndpoint + 'static>(self, route: &str, handler: T) -> TiiResult<Self> {
self.route_method(HttpMethod::Head, route, handler)
}
pub fn begin<T: FnOnce(Self) -> TiiResult<Self>>(self, section: T) -> TiiResult<Self> {
section(self)
}
pub fn get(self, route: &str) -> RouteBuilder {
RouteBuilder::new(self, HttpMethod::Get, route.to_string())
}
pub fn begin_get<T: FnOnce(RouteBuilder) -> TiiResult<Self>>(
self,
route: &str,
closure: T,
) -> TiiResult<Self> {
closure(self.get(route))
}
pub fn post(self, route: &str) -> RouteBuilder {
RouteBuilder::new(self, HttpMethod::Post, route.to_string())
}
pub fn begin_post<T: FnOnce(RouteBuilder) -> TiiResult<Self>>(
self,
route: &str,
closure: T,
) -> TiiResult<Self> {
closure(self.post(route))
}
pub fn put(self, route: &str) -> RouteBuilder {
RouteBuilder::new(self, HttpMethod::Put, route.to_string())
}
pub fn begin_put<T: FnOnce(RouteBuilder) -> TiiResult<Self>>(
self,
route: &str,
closure: T,
) -> TiiResult<Self> {
closure(self.put(route))
}
pub fn patch(self, route: &str) -> RouteBuilder {
RouteBuilder::new(self, HttpMethod::Patch, route.to_string())
}
pub fn begin_patch<T: FnOnce(RouteBuilder) -> TiiResult<Self>>(
self,
route: &str,
closure: T,
) -> TiiResult<Self> {
closure(self.patch(route))
}
pub fn delete(self, route: &str) -> RouteBuilder {
RouteBuilder::new(self, HttpMethod::Delete, route.to_string())
}
pub fn begin_delete<T: FnOnce(RouteBuilder) -> TiiResult<Self>>(
self,
route: &str,
closure: T,
) -> TiiResult<Self> {
closure(self.delete(route))
}
pub fn options(self, route: &str) -> RouteBuilder {
RouteBuilder::new(self, HttpMethod::Options, route.to_string())
}
pub fn begin_options<T: FnOnce(RouteBuilder) -> TiiResult<Self>>(
self,
route: &str,
closure: T,
) -> TiiResult<Self> {
closure(self.delete(route))
}
pub fn method(self, method: HttpMethod, route: &str) -> RouteBuilder {
RouteBuilder::new(self, method, route.to_string())
}
pub fn begin_method<T: FnOnce(RouteBuilder) -> TiiResult<Self>>(
self,
method: HttpMethod,
route: &str,
closure: T,
) -> TiiResult<Self> {
closure(self.method(method, route))
}
pub fn ws_route_any<T>(self, route: &str, handler: T) -> TiiResult<Self>
where
T: WebsocketEndpoint + 'static,
{
let wrapped = WSRouteWrapper(Arc::new(handler));
self
.ws_route_get(route, wrapped.clone())?
.ws_route_put(route, wrapped.clone())?
.ws_route_post(route, wrapped.clone())?
.ws_route_patch(route, wrapped.clone())?
.ws_route_delete(route, wrapped.clone())?
.ws_route_options(route, wrapped)
}
pub fn ws_route_method<T: WebsocketEndpoint + 'static>(
mut self,
method: HttpMethod,
route: &str,
handler: T,
) -> TiiResult<Self> {
self.websocket_routes.push(WebSocketRoute::new(
route,
method,
HashSet::new(),
HashSet::new(),
handler,
)?);
Ok(self)
}
pub fn ws_route_get<T>(self, route: &str, handler: T) -> TiiResult<Self>
where
T: WebsocketEndpoint + 'static,
{
self.ws_route_method(HttpMethod::Get, route, handler)
}
pub fn ws_route_post<T>(self, route: &str, handler: T) -> TiiResult<Self>
where
T: WebsocketEndpoint + 'static,
{
self.ws_route_method(HttpMethod::Post, route, handler)
}
pub fn ws_route_put<T>(self, route: &str, handler: T) -> TiiResult<Self>
where
T: WebsocketEndpoint + 'static,
{
self.ws_route_method(HttpMethod::Put, route, handler)
}
pub fn ws_route_options<T>(self, route: &str, handler: T) -> TiiResult<Self>
where
T: WebsocketEndpoint + 'static,
{
self.ws_route_method(HttpMethod::Options, route, handler)
}
pub fn ws_route_patch<T>(self, route: &str, handler: T) -> TiiResult<Self>
where
T: WebsocketEndpoint + 'static,
{
self.ws_route_method(HttpMethod::Patch, route, handler)
}
pub fn ws_route_delete<T>(self, route: &str, handler: T) -> TiiResult<Self>
where
T: WebsocketEndpoint + 'static,
{
self.ws_route_method(HttpMethod::Delete, route, handler)
}
pub fn with_error_handler(mut self, handler: ErrorHandler) -> TiiResult<Self> {
self.error_handler = handler;
Ok(self)
}
pub fn build(self) -> impl Router + 'static {
DefaultRouter::new(
self.router_filter,
self.pre_routing_filters,
self.routing_filters,
self.response_filters,
self.routes,
self.websocket_routes,
self.not_found_handler,
self.not_acceptable_handler,
self.method_not_allowed_handler,
self.unsupported_media_type_handler,
self.error_handler,
)
}
pub fn build_arc(self) -> Arc<impl Router + 'static> {
Arc::new(self.build())
}
}