use std::fmt::Debug; use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; use actix_web::dev::Payload; use actix_web::web::Json; use actix_web::{FromRequest, HttpRequest}; use deserr::{DeserializeError, DeserializeFromValue}; use futures::ready; use meilisearch_types::error::{ErrorCode, ResponseError}; /// Extractor for typed data from Json request payloads /// deserialised by deserr. /// /// # Extractor /// To extract typed data from a request body, the inner type `T` must implement the /// [`deserr::DeserializeFromError`] trait. The inner type `E` must implement the /// [`ErrorCode`](meilisearch_error::ErrorCode) trait. #[derive(Debug)] pub struct ValidatedJson(pub T, PhantomData<*const E>); impl ValidatedJson { pub fn new(data: T) -> Self { ValidatedJson(data, PhantomData) } pub fn into_inner(self) -> T { self.0 } } impl FromRequest for ValidatedJson where E: DeserializeError + ErrorCode + std::error::Error + 'static, T: DeserializeFromValue, { type Error = actix_web::Error; type Future = ValidatedJsonExtractFut; #[inline] fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { ValidatedJsonExtractFut { fut: Json::::from_request(req, payload), _phantom: PhantomData, } } } pub struct ValidatedJsonExtractFut { fut: as FromRequest>::Future, _phantom: PhantomData<*const (T, E)>, } impl Future for ValidatedJsonExtractFut where T: DeserializeFromValue, E: DeserializeError + ErrorCode + std::error::Error + 'static, { type Output = Result, actix_web::Error>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let ValidatedJsonExtractFut { fut, .. } = self.get_mut(); let fut = Pin::new(fut); let res = ready!(fut.poll(cx)); let res = match res { Err(err) => Err(err), Ok(data) => match deserr::deserialize::<_, _, E>(data.into_inner()) { Ok(data) => Ok(ValidatedJson::new(data)), Err(e) => Err(ResponseError::from(e).into()), }, }; Poll::Ready(res) } }