use {
super::Route,
crate::{
endpoint::Endpoint, error::Error,
generic::Tuple,
handler::Handler,
input::param::Params,
},
std::{marker::PhantomData, sync::Arc},
};
#[doc(hidden)]
pub use tsukuyomi_macros::path_impl;
pub trait PathExtractor {
type Output: Tuple;
fn extract(params: Option<&Params<'_>>) -> Result<Self::Output, Error>;
}
impl PathExtractor for () {
type Output = ();
#[inline]
fn extract(_: Option<&Params<'_>>) -> Result<Self::Output, Error> {
Ok(())
}
}
#[macro_export]
macro_rules! path {
($path:expr) => {{
use $crate::config::path::internal as __path_internal;
enum __Dummy {}
impl __Dummy {
$crate::config::path::path_impl!(__path_internal, $path);
}
__Dummy::call()
}};
}
#[doc(hidden)]
pub mod internal {
pub use {
super::{Path, PathExtractor},
crate::{
error::Error,
input::param::{FromPercentEncoded, Params, PercentEncoded},
},
};
}
#[derive(Debug)]
pub struct Path<E: PathExtractor = ()> {
path: &'static str,
_marker: PhantomData<E>,
}
impl<E> Path<E>
where
E: PathExtractor,
{
pub fn new(path: &'static str) -> Self {
Self {
path,
_marker: PhantomData,
}
}
pub fn to<T>(
self,
endpoint: T,
) -> Route<
impl Handler<
Output = T::Output,
Error = Error,
Handle = self::handle::RouteHandle<E, T>, >,
>
where
T: Endpoint<E::Output>,
{
let Self { path, .. } = self;
let endpoint = Arc::new(endpoint);
let allowed_methods = endpoint.allowed_methods();
Route {
path: path.into(),
handler: crate::handler::handler(
move || self::handle::RouteHandle::new(endpoint.clone()),
allowed_methods,
),
}
}
}
mod handle {
use {
super::PathExtractor,
crate::{
endpoint::{ApplyContext, Endpoint},
error::Error,
future::{Poll, TryFuture},
input::Input,
},
std::{marker::PhantomData, sync::Arc},
};
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct RouteHandle<E, T>
where
E: PathExtractor,
T: Endpoint<E::Output>,
{
state: RouteHandleState<T, T::Future>,
_marker: PhantomData<E>,
}
impl<E, T> RouteHandle<E, T>
where
E: PathExtractor,
T: Endpoint<E::Output>,
{
pub fn new(endpoint: Arc<T>) -> Self {
Self {
state: RouteHandleState::Init(endpoint),
_marker: PhantomData,
}
}
}
#[allow(missing_debug_implementations)]
enum RouteHandleState<T, Fut> {
Init(Arc<T>),
InFlight(Fut),
}
impl<E, T> TryFuture for RouteHandle<E, T>
where
E: PathExtractor,
T: Endpoint<E::Output>,
{
type Ok = T::Output;
type Error = Error;
#[inline]
fn poll_ready(&mut self, input: &mut Input<'_>) -> Poll<Self::Ok, Self::Error> {
loop {
self.state = match self.state {
RouteHandleState::Init(ref endpoint) => {
let args = E::extract(input.params.as_ref())?;
RouteHandleState::InFlight(
endpoint
.apply(args, &mut ApplyContext::new(input))
.map_err(|(_args, err)| err)?,
)
}
RouteHandleState::InFlight(ref mut in_flight) => {
return in_flight.poll_ready(input).map_err(Into::into);
}
};
}
}
}
}