use std::{borrow::Cow, fmt};
use musdk_common::{
http_client::{
header::{AUTHORIZATION_HEADER, CONTENT_TYPE_HEADER},
*,
},
incoming_message::IncomingMessage,
outgoing_message::OutgoingMessage,
};
use serde::Serialize;
use crate::{error, MuContext};
pub struct HttpClient<'c> {
ctx: &'c mut MuContext,
}
impl<'c> HttpClient<'c> {
pub fn execute_request(
&mut self,
req: Request,
) -> Result<Result<Response<'static>, Error>, ClientError> {
self.ctx
.write_message(OutgoingMessage::HttpRequest(req))
.map_err(ClientError::SendRequest)?;
match self.ctx.read_message().map_err(ClientError::RecvResponse)? {
IncomingMessage::HttpResponse(response) => Ok(response),
_ => Err(ClientError::RecvResponse(
error::Error::UnexpectedMessageKind("HttpResponse"),
)),
}
}
pub fn get<S: Into<String>>(&mut self, url: S) -> RequestBuilder {
self.request(HttpMethod::Get, url.into())
}
pub fn post<S: Into<String>>(&mut self, url: S) -> RequestBuilder {
self.request(HttpMethod::Post, url.into())
}
pub fn put<S: Into<String>>(&mut self, url: S) -> RequestBuilder {
self.request(HttpMethod::Put, url.into())
}
pub fn patch<S: Into<String>>(&mut self, url: S) -> RequestBuilder {
self.request(HttpMethod::Patch, url.into())
}
pub fn delete<S: Into<String>>(&mut self, url: S) -> RequestBuilder {
self.request(HttpMethod::Delete, url.into())
}
pub fn head<S: Into<String>>(&mut self, url: S) -> RequestBuilder {
self.request(HttpMethod::Head, url.into())
}
pub fn request<S: Into<String>>(&mut self, method: HttpMethod, url: S) -> RequestBuilder {
let req = Request::new(method, url.into());
let client = HttpClient { ctx: self.ctx };
RequestBuilder::new(client, Ok(req))
}
pub fn execute(&mut self, request: Request) -> Result<Result<Response, Error>, ClientError> {
self.execute_request(request)
}
#[inline]
pub(crate) fn new(ctx: &'c mut MuContext) -> Self {
Self { ctx }
}
}
#[must_use = "RequestBuilder does nothing until you 'send' it"]
pub struct RequestBuilder<'a, 'c: 'a> {
client: HttpClient<'c>,
request: Result<Request<'a>, Error>,
}
impl<'a, 'c: 'a> RequestBuilder<'a, 'c> {
pub(super) fn new(client: HttpClient<'c>, request: Result<Request<'a>, Error>) -> Self {
RequestBuilder { client, request }
}
fn header<K, V>(mut self, name: K, value: V) -> Self
where
Cow<'a, str>: From<K>,
Cow<'a, str>: From<V>,
{
if let Ok(ref mut req) = self.request {
req.headers.push(Header {
name: name.into(),
value: value.into(),
});
}
self
}
pub fn headers(mut self, mut headers: Vec<Header<'a>>) -> Self {
if let Ok(ref mut req) = self.request {
req.headers.append(&mut headers);
}
self
}
pub fn bearer_auth<T>(self, token: T) -> Self
where
T: fmt::Display,
{
let header_value = format!("Bearer {token}");
self.header(AUTHORIZATION_HEADER, header_value)
}
pub fn body<T: Into<Body<'a>>>(mut self, body: T) -> Self {
if let Ok(ref mut req) = self.request {
req.body = body.into();
}
self
}
pub fn version(mut self, version: Version) -> Self {
if let Ok(ref mut req) = self.request {
req.version = version;
}
self
}
pub fn form<T: Serialize + ?Sized>(mut self, form: &'a T) -> RequestBuilder {
let mut error = None;
if let Ok(ref mut req) = self.request {
match serde_urlencoded::to_string(form) {
Ok(body) => {
req.headers.push(Header {
name: CONTENT_TYPE_HEADER.into(),
value: "application/x-www-form-urlencoded".into(),
});
req.body = body.into_bytes().into();
}
Err(err) => {
error = Some(Error::Request(format!("failed to serialize url: {err:?}")))
}
}
}
if let Some(err) = error {
self.request = Err(err);
}
self
}
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
pub fn json<T: Serialize + ?Sized>(mut self, json: &T) -> Self {
let mut error = None;
if let Ok(ref mut req) = self.request {
match serde_json::to_vec(json) {
Ok(body) => {
req.headers.push(Header {
name: CONTENT_TYPE_HEADER.into(),
value: "application/json".into(),
});
req.body = body.into();
}
Err(err) => {
error = Some(Error::Request(format!(
"failed to serialize request: {err:?}"
)))
}
}
}
if let Some(err) = error {
self.request = Err(err);
}
self
}
pub fn build(self) -> Result<Request<'a>, Error> {
self.request
}
pub fn send(mut self) -> Result<Result<Response<'static>, Error>, ClientError> {
match self.request {
Ok(req) => self.client.execute_request(req),
Err(e) => Ok(Err(e)),
}
}
}
impl<'a> fmt::Debug for RequestBuilder<'a, '_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut builder = f.debug_struct("RequestBuilder");
match self.request {
Ok(ref req) => fmt_request_fields(&mut builder, req).finish(),
Err(ref err) => builder.field("error", err).finish(),
}
}
}
fn fmt_request_fields<'a, 'b>(
f: &'a mut fmt::DebugStruct<'a, 'b>,
req: &Request,
) -> &'a mut fmt::DebugStruct<'a, 'b> {
f.field("method", &req.method)
.field("url", &req.url)
.field("headers", &req.headers)
}
#[derive(Debug)]
pub enum ClientError {
SendRequest(error::Error),
RecvResponse(error::Error),
}