use bytes::Bytes;
use http::{Request, Response, StatusCode};
use http_body::Body;
use http_body_util::BodyExt;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use vld::schema::VldParse;
#[derive(Clone)]
pub struct ValidateJsonLayer<T> {
_marker: PhantomData<fn() -> T>,
}
impl<T> ValidateJsonLayer<T> {
pub fn new() -> Self {
Self {
_marker: PhantomData,
}
}
}
impl<T> Default for ValidateJsonLayer<T> {
fn default() -> Self {
Self::new()
}
}
impl<S, T> tower_layer::Layer<S> for ValidateJsonLayer<T> {
type Service = ValidateJsonService<S, T>;
fn layer(&self, inner: S) -> Self::Service {
ValidateJsonService {
inner,
_marker: PhantomData,
}
}
}
#[derive(Clone)]
pub struct ValidateJsonService<S, T> {
inner: S,
_marker: PhantomData<fn() -> T>,
}
impl<S, T, ReqBody, ResBody> tower_service::Service<Request<ReqBody>> for ValidateJsonService<S, T>
where
S: tower_service::Service<Request<http_body_util::Full<Bytes>>, Response = Response<ResBody>>
+ Clone
+ Send
+ 'static,
S::Future: Send + 'static,
S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
ReqBody: Body + Send + 'static,
ReqBody::Data: Send,
ReqBody::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
ResBody: From<http_body_util::Full<Bytes>> + Send + 'static,
T: VldParse + Clone + Send + Sync + 'static,
{
type Response = Response<ResBody>;
type Error = Box<dyn std::error::Error + Send + Sync>;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx).map_err(Into::into)
}
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
let mut inner = self.inner.clone();
std::mem::swap(&mut self.inner, &mut inner);
Box::pin(async move {
let is_json = req
.headers()
.get(http::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.map(|ct| ct.starts_with("application/json"))
.unwrap_or(false);
if !is_json {
let (parts, body) = req.into_parts();
let bytes = body
.collect()
.await
.map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { e.into() })?
.to_bytes();
let new_req = Request::from_parts(parts, http_body_util::Full::new(bytes));
return inner.call(new_req).await.map_err(Into::into);
}
let (parts, body) = req.into_parts();
let bytes = body
.collect()
.await
.map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { e.into() })?
.to_bytes();
let json_value: serde_json::Value = match serde_json::from_slice(&bytes) {
Ok(v) => v,
Err(e) => {
let error_body = vld_http_common::format_json_parse_error(&e.to_string());
let resp = Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(http::header::CONTENT_TYPE, "application/json")
.body(ResBody::from(http_body_util::Full::new(Bytes::from(
serde_json::to_vec(&error_body).unwrap_or_default(),
))))
.unwrap();
return Ok(resp);
}
};
match T::vld_parse_value(&json_value) {
Ok(validated) => {
let mut new_req = Request::from_parts(parts, http_body_util::Full::new(bytes));
new_req.extensions_mut().insert(validated);
inner.call(new_req).await.map_err(Into::into)
}
Err(vld_err) => {
let error_body = vld_http_common::format_vld_error(&vld_err);
let resp = Response::builder()
.status(StatusCode::UNPROCESSABLE_ENTITY)
.header(http::header::CONTENT_TYPE, "application/json")
.body(ResBody::from(http_body_util::Full::new(Bytes::from(
serde_json::to_vec(&error_body).unwrap_or_default(),
))))
.unwrap();
Ok(resp)
}
}
})
}
}
pub fn validated<T: Clone + Send + Sync + 'static>(req: &Request<impl Body>) -> T {
req.extensions()
.get::<T>()
.expect(
"vld-tower: validated value not found in request extensions. \
Make sure ValidateJsonLayer is applied.",
)
.clone()
}
pub fn try_validated<T: Clone + Send + Sync + 'static>(req: &Request<impl Body>) -> Option<T> {
req.extensions().get::<T>().cloned()
}
pub mod prelude {
pub use crate::{try_validated, validated, ValidateJsonLayer, ValidateJsonService};
}