use crate::Config;
use crate::common::types::{Bodies, Headers, QueryParams};
use crate::interceptor::InterceptorChain;
use reqwest::{Method, RequestBuilder as ReqwestRequestBuilder};
use serde_json::Value;
use std::collections::HashMap;
use std::time::Duration;
pub struct RequestSpec<U, F>
where
U: FnOnce(&Config) -> String,
F: FnOnce(&Config, &mut RequestBuilder),
{
pub url_fn: U,
pub builder_fn: F,
pub retry_count: u32,
pub module_interceptors: Option<InterceptorChain>,
}
impl<U, F> RequestSpec<U, F>
where
U: FnOnce(&Config) -> String,
F: FnOnce(&Config, &mut RequestBuilder),
{
pub fn new(
url_fn: U,
builder_fn: F,
retry_count: u32,
module_interceptors: Option<InterceptorChain>,
) -> Self {
Self {
url_fn,
builder_fn,
retry_count,
module_interceptors,
}
}
pub fn new_with_defaults(url_fn: U, builder_fn: F) -> Self {
Self {
url_fn,
builder_fn,
retry_count: 0,
module_interceptors: None,
}
}
pub fn with_retry_count(mut self, retry_count: u32) -> Self {
self.retry_count = retry_count;
self
}
pub fn with_interceptors(mut self, interceptors: Option<InterceptorChain>) -> Self {
self.module_interceptors = interceptors;
self
}
}
#[derive(Debug, Clone)]
pub struct Request {
method: Method,
url: String,
headers: Headers,
query_params: QueryParams,
body_fields: Option<Bodies>,
timeout: Option<Duration>,
}
impl Request {
#[inline]
pub fn method(&self) -> &Method {
&self.method
}
#[inline]
pub fn url(&self) -> &str {
&self.url
}
#[inline]
pub fn headers(&self) -> &Headers {
&self.headers
}
#[inline]
pub fn query_params(&self) -> &QueryParams {
&self.query_params
}
#[inline]
pub fn body(&self) -> Option<&Bodies> {
self.body_fields.as_ref()
}
#[inline]
pub fn url_mut(&mut self) -> &mut String {
&mut self.url
}
#[inline]
pub fn headers_mut(&mut self) -> &mut Headers {
&mut self.headers
}
#[inline]
pub fn query_params_mut(&mut self) -> &mut QueryParams {
&mut self.query_params
}
#[inline]
pub fn body_mut(&mut self) -> &mut Option<Bodies> {
&mut self.body_fields
}
#[inline]
pub fn timeout(&self) -> Option<&Duration> {
self.timeout.as_ref()
}
#[inline]
pub fn timeout_mut(&mut self) -> &mut Option<Duration> {
&mut self.timeout
}
pub fn to_reqwest(&self, client: &reqwest::Client) -> ReqwestRequestBuilder {
let mut builder = client.request(self.method.clone(), &self.url);
if !self.query_params.is_empty() {
builder = builder.query(&self.query_params);
}
for (k, v) in &self.headers {
builder = builder.header(k, v);
}
if let Some(body) = &self.body_fields {
builder = builder.json(body);
}
if let Some(timeout_val) = self.timeout {
builder = builder.timeout(timeout_val);
}
builder
}
}
pub struct RequestBuilder {
request: Request,
}
impl RequestBuilder {
pub fn new(method: Method, base_url: &str) -> RequestBuilder {
RequestBuilder {
request: Request {
method,
url: base_url.to_string(),
headers: HashMap::new(),
query_params: HashMap::new(),
body_fields: None,
timeout: None,
},
}
}
pub fn header(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
self.request.headers_mut().insert(key.into(), value.into());
self
}
pub fn bearer_auth(&mut self, token: &str) -> &mut Self {
self.request
.headers_mut()
.insert("Authorization".to_string(), format!("Bearer {}", token));
self
}
pub fn query(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
self.request
.query_params_mut()
.insert(key.into(), value.into());
self
}
pub fn body_field(&mut self, key: impl Into<String>, value: impl Into<Value>) -> &mut Self {
self.request
.body_mut()
.get_or_insert_with(HashMap::new)
.insert(key.into(), value.into());
self
}
pub fn body_fields(&mut self, fields: Bodies) -> &mut Self {
self.request
.body_mut()
.get_or_insert_with(HashMap::new)
.extend(fields);
self
}
pub fn body_fields_map(&mut self, body_map: Bodies) -> &mut Self {
*self.request.body_mut() = Some(body_map);
self
}
pub fn without_body(&mut self) -> &mut Self {
*self.request.body_mut() = None;
self
}
pub fn timeout(&mut self, timeout: Duration) -> &mut Self {
*self.request.timeout_mut() = Some(timeout);
self
}
#[inline]
pub fn has_header(&self, key: &str) -> bool {
self.request.headers().contains_key(key)
}
#[inline]
pub fn has_query(&self, key: &str) -> bool {
self.request.query_params().contains_key(key)
}
#[inline]
pub fn has_body_field(&self, key: &str) -> bool {
match self.request.body() {
Some(body_fields) => body_fields.contains_key(key),
None => false,
}
}
#[inline]
pub fn has_any_headers(&self) -> bool {
!self.request.headers().is_empty()
}
#[inline]
pub fn has_any_query_params(&self) -> bool {
!self.request.query_params().is_empty()
}
#[inline]
pub fn has_any_body_fields(&self) -> bool {
match self.request.body() {
Some(body_fields) => !body_fields.is_empty(),
None => false,
}
}
pub fn build(self) -> Request {
self.request
}
}