use crate::error::{Error, Result};
use serde::de::DeserializeOwned;
use std::sync::RwLock;
use std::time::Duration;
const USER_AGENT: &str = "(weathervane, https://gitlab.com/vintagetechie/weathervane)";
const REQUEST_TIMEOUT: Duration = Duration::from_secs(15);
const TCP_KEEPALIVE: Duration = Duration::from_secs(30);
const POOL_IDLE_TIMEOUT: Duration = Duration::from_secs(30);
static CLIENT: RwLock<Option<reqwest::Client>> = RwLock::new(None);
pub(crate) fn http_client() -> Result<reqwest::Client> {
if let Some(client) = CLIENT.read().unwrap().as_ref() {
return Ok(client.clone());
}
let mut slot = CLIENT.write().unwrap();
if let Some(client) = slot.as_ref() {
return Ok(client.clone());
}
let client = reqwest::Client::builder()
.user_agent(USER_AGENT)
.timeout(REQUEST_TIMEOUT)
.tcp_keepalive(Some(TCP_KEEPALIVE))
.pool_idle_timeout(Some(POOL_IDLE_TIMEOUT))
.pool_max_idle_per_host(0)
.build()
.map_err(|e| Error::HttpClient(e.to_string()))?;
*slot = Some(client.clone());
Ok(client)
}
pub fn reset_http_client() {
*CLIENT.write().unwrap() = None;
}
pub(crate) async fn get_json<T: DeserializeOwned>(url: &str, ctx: &str) -> Option<T> {
http_client()
.ok()?
.get(url)
.send()
.await
.map_err(|e| tracing::debug!("{ctx} request failed: {e}"))
.ok()?
.error_for_status()
.map_err(|e| tracing::debug!("{ctx} status error: {e}"))
.ok()?
.json::<T>()
.await
.map_err(|e| tracing::debug!("{ctx} parse failed: {e}"))
.ok()
}
pub(crate) async fn get_text(url: &str, ctx: &str) -> Option<String> {
http_client()
.ok()?
.get(url)
.send()
.await
.map_err(|e| tracing::debug!("{ctx} request failed: {e}"))
.ok()?
.text()
.await
.map_err(|e| tracing::debug!("{ctx} body failed: {e}"))
.ok()
}