use crate::cookie::Cookie;
use crate::{Response, Result};
use http::Method;
use reqwest_cookie_store::CookieStoreMutex;
use serde_json::Value;
use std::sync::Arc;
pub struct Client {
base_url: Option<String>,
cookie_store: Arc<CookieStoreMutex>,
client: reqwest::Client,
}
pub fn new_client(base_url: impl Into<BaseUrl>) -> Result<Client> {
let base_url = base_url.into().into();
let cookie_store = Arc::new(CookieStoreMutex::default());
let client = reqwest::Client::builder().cookie_provider(cookie_store.clone()).build()?;
Ok(Client {
base_url,
cookie_store,
client,
})
}
impl Client {
pub async fn do_get(&self, url: &str) -> Result<Response> {
let url = self.compose_url(url);
let reqwest_res = self.client.get(&url).send().await?;
self.capture_response(Method::GET, url, reqwest_res).await
}
pub async fn do_post_json(&self, url: &str, body: Value) -> Result<Response> {
let url = self.compose_url(url);
let reqwest_res = self.client.post(&url).json(&body).send().await?;
self.capture_response(Method::POST, url, reqwest_res).await
}
#[allow(clippy::await_holding_lock)] async fn capture_response(
&self,
request_method: Method,
url: String,
reqwest_res: reqwest::Response,
) -> Result<Response> {
let cookie_store = self.cookie_store.lock().unwrap();
let client_cookies: Vec<Cookie> = cookie_store
.iter_any()
.map(|c| Cookie {
name: c.name().to_string(),
value: c.value().to_string(),
})
.collect();
Response::from_reqwest_response(request_method, url, client_cookies, reqwest_res).await
}
fn compose_url(&self, url: &str) -> String {
match &self.base_url {
Some(base_url) => format!("{base_url}{url}"),
None => url.to_string(),
}
}
}
pub struct BaseUrl(Option<String>);
impl From<&str> for BaseUrl {
fn from(val: &str) -> Self {
BaseUrl(Some(val.to_string()))
}
}
impl From<String> for BaseUrl {
fn from(val: String) -> Self {
BaseUrl(Some(val))
}
}
impl From<&String> for BaseUrl {
fn from(val: &String) -> Self {
BaseUrl(Some(val.to_string()))
}
}
impl From<BaseUrl> for Option<String> {
fn from(val: BaseUrl) -> Self {
val.0
}
}
impl From<Option<String>> for BaseUrl {
fn from(val: Option<String>) -> Self {
BaseUrl(val)
}
}