use crate::expect::{ExpectBytes, ExpectJson, ExpectString};
use crate::middleware::Middleware;
use crate::{
expect::ResponseExpectation,
http::{
headers::{HeaderName, ToHeaderValues},
Body, Method, Mime, Url,
},
};
use crate::{Client, Error, Request, Response, ResponseAsync, Result};
use futures_util::future::BoxFuture;
use http_types::convert::DeserializeOwned;
use serde::Serialize;
use std::{fmt, marker::PhantomData};
#[must_use]
pub struct RequestBuilder<Event, ExpectBody = Vec<u8>> {
req: Option<Request>,
cap_or_client: CapOrClient<Event>,
phantom: PhantomData<fn() -> Event>,
expectation: Box<dyn ResponseExpectation<Body = ExpectBody> + Send>,
}
enum CapOrClient<Event> {
Client(Client),
Capability(crate::Http<Event>),
}
impl<Event> RequestBuilder<Event, Vec<u8>> {
pub(crate) fn new(method: Method, url: Url, capability: crate::Http<Event>) -> Self {
Self {
req: Some(Request::new(method, url)),
cap_or_client: CapOrClient::Capability(capability),
phantom: PhantomData,
expectation: Box::new(ExpectBytes),
}
}
}
impl RequestBuilder<(), Vec<u8>> {
pub(crate) fn new_for_middleware(method: Method, url: Url, client: Client) -> Self {
Self {
req: Some(Request::new(method, url)),
cap_or_client: CapOrClient::Client(client),
phantom: PhantomData,
expectation: Box::new(ExpectBytes),
}
}
}
impl<Event, ExpectBody> RequestBuilder<Event, ExpectBody>
where
Event: 'static,
ExpectBody: 'static,
{
pub fn header(mut self, key: impl Into<HeaderName>, value: impl ToHeaderValues) -> Self {
self.req.as_mut().unwrap().insert_header(key, value);
self
}
pub fn content_type(mut self, content_type: impl Into<Mime>) -> Self {
self.req
.as_mut()
.unwrap()
.set_content_type(content_type.into());
self
}
pub fn body(mut self, body: impl Into<Body>) -> Self {
self.req.as_mut().unwrap().set_body(body);
self
}
pub fn body_json(self, json: &impl Serialize) -> crate::Result<Self> {
Ok(self.body(Body::from_json(json)?))
}
pub fn body_string(self, string: String) -> Self {
self.body(Body::from_string(string))
}
pub fn body_bytes(self, bytes: impl AsRef<[u8]>) -> Self {
self.body(Body::from(bytes.as_ref()))
}
pub fn query(mut self, query: &impl Serialize) -> std::result::Result<Self, Error> {
self.req.as_mut().unwrap().set_query(query)?;
Ok(self)
}
pub fn middleware(mut self, middleware: impl Middleware) -> Self {
self.req.as_mut().unwrap().middleware(middleware);
self
}
pub fn build(self) -> Request {
self.req.unwrap()
}
pub fn expect_string(self) -> RequestBuilder<Event, String> {
let expectation = Box::<ExpectString>::default();
RequestBuilder {
req: self.req,
cap_or_client: self.cap_or_client,
phantom: PhantomData,
expectation,
}
}
pub fn expect_json<T>(self) -> RequestBuilder<Event, T>
where
T: DeserializeOwned + 'static,
{
let expectation = Box::<ExpectJson<T>>::default();
RequestBuilder {
req: self.req,
cap_or_client: self.cap_or_client,
phantom: PhantomData,
expectation,
}
}
pub fn send<F>(self, make_event: F)
where
F: FnOnce(crate::Result<Response<ExpectBody>>) -> Event + Send + 'static,
{
let CapOrClient::Capability(capability) = self.cap_or_client else {
panic!("Called RequestBuilder::send in a middleware context");
};
let request = self.req;
let ctx = capability.context.clone();
ctx.spawn(async move {
let result = capability.client.send(request.unwrap()).await;
let resp = match result {
Ok(resp) => resp,
Err(e) => {
capability.context.update_app(make_event(Err(e)));
return;
}
};
let resp = Response::<Vec<u8>>::new(resp).await.unwrap();
capability
.context
.update_app(make_event(self.expectation.decode(resp)));
});
}
}
impl std::future::IntoFuture for RequestBuilder<()> {
type Output = Result<ResponseAsync>;
type IntoFuture = BoxFuture<'static, Result<ResponseAsync>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let CapOrClient::Client(client) = self.cap_or_client else {
panic!("Tried to await a RequestBuilder in a non-middleware context");
};
client.send(self.req.unwrap()).await
})
}
}
impl<Ev> fmt::Debug for RequestBuilder<Ev> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.req, f)
}
}