pub mod params;
mod payload;
mod query;
pub use params::{PathParams, QueryParams};
pub use payload::{Aggregate, Coalesce, Payload};
use cookie::CookieJar;
use delegate::delegate;
use http::request::Parts;
use http::{Extensions, HeaderMap, Method, StatusCode, Uri, Version};
use http_body_util::Limited;
use hyper::body::Incoming;
use std::fmt::{self, Debug, Formatter};
use crate::app::Shared;
use crate::error::Error;
use crate::response::{Finalize, Response, ResponseBuilder};
use params::PathParam;
pub struct Envelope {
parts: Parts,
params: Vec<via_router::PathParam>,
cookies: CookieJar,
}
pub struct Request<App = ()> {
envelope: Envelope,
body: Limited<Incoming>,
app: Shared<App>,
}
impl Envelope {
#[inline]
pub fn method(&self) -> &Method {
&self.parts.method
}
#[inline]
pub fn uri(&self) -> &Uri {
&self.parts.uri
}
#[inline]
pub fn version(&self) -> Version {
self.parts.version
}
#[inline]
pub fn headers(&self) -> &HeaderMap {
&self.parts.headers
}
#[inline]
pub fn cookies(&self) -> &CookieJar {
&self.cookies
}
#[inline]
pub fn cookies_mut(&mut self) -> &mut CookieJar {
&mut self.cookies
}
#[inline]
pub fn extensions(&self) -> &Extensions {
&self.parts.extensions
}
#[inline]
pub fn extensions_mut(&mut self) -> &mut Extensions {
&mut self.parts.extensions
}
pub fn param<'b>(&self, name: &'b str) -> PathParam<'_, 'b> {
let param = params::get(&self.params, name);
PathParam::new(self.uri().path(), param, name)
}
pub fn query<'a, T>(&'a self) -> crate::Result<T>
where
T: TryFrom<QueryParams<'a>, Error = Error>,
{
T::try_from(QueryParams::new(self.uri().query()))
}
pub fn params<'a, T>(&'a self) -> crate::Result<T>
where
T: TryFrom<PathParams<'a>>,
Error: From<T::Error>,
{
let params = PathParams::new(self.uri().path(), &self.params);
T::try_from(params).map_err(|error| {
let mut error = Error::from(error);
*error.status_mut() = StatusCode::BAD_REQUEST;
error
})
}
}
impl Envelope {
#[inline]
pub(crate) fn new(parts: Parts, params: Vec<via_router::PathParam>) -> Self {
Self {
parts,
params,
cookies: CookieJar::new(),
}
}
}
impl Debug for Envelope {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
#[derive(Debug)]
struct CookieJar;
f.debug_struct("Envelope")
.field("method", self.method())
.field("uri", self.uri())
.field("params", &self.params)
.field("version", &self.version())
.field("headers", self.headers())
.field("cookies", &CookieJar)
.field("extensions", self.extensions())
.finish()
}
}
impl<App> Request<App> {
#[inline]
pub(crate) fn new(envelope: Envelope, body: Limited<Incoming>, app: Shared<App>) -> Self {
Self {
envelope,
body,
app,
}
}
#[inline]
pub fn app(&self) -> &App {
&self.app
}
pub fn app_owned(&self) -> Shared<App> {
self.app.clone()
}
#[inline]
pub fn envelope(&self) -> &Envelope {
&self.envelope
}
delegate! {
to self.envelope() {
pub fn method(&self) -> &Method;
pub fn uri(&self) -> &Uri;
pub fn version(&self) -> Version;
pub fn headers(&self) -> &HeaderMap;
}
}
#[inline]
pub fn cookies(&self) -> &CookieJar {
self.envelope().cookies()
}
pub fn cookies_mut(&mut self) -> &mut CookieJar {
self.envelope.cookies_mut()
}
pub fn extensions(&self) -> &Extensions {
self.envelope().extensions()
}
#[inline]
pub fn extensions_mut(&mut self) -> &mut Extensions {
self.envelope.extensions_mut()
}
delegate! {
to self.envelope() {
pub fn param<'b>(&self, name: &'b str) -> PathParam<'_, 'b>;
pub fn query<'a, T>(&'a self) -> crate::Result<T>
where
T: TryFrom<QueryParams<'a>, Error = Error>;
pub fn params<'a, T>(&'a self) -> crate::Result<T>
where
T: TryFrom<PathParams<'a>>,
Error: From<T::Error>;
}
}
pub fn into_future(self) -> (Coalesce, Shared<App>) {
(Coalesce::new(self.body), self.app)
}
#[inline]
pub fn into_parts(self) -> (Envelope, Limited<Incoming>, Shared<App>) {
(self.envelope, self.body, self.app)
}
}
impl<App> Debug for Request<App> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("Request")
.field("envelope", self.envelope())
.field("body", &self.body)
.field("app", &self.app)
.finish()
}
}
impl<App> Finalize for Request<App> {
fn finalize(self, response: ResponseBuilder) -> Result<Response, Error> {
use http::header::{CONTENT_LENGTH, CONTENT_TYPE, TRANSFER_ENCODING};
use http_body_util::combinators::BoxBody;
let headers = self.headers();
let mut response = match headers.get(CONTENT_LENGTH).cloned() {
Some(content_length) => response.header(CONTENT_LENGTH, content_length),
None => response.header(TRANSFER_ENCODING, "chunked"),
};
if let Some(content_type) = headers.get(CONTENT_TYPE).cloned() {
response = response.header(CONTENT_TYPE, content_type);
}
response.body(BoxBody::new(self.body).into())
}
}