use std::convert::Infallible;
use std::marker::PhantomData;
use http::{HeaderMap, HeaderName};
use crate::Request;
use crate::guard::error::{MissingUriQuery, UnknownExtension};
pub struct Extensions;
pub struct Headers;
pub struct Method;
pub struct Path;
pub struct Query;
pub struct Uri;
pub trait Project<Input> {
type Error<'a>
where
Self: 'a;
type Output: ?Sized;
fn project<'a, 'b>(&'a self, input: &'b Input) -> Result<&'b Self::Output, Self::Error<'a>>;
}
pub struct Extension<T> {
pub(super) _ty: PhantomData<T>,
}
pub(crate) struct Header {
pub(super) name: HeaderName,
}
impl<App> Project<Request<App>> for Extensions {
type Error<'a> = Infallible;
type Output = http::Extensions;
fn project<'a>(&self, request: &'a Request<App>) -> Result<&'a Self::Output, Infallible> {
Ok(request.extensions())
}
}
impl<T> Project<http::Extensions> for Extension<T>
where
T: Send + Sync + 'static,
{
type Error<'a> = UnknownExtension;
type Output = T;
fn project<'a>(&self, ext: &'a http::Extensions) -> Result<&'a Self::Output, UnknownExtension> {
ext.get().ok_or(UnknownExtension)
}
}
impl<T, App> Project<Request<App>> for Extension<T>
where
T: Send + Sync + 'static,
{
type Error<'a> = UnknownExtension;
type Output = T;
fn project<'a>(&self, request: &'a Request<App>) -> Result<&'a Self::Output, UnknownExtension> {
self.project(request.extensions())
}
}
impl<App> Project<Request<App>> for Headers {
type Error<'a> = Infallible;
type Output = http::HeaderMap;
fn project<'a>(&self, request: &'a Request<App>) -> Result<&'a Self::Output, Infallible> {
Ok(request.headers())
}
}
impl Project<HeaderMap> for Header {
type Error<'a> = &'a HeaderName;
type Output = [u8];
fn project<'a, 'b>(
&'a self,
headers: &'b HeaderMap,
) -> Result<&'b Self::Output, Self::Error<'a>> {
let name = &self.name;
if let Some(value) = headers.get(name) {
Ok(value.as_bytes())
} else {
Err(name)
}
}
}
impl<App> Project<Request<App>> for Method {
type Error<'a> = Infallible;
type Output = http::Method;
fn project<'a>(&self, request: &'a Request<App>) -> Result<&'a Self::Output, Infallible> {
Ok(request.method())
}
}
impl Project<http::Uri> for Query {
type Error<'a> = MissingUriQuery;
type Output = [u8];
fn project<'a>(&self, uri: &'a http::Uri) -> Result<&'a Self::Output, MissingUriQuery> {
uri.query().map(str::as_bytes).ok_or(MissingUriQuery)
}
}
impl<App> Project<Request<App>> for Query {
type Error<'a> = MissingUriQuery;
type Output = [u8];
fn project<'a>(&self, request: &'a Request<App>) -> Result<&'a Self::Output, MissingUriQuery> {
self.project(request.uri())
}
}
impl Project<http::Uri> for Path {
type Error<'a> = Infallible;
type Output = [u8];
fn project<'a>(&self, uri: &'a http::Uri) -> Result<&'a Self::Output, Infallible> {
Ok(uri.path().as_bytes())
}
}
impl<App> Project<Request<App>> for Path {
type Error<'a> = Infallible;
type Output = [u8];
fn project<'a>(&self, request: &'a Request<App>) -> Result<&'a Self::Output, Infallible> {
self.project(request.uri())
}
}
impl<App> Project<Request<App>> for Uri {
type Error<'a> = Infallible;
type Output = http::Uri;
fn project<'a>(&self, request: &'a Request<App>) -> Result<&'a Self::Output, Infallible> {
Ok(request.uri())
}
}