use crate::Response;
use crate::TiiResult;
use crate::{ConnectionStream, RequestBody, ResponseContext, TiiError, UserError};
use crate::{MimeTypeWithCharset, RequestContext};
use crate::{WebsocketReceiver, WebsocketSender};
use std::any::Any;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};
use std::thread;
use std::thread::JoinHandle;
pub struct ThreadAdapterJoinHandle(Box<dyn FnOnce() -> thread::Result<()> + Send>);
impl ThreadAdapterJoinHandle {
pub fn new(inner: Box<dyn FnOnce() -> thread::Result<()> + Send>) -> Self {
ThreadAdapterJoinHandle(inner)
}
pub fn join(self) -> thread::Result<()> {
self.0()
}
}
impl Debug for ThreadAdapterJoinHandle {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("ThreadAdapterJoinHandle")
}
}
impl Default for ThreadAdapterJoinHandle {
fn default() -> Self {
Self(Box::new(|| Ok(())))
}
}
pub trait ThreadAdapter: Send + Sync + Debug {
fn spawn(&self, task: Box<dyn FnOnce() + Send>) -> TiiResult<ThreadAdapterJoinHandle>;
}
#[allow(dead_code)] #[derive(Debug)]
pub(crate) struct DefaultThreadAdapter;
impl ThreadAdapter for DefaultThreadAdapter {
fn spawn(&self, task: Box<dyn FnOnce() + Send>) -> TiiResult<ThreadAdapterJoinHandle> {
let hdl: JoinHandle<()> = thread::Builder::new().spawn(task)?;
Ok(ThreadAdapterJoinHandle::new(Box::new(move || hdl.join())))
}
}
pub trait WebsocketEndpoint: Send + Sync {
fn serve(
&self,
request: &RequestContext,
receiver: WebsocketReceiver,
sender: WebsocketSender,
) -> TiiResult<()>;
}
trait IntoWebsocketEndpointResponse {
fn into(self) -> TiiResult<()>;
}
impl IntoWebsocketEndpointResponse for TiiResult<()> {
fn into(self) -> TiiResult<()> {
self
}
}
impl IntoWebsocketEndpointResponse for () {
fn into(self) -> TiiResult<()> {
Ok(())
}
}
impl<F, R> WebsocketEndpoint for F
where
R: IntoWebsocketEndpointResponse,
F: Fn(&RequestContext, WebsocketReceiver, WebsocketSender) -> R + Send + Sync,
{
fn serve(
&self,
request: &RequestContext,
receiver: WebsocketReceiver,
sender: WebsocketSender,
) -> TiiResult<()> {
self(request, receiver, sender).into()
}
}
pub trait HttpEndpoint: Send + Sync {
fn serve(&self, request: &RequestContext) -> TiiResult<Response>;
fn parse_entity(
&self,
_mime: &MimeTypeWithCharset,
_request: &RequestBody,
) -> TiiResult<Option<Box<dyn Any + Send + Sync>>> {
Ok(None)
}
}
impl<F, R> HttpEndpoint for F
where
R: Into<TiiResult<Response>>,
F: Fn(&RequestContext) -> R + Send + Sync,
{
fn serve(&self, request: &RequestContext) -> TiiResult<Response> {
if request.get_request_entity().is_some() {
return Err(TiiError::UserError(UserError::BadFilterOrBadEndpointCausedEntityTypeMismatch));
}
self(request).into()
}
fn parse_entity(
&self,
_: &MimeTypeWithCharset,
_: &RequestBody,
) -> TiiResult<Option<Box<dyn Any + Send + Sync>>> {
Ok(None)
}
}
mod seal {
pub trait AsRequestStateSeal: Send + Sync {
type Target;
fn as_request_state(&self) -> &Self::Target;
}
}
pub trait AsRequestState: seal::AsRequestStateSeal {}
impl<T: Send + Sync> AsRequestState for Arc<T> {}
impl<T: Send + Sync> seal::AsRequestStateSeal for Arc<T> {
type Target = T;
fn as_request_state(&self) -> &Self::Target {
self
}
}
impl<T: Send + Sync> AsRequestState for Box<T> {}
impl<T: Send + Sync> seal::AsRequestStateSeal for Box<T> {
type Target = T;
fn as_request_state(&self) -> &Self::Target {
self
}
}
impl<T: Send + Sync> AsRequestState for &T {}
impl<T: Send + Sync> seal::AsRequestStateSeal for &T {
type Target = T;
fn as_request_state(&self) -> &Self::Target {
self
}
}
impl<T: Send + Sync> AsRequestState for (T,) {}
impl<T: Send + Sync> seal::AsRequestStateSeal for (T,) {
type Target = T;
fn as_request_state(&self) -> &Self::Target {
&self.0
}
}
impl<T: Send + Sync> AsRequestState for [T] {}
impl<T: Send + Sync> seal::AsRequestStateSeal for [T] {
type Target = T;
#[expect(clippy::indexing_slicing)]
fn as_request_state(&self) -> &Self::Target {
&self[0]
}
}
impl<F, R, T, X> HttpEndpoint for (T, F)
where
R: Into<TiiResult<Response>>,
F: Fn(&X, &RequestContext) -> R + Send + Sync,
T: Send + Sync + 'static + seal::AsRequestStateSeal<Target = X>,
{
fn serve(&self, request: &RequestContext) -> TiiResult<Response> {
if request.get_request_entity().is_some() {
return Err(TiiError::UserError(UserError::BadFilterOrBadEndpointCausedEntityTypeMismatch));
}
(self.1)(self.0.as_request_state(), request).into()
}
}
pub trait EntityDeserializer<T: Any + Send + Sync> {
fn deserialize(&self, mime: &MimeTypeWithCharset, body: &RequestBody) -> TiiResult<T>;
}
impl<F, T> EntityDeserializer<T> for F
where
T: Any + Send + Sync,
F: Fn(&MimeTypeWithCharset, &RequestBody) -> TiiResult<T>,
{
fn deserialize(&self, mime: &MimeTypeWithCharset, body: &RequestBody) -> TiiResult<T> {
self(mime, body)
}
}
pub(crate) struct EntityHttpEndpoint<T, F, R, D>
where
T: Any + Send + Sync,
R: Into<TiiResult<Response>> + Send,
F: Fn(&RequestContext, &T) -> R + Send + Sync,
D: EntityDeserializer<T> + Send + Sync,
{
pub(crate) endpoint: F,
pub(crate) deserializer: D,
pub(crate) _p1: PhantomData<T>,
pub(crate) _p2: PhantomData<Mutex<PhantomData<R>>>,
}
impl<T, F, R, D> HttpEndpoint for EntityHttpEndpoint<T, F, R, D>
where
T: Any + Send + Sync,
R: Into<TiiResult<Response>> + Send,
F: Fn(&RequestContext, &T) -> R + Send + Sync,
D: EntityDeserializer<T> + Send + Sync,
{
fn serve(&self, request: &RequestContext) -> TiiResult<Response> {
let Some(entity) = request.get_request_entity() else {
return Err(TiiError::UserError(UserError::BadFilterOrBadEndpointCausedEntityTypeMismatch));
};
let Some(entity) = entity.downcast_ref::<T>() else {
return Err(TiiError::UserError(UserError::BadFilterOrBadEndpointCausedEntityTypeMismatch));
};
(self.endpoint)(request, entity).into()
}
fn parse_entity(
&self,
mime: &MimeTypeWithCharset,
request: &RequestBody,
) -> TiiResult<Option<Box<dyn Any + Send + Sync>>> {
let result: T = self.deserializer.deserialize(mime, request)?;
Ok(Some(Box::new(result) as Box<dyn Any + Send + Sync>))
}
}
pub(crate) struct StatefulEntityHttpEndpoint<T, S, SR, F, R, D>
where
T: Any + Send + Sync,
R: Into<TiiResult<Response>> + Send,
F: Fn(&SR, &RequestContext, &T) -> R + Send + Sync,
D: EntityDeserializer<T> + Send + Sync,
S: seal::AsRequestStateSeal<Target = SR> + Send + Sync,
SR: Send + Sync,
{
pub(crate) endpoint: F,
pub(crate) state: S,
pub(crate) deserializer: D,
pub(crate) _p1: PhantomData<T>,
pub(crate) _p2: PhantomData<Mutex<PhantomData<R>>>,
pub(crate) _p4: PhantomData<SR>,
}
impl<T, S, SR, F, R, D> HttpEndpoint for StatefulEntityHttpEndpoint<T, S, SR, F, R, D>
where
T: Any + Send + Sync,
R: Into<TiiResult<Response>> + Send,
F: Fn(&SR, &RequestContext, &T) -> R + Send + Sync,
D: EntityDeserializer<T> + Send + Sync,
S: seal::AsRequestStateSeal<Target = SR> + Send + Sync,
SR: Send + Sync,
{
fn serve(&self, request: &RequestContext) -> TiiResult<Response> {
let Some(entity) = request.get_request_entity() else {
return Err(TiiError::UserError(UserError::BadFilterOrBadEndpointCausedEntityTypeMismatch));
};
let Some(entity) = entity.downcast_ref::<T>() else {
return Err(TiiError::UserError(UserError::BadFilterOrBadEndpointCausedEntityTypeMismatch));
};
(self.endpoint)(self.state.as_request_state(), request, entity).into()
}
fn parse_entity(
&self,
mime: &MimeTypeWithCharset,
request: &RequestBody,
) -> TiiResult<Option<Box<dyn Any + Send + Sync>>> {
let result: T = self.deserializer.deserialize(mime, request)?;
Ok(Some(Box::new(result) as Box<dyn Any + Send + Sync>))
}
}
pub trait RouterFilter: Send + Sync {
fn filter(&self, request: &RequestContext) -> TiiResult<bool>;
}
impl<F: Fn(&RequestContext) -> TiiResult<bool> + Send + Sync> RouterFilter for F {
fn filter(&self, request: &RequestContext) -> TiiResult<bool> {
self(request)
}
}
trait IntoRequestFilterResult {
fn into(self) -> TiiResult<Option<Response>>;
}
impl IntoRequestFilterResult for Option<Response> {
fn into(self) -> TiiResult<Option<Response>> {
Ok(self)
}
}
impl IntoRequestFilterResult for TiiResult<Option<Response>> {
fn into(self) -> TiiResult<Option<Response>> {
self
}
}
impl IntoRequestFilterResult for () {
fn into(self) -> TiiResult<Option<Response>> {
Ok(None)
}
}
impl IntoRequestFilterResult for TiiResult<()> {
fn into(self) -> TiiResult<Option<Response>> {
self.map(|_| None)
}
}
pub trait RequestFilter: Send + Sync {
fn filter(&self, request: &mut RequestContext) -> TiiResult<Option<Response>>;
}
impl<F, R> RequestFilter for F
where
R: IntoRequestFilterResult,
F: Fn(&mut RequestContext) -> R + Send + Sync,
{
fn filter(&self, request: &mut RequestContext) -> TiiResult<Option<Response>> {
self(request).into()
}
}
impl<S, SR, F, R> RequestFilter for (S, F)
where
R: IntoRequestFilterResult,
F: Fn(&SR, &mut RequestContext) -> R + Send + Sync,
S: seal::AsRequestStateSeal<Target = SR> + Send + Sync,
{
fn filter(&self, request: &mut RequestContext) -> TiiResult<Option<Response>> {
(self.1)(self.0.as_request_state(), request).into()
}
}
pub trait ResponseFilter: Send + Sync {
fn filter(&self, request: &mut ResponseContext<'_>) -> TiiResult<()>;
}
impl<F, R> ResponseFilter for F
where
R: Into<TiiResult<()>>,
F: Fn(&mut ResponseContext<'_>) -> R + Send + Sync,
{
fn filter(&self, request: &mut ResponseContext<'_>) -> TiiResult<()> {
self(request).into()
}
}
impl<S, SR, F, R> ResponseFilter for (S, F)
where
R: Into<TiiResult<()>>,
F: Fn(&SR, &mut ResponseContext<'_>) -> R + Send + Sync,
S: seal::AsRequestStateSeal<Target = SR> + Send + Sync,
{
fn filter(&self, request: &mut ResponseContext<'_>) -> TiiResult<()> {
(self.1)(self.0.as_request_state(), request).into()
}
}
#[derive(Debug)]
pub enum RouterWebSocketServingResponse {
HandledWithProtocolSwitch,
HandledWithoutProtocolSwitch(Response),
NotHandled,
}
pub trait Router: Debug + Send + Sync {
fn serve(&self, request: &mut RequestContext) -> TiiResult<Option<Response>>;
fn serve_websocket(
&self,
stream: &dyn ConnectionStream,
request: &mut RequestContext,
) -> TiiResult<RouterWebSocketServingResponse>;
}
pub type ContinueHandler = fn(&mut RequestContext) -> TiiResult<bool>;