use pin_project_lite::pin_project;
use serde::de::DeserializeOwned;
use std::{
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use crate::{
Json,
error::Error,
http::endpoints::args::{FromPayload, Payload, Source},
};
pin_project! {
pub struct ExtractVecFromPayloadFut<T, F> {
#[pin]
inner: F,
_marker: PhantomData<T>
}
}
impl<F, T> Future for ExtractVecFromPayloadFut<T, F>
where
F: Future<Output = Result<Json<Vec<T>>, Error>>,
{
type Output = Result<Vec<T>, Error>;
#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
match this.inner.poll(cx) {
Poll::Ready(Ok(json)) => Poll::Ready(Ok(json.into_inner())),
Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
Poll::Pending => Poll::Pending,
}
}
}
impl<T> FromPayload for Vec<T>
where
T: DeserializeOwned + Send,
{
type Future = ExtractVecFromPayloadFut<T, <Json<Vec<T>> as FromPayload>::Future>;
const SOURCE: Source = Source::Body;
#[inline]
fn from_payload(payload: Payload<'_>) -> Self::Future {
ExtractVecFromPayloadFut {
inner: Json::<Vec<T>>::from_payload(payload),
_marker: PhantomData,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::HttpBody;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Ord, PartialOrd, PartialEq, Eq)]
struct User {
age: i32,
name: String,
}
#[tokio::test]
async fn it_extracts_vec_of_integers() {
let body = HttpBody::boxed(HttpBody::json([1, 2, 3]).unwrap());
let payload = Payload::Body(body);
let fut = <Vec<i32> as FromPayload>::from_payload(payload);
let result = fut.await;
assert_eq!(result.unwrap(), vec![1, 2, 3]);
}
#[tokio::test]
async fn it_extracts_vec_of_strings() {
let body = HttpBody::boxed(HttpBody::json(["foo", "bar"]).unwrap());
let payload = Payload::Body(body);
let fut = <Vec<String> as FromPayload>::from_payload(payload);
let result = fut.await;
assert_eq!(result.unwrap(), vec!["foo".to_string(), "bar".to_string()]);
}
#[tokio::test]
async fn it_extracts_vec_empty_array() {
let body = HttpBody::boxed(HttpBody::json(Vec::<i32>::new()).unwrap());
let payload = Payload::Body(body);
let fut = <Vec<i32> as FromPayload>::from_payload(payload);
let result = fut.await;
assert_eq!(result.unwrap(), Vec::<i32>::new());
}
#[tokio::test]
async fn it_extracts_vec_of_struct_array() {
let body = HttpBody::boxed(
HttpBody::json([
User {
age: 33,
name: "John".into(),
},
User {
age: 30,
name: "Jack".into(),
},
])
.unwrap(),
);
let payload = Payload::Body(body);
let fut = <Vec<User> as FromPayload>::from_payload(payload);
let result = fut.await;
assert_eq!(
result.unwrap(),
vec![
User {
age: 33,
name: "John".into()
},
User {
age: 30,
name: "Jack".into()
}
]
);
}
}