use crate::path_params::PathParams;
use bytes::Bytes;
use http::{request::Parts, Extensions, HeaderMap, Method, Uri, Version};
use http_body_util::BodyExt;
use hyper::body::Incoming;
use std::sync::Arc;
pub enum BodyVariant {
Buffered(Bytes),
Streaming(Incoming),
Consumed,
}
pub struct Request {
pub(crate) parts: Parts,
pub(crate) body: BodyVariant,
pub(crate) state: Arc<Extensions>,
pub(crate) path_params: PathParams,
}
impl Request {
pub fn new(
parts: Parts,
body: BodyVariant,
state: Arc<Extensions>,
path_params: PathParams,
) -> Self {
Self {
parts,
body,
state,
path_params,
}
}
pub fn method(&self) -> &Method {
&self.parts.method
}
pub fn uri(&self) -> &Uri {
&self.parts.uri
}
pub fn version(&self) -> Version {
self.parts.version
}
pub fn headers(&self) -> &HeaderMap {
&self.parts.headers
}
pub fn extensions(&self) -> &Extensions {
&self.parts.extensions
}
pub fn extensions_mut(&mut self) -> &mut Extensions {
&mut self.parts.extensions
}
pub fn path(&self) -> &str {
self.parts.uri.path()
}
pub fn query_string(&self) -> Option<&str> {
self.parts.uri.query()
}
pub fn take_body(&mut self) -> Option<Bytes> {
match std::mem::replace(&mut self.body, BodyVariant::Consumed) {
BodyVariant::Buffered(bytes) => Some(bytes),
other => {
self.body = other;
None
}
}
}
pub fn take_stream(&mut self) -> Option<Incoming> {
match std::mem::replace(&mut self.body, BodyVariant::Consumed) {
BodyVariant::Streaming(stream) => Some(stream),
other => {
self.body = other;
None
}
}
}
pub async fn load_body(&mut self) -> Result<(), crate::error::ApiError> {
let new_body = match std::mem::replace(&mut self.body, BodyVariant::Consumed) {
BodyVariant::Streaming(incoming) => {
let collected = incoming
.collect()
.await
.map_err(|e| crate::error::ApiError::bad_request(e.to_string()))?;
BodyVariant::Buffered(collected.to_bytes())
}
BodyVariant::Buffered(b) => BodyVariant::Buffered(b),
BodyVariant::Consumed => BodyVariant::Consumed,
};
self.body = new_body;
Ok(())
}
pub fn path_params(&self) -> &PathParams {
&self.path_params
}
pub fn path_param(&self, name: &str) -> Option<&String> {
self.path_params.get(name)
}
pub fn state(&self) -> &Arc<Extensions> {
&self.state
}
pub(crate) fn set_path_params(&mut self, params: PathParams) {
self.path_params = params;
}
#[cfg(any(test, feature = "test-utils"))]
pub fn from_http_request<B>(req: http::Request<B>, body: Bytes) -> Self {
let (parts, _) = req.into_parts();
Self {
parts,
body: BodyVariant::Buffered(body),
state: Arc::new(Extensions::new()),
path_params: PathParams::new(),
}
}
pub fn try_clone(&self) -> Option<Self> {
let mut builder = http::Request::builder()
.method(self.method().clone())
.uri(self.uri().clone())
.version(self.version());
if let Some(headers) = builder.headers_mut() {
*headers = self.headers().clone();
}
let req = builder.body(()).ok()?;
let (parts, _) = req.into_parts();
let new_body = match &self.body {
BodyVariant::Buffered(b) => BodyVariant::Buffered(b.clone()),
BodyVariant::Streaming(_) => return None, BodyVariant::Consumed => return None,
};
Some(Self {
parts,
body: new_body,
state: self.state.clone(),
path_params: self.path_params.clone(),
})
}
}
impl std::fmt::Debug for Request {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Request")
.field("method", &self.parts.method)
.field("uri", &self.parts.uri)
.field("version", &self.parts.version)
.finish()
}
}