use super::{Body, Client};
use crate::net::http::request::body::{BodyOwned, BodyTrait};
#[cfg(feature = "net-signature")]
use crate::net::http::request::client::GLOBAL_SIGNING;
use crate::net::http::request::{Error, Header, HeaderMap, Response, Url};
#[cfg(feature = "net-signature")]
use crate::net::signature::Signature;
use crate::error::{DynTracerError, TracerError};
use crate::net::http::request::header::values::Values;
use crate::net::http::request::header::Name;
use crate::rails::ext::syn::{RailsMapErrInto, RailsMapInto};
use alloc::{string::String, sync::Arc};
use core::fmt;
use core::future::Future;
use crate_serde::ser::{self, SerializeStruct};
use crate_serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(feature = "net-signature")]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
#[derive(Clone)]
pub struct Builder {
client: Option<Arc<Client>>,
method: Method,
url: Option<Url>,
headers: Option<HeaderMap>,
body: Option<BodyOwned>,
content_type: Option<String>,
signature: Option<Signature>,
}
#[cfg(not(feature = "net-signature"))]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
#[derive(Clone)]
pub struct RequestBuilder {
client: Option<Arc<Client>>,
method: Method,
uri: Option<Url>,
headers: HeaderMap,
body: None,
content_type: String,
}
impl Builder {
pub const GET: Builder = Self::new(Method::Get);
pub const POST: Builder = Self::new(Method::Post);
pub const PUT: Builder = Self::new(Method::Put);
pub const DELETE: Builder = Self::new(Method::Delete);
pub const PATCH: Builder = Self::new(Method::Patch);
pub const HEADER: Builder = Self::new(Method::Head);
pub const OPTIONS: Builder = Self::new(Method::Options);
pub const CONNECT: Builder = Self::new(Method::Connect);
pub const TRACE: Builder = Self::new(Method::Trace);
pub const fn new(method: Method) -> Self {
Self {
client: None,
method,
url: None,
headers: None,
body: None,
content_type: None,
#[cfg(feature = "net-signature")]
signature: None,
}
}
pub fn new_with_client(client: Option<Client>, method: Method, uri: Url) -> Self {
Self {
client: client.map(Arc::new),
method,
url: Option::from(uri),
headers: None,
body: None,
content_type: None,
#[cfg(feature = "net-signature")]
signature: GLOBAL_SIGNING.read().clone(),
}
}
pub fn content_type(mut self, content_type: &str) -> Self {
self.headers
.get_or_insert(HeaderMap::default())
.insert(("Content-Type", content_type).into());
self
}
#[cfg(feature = "net-signature")]
pub fn with_signing_default(mut self) -> Self {
self.signature = Some(Signature::default());
self
}
#[cfg(feature = "net-signature")]
pub fn with_signing(mut self, signature: Signature) -> Self {
self.signature = Some(signature);
self
}
pub async fn send(mut self) -> Result<Response, Error> {
let uri: reqwest::Url = self.url.as_ref().map(|t| t.into()).ok_or(Error::NoUrl)?;
let request = (&self.method).into();
let headers = self
.headers
.get_or_insert(HeaderMap::default())
.clone()
.try_into()
.map_err_into::<Error>()?;
let body = self
.body
.get_or_insert(BodyOwned::default())
.into_bytes()
.await
.map_err(Error::BodyError)?;
reqwest::Client::new()
.request(request, uri)
.headers(headers)
.body(body)
.send()
.await
.map_err_into()
.map_into()
}
}
impl Builder {
pub fn method<T: Into<Method>>(mut self, method: T) -> Self {
self.method = method.into();
self
}
pub fn method_mut(&mut self) -> &mut Method {
&mut self.method
}
pub fn header<H: Into<Header>>(mut self, header: H) -> Self {
self.headers
.get_or_insert(HeaderMap::default())
.insert(header.into());
self
}
pub fn header_mut<N: Into<Name>>(&mut self, name: N) -> Option<&mut Values> {
self.headers
.get_or_insert(HeaderMap::default())
.get_mut(name)
}
pub fn headers<H: Into<HeaderMap>>(mut self, headers: H) -> Self {
self.headers
.get_or_insert(HeaderMap::default())
.extend(headers.into());
self
}
pub fn headers_mut(&mut self) -> &mut HeaderMap {
self.headers.get_or_insert(HeaderMap::default())
}
pub fn body<B: Into<Body>>(mut self, body: B) -> Self {
self.body = Some(BodyOwned::from(body.into()));
self
}
pub fn body_mut(&mut self) -> &mut BodyOwned {
self.body.get_or_insert(BodyOwned::default())
}
pub async fn update_body<
P: FnOnce(Box<Body>) -> O,
O: Future<Output = Result<Box<Body>, DynTracerError>>,
>(
&mut self,
o: P,
) -> Result<(), DynTracerError> {
let new_body = o(self
.body
.get_or_insert(BodyOwned::default())
.body
.lock()
.clone())
.await?;
*self.body.get_or_insert(BodyOwned::default()).body.lock() = new_body;
Ok(())
}
pub fn url<U: Into<Url>>(mut self, url: U) -> Self {
self.url = Some(url.into());
self
}
pub fn url_mut(&mut self) -> &mut Option<Url> {
&mut self.url
}
}
impl Builder {
pub fn set_method(mut self, method: Method) -> Self {
self.method = method.into();
self
}
pub fn get_method(&self) -> Method {
self.method.clone()
}
pub fn set_header(mut self, header: Header) -> Self {
self.headers
.get_or_insert(HeaderMap::default())
.insert(header.into());
self
}
pub fn set_headers(mut self, headers: HeaderMap) -> Self {
self.headers
.get_or_insert(HeaderMap::default())
.extend(headers.into());
self
}
pub fn get_headers(&self) -> Option<HeaderMap> {
self.headers.clone()
}
pub fn add_header(mut self, name: String, value: String) -> Self {
self.headers
.get_or_insert(HeaderMap::default())
.insert((name, value).into());
self
}
pub fn add_headers(mut self, headers: Vec<(String, String)>) -> Self {
self.headers
.get_or_insert(HeaderMap::default())
.extend(headers.into());
self
}
pub fn get_header(&self, name: &str) -> Option<Header> {
self.headers.as_ref().and_then(|headers| headers.get(name))
}
pub fn set_body(mut self, body: Body) -> Self {
self.body = Some(body.into());
self
}
pub fn get_body(&self) -> Option<&BodyOwned> {
self.body.as_ref()
}
pub fn set_uri(mut self, uri: &str) -> Self {
self.url = Some(Url::from(uri));
self
}
pub fn get_uri(&self) -> Option<&Url> {
self.url.as_ref()
}
}
impl fmt::Debug for Builder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let client_placeholder = match &self.client {
Some(_) => "<Client>",
None => "None",
};
let mut debug_struct = f.debug_struct("RequestBuilder");
debug_struct
.field("client", &client_placeholder)
.field("method", &self.method)
.field("uri", &self.url)
.field("headers", &self.headers)
.field("body", &"<Body>")
.field("content_type", &self.content_type);
#[cfg(feature = "net-signature")]
debug_struct.field("signature", &self.signature);
debug_struct.finish()
}
}
impl Default for Builder {
fn default() -> Self {
Self {
client: None,
method: Method::None,
url: None,
headers: None,
body: None,
content_type: None,
#[cfg(feature = "net-signature")]
signature: GLOBAL_SIGNING.read().clone(),
}
}
}
impl ser::Serialize for Builder {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("RequestBuilder", 6)?;
state.serialize_field("method", &self.method)?;
state.serialize_field("uri", &self.url)?;
state.serialize_field("headers", &self.headers)?;
if let Some(ref body_owned) = self.body {
let _t = body_owned
.try_sync_into_string()
.map(|t| state.serialize_field("body", &t))
.map_err(|t| ser::Error::custom(t.to_string()))?;
} else {
state.serialize_field("body", &self.body)?;
}
if let Some(ref content_type) = self.content_type {
state.serialize_field("content_type", &content_type)?;
}
#[cfg(feature = "net-signature")]
if let Some(ref signature) = self.signature {
state.serialize_field("signature", &signature)?;
}
state.end()
}
}
impl<'de> Deserialize<'de> for Builder {
fn deserialize<D>(deserializer: D) -> Result<Builder, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct BuilderHelper {
method: Method,
uri: Option<Url>,
headers: Option<HeaderMap>,
body: Option<String>,
content_type: Option<String>,
#[cfg(feature = "net-signature")]
signature: Option<Signature>,
}
let data = BuilderHelper::deserialize(deserializer)?;
let body = if let Some(body_str) = data.body {
Some(BodyOwned::from(body_str))
} else {
None
};
Ok(Builder {
client: None,
method: data.method,
url: data.uri,
headers: data.headers,
body,
content_type: data.content_type,
#[cfg(feature = "net-signature")]
signature: data.signature,
})
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum Method {
Get,
Post,
Put,
Delete,
Patch,
Head,
Options,
Connect,
Trace,
None,
}
impl Method {}
impl From<&str> for Method {
fn from(s: &str) -> Self {
match s.to_lowercase().as_str() {
"get" => Method::Get,
"post" => Method::Post,
"put" => Method::Put,
"delete" => Method::Delete,
"patch" => Method::Patch,
"head" => Method::Head,
"option" => Method::Options,
"connect" => Method::Connect,
"trace" => Method::Trace,
_ => Method::None,
}
}
}
impl From<String> for Method {
fn from(s: String) -> Self {
Method::from(s.as_str())
}
}
impl From<Method> for reqwest::Method {
fn from(m: Method) -> Self {
match m {
Method::Get => reqwest::Method::GET,
Method::Post => reqwest::Method::POST,
Method::Put => reqwest::Method::PUT,
Method::Delete => reqwest::Method::DELETE,
Method::Patch => reqwest::Method::PATCH,
Method::Head => reqwest::Method::HEAD,
Method::Options => reqwest::Method::OPTIONS,
Method::Connect => reqwest::Method::CONNECT,
Method::Trace => reqwest::Method::TRACE,
Method::None => reqwest::Method::GET,
}
}
}
impl From<&Method> for reqwest::Method {
fn from(m: &Method) -> Self {
reqwest::Method::from(m.clone())
}
}
impl From<&Method> for Method {
fn from(m: &Method) -> Self {
m.clone()
}
}
#[cfg(test)]
pub mod test {
use super::Builder;
use serde_json;
#[test]
fn test_builder_serialization() {
let builder = Builder::GET;
let serialized = serde_json::to_string(&builder).unwrap();
println!("{}", serialized);
}
#[test]
fn test_builder_deserialization() {
let data = r#"{"method":"Get","uri":null,"headers":null,"body":null,"content_type":null}"#;
let builder: Builder = serde_json::from_str(data).unwrap();
println!("{:?}", builder);
}
}