use crate::error::{BotError, Result};
use reqwest::Client;
use std::sync::{Arc, RwLock};
use std::time::Duration;
use tracing::debug;
#[derive(Clone)]
pub struct HttpClient {
pub(crate) client: Client,
pub(crate) base_url: String,
pub(crate) is_sandbox: bool,
pub(crate) timeout: Duration,
pub(crate) last_trace_id: Arc<RwLock<Option<String>>>,
pub(crate) debug: bool,
pub(crate) union_app_id: Option<String>,
}
impl HttpClient {
pub fn new(timeout: u64, is_sandbox: bool) -> Result<Self> {
let client = Client::builder()
.timeout(Duration::from_secs(timeout))
.user_agent(format!("BotRS/{}", crate::VERSION))
.build()
.map_err(BotError::Http)?;
let base_url = if is_sandbox {
crate::SANDBOX_API_URL.to_string()
} else {
crate::DEFAULT_API_URL.to_string()
};
Ok(Self {
client,
base_url,
is_sandbox,
timeout: Duration::from_secs(timeout),
last_trace_id: Arc::new(RwLock::new(None)),
debug: false,
union_app_id: None,
})
}
pub(crate) fn clone_with_client(&self, client: Client, timeout: Duration, debug: bool) -> Self {
Self {
client,
base_url: self.base_url.clone(),
is_sandbox: self.is_sandbox,
timeout,
last_trace_id: Arc::clone(&self.last_trace_id),
debug,
union_app_id: self.union_app_id.clone(),
}
}
pub fn with_timeout(&self, timeout: Duration) -> Result<Self> {
let client = Client::builder()
.timeout(timeout)
.user_agent(format!("BotRS/{}", crate::VERSION))
.build()
.map_err(BotError::Http)?;
Ok(self.clone_with_client(client, timeout, self.debug))
}
pub fn with_debug(&self, debug: bool) -> Self {
Self {
debug,
..self.clone()
}
}
pub fn with_sandbox(&self, is_sandbox: bool) -> Result<Self> {
Self::new(self.timeout.as_secs(), is_sandbox).map(|client| {
if self.debug {
client.with_debug(true)
} else {
client
}
})
}
pub fn with_union_app_id(&self, app_id: impl Into<String>) -> Self {
Self {
union_app_id: Some(app_id.into()),
..self.clone()
}
}
pub fn base_url(&self) -> &str {
&self.base_url
}
pub fn is_sandbox(&self) -> bool {
self.is_sandbox
}
pub fn timeout(&self) -> Duration {
self.timeout
}
pub fn trace_id(&self) -> String {
self.last_trace_id
.read()
.ok()
.and_then(|trace_id| trace_id.clone())
.unwrap_or_default()
}
pub fn debug_enabled(&self) -> bool {
self.debug
}
pub fn union_app_id(&self) -> Option<&str> {
self.union_app_id.as_deref()
}
pub async fn close(&self) {
debug!("HTTP client closed");
}
}
impl std::fmt::Debug for HttpClient {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HttpClient")
.field("base_url", &self.base_url)
.field("is_sandbox", &self.is_sandbox)
.field("timeout", &self.timeout)
.field("debug", &self.debug)
.finish()
}
}