use std::sync::Arc;
use asterisk_rs_core::event::{EventBus, EventSubscription};
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::config::AriConfig;
use crate::error::{AriError, Result};
use crate::event::AriEvent;
use crate::websocket::WsEventListener;
#[derive(Clone)]
pub struct AriClient {
http: reqwest::Client,
config: Arc<AriConfig>,
event_bus: EventBus<AriEvent>,
ws_listener: Arc<WsEventListener>,
}
impl std::fmt::Debug for AriClient {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AriClient")
.field("base_url", &self.config.base_url)
.finish_non_exhaustive()
}
}
impl AriClient {
pub async fn connect(config: AriConfig) -> Result<Self> {
let http = reqwest::Client::builder().build().map_err(AriError::Http)?;
let event_bus = EventBus::new(256);
let ws_listener = WsEventListener::spawn(
config.ws_url.to_string(),
event_bus.clone(),
config.reconnect_policy.clone(),
);
Ok(Self {
http,
config: Arc::new(config),
event_bus,
ws_listener: Arc::new(ws_listener),
})
}
pub async fn get<T: DeserializeOwned>(&self, path: &str) -> Result<T> {
let url = self.build_url(path)?;
let response = self
.http
.get(url)
.basic_auth(&self.config.username, Some(&self.config.password))
.send()
.await?;
Self::check_response(response)
.await?
.json()
.await
.map_err(AriError::Http)
}
pub async fn post<T: DeserializeOwned>(&self, path: &str, body: &impl Serialize) -> Result<T> {
let url = self.build_url(path)?;
let response = self
.http
.post(url)
.basic_auth(&self.config.username, Some(&self.config.password))
.json(body)
.send()
.await?;
Self::check_response(response)
.await?
.json()
.await
.map_err(AriError::Http)
}
pub async fn post_empty(&self, path: &str) -> Result<()> {
let url = self.build_url(path)?;
let response = self
.http
.post(url)
.basic_auth(&self.config.username, Some(&self.config.password))
.send()
.await?;
Self::check_response(response).await?;
Ok(())
}
pub async fn delete(&self, path: &str) -> Result<()> {
let url = self.build_url(path)?;
let response = self
.http
.delete(url)
.basic_auth(&self.config.username, Some(&self.config.password))
.send()
.await?;
Self::check_response(response).await?;
Ok(())
}
pub fn subscribe(&self) -> EventSubscription<AriEvent> {
self.event_bus.subscribe()
}
pub fn events(&self) -> &EventBus<AriEvent> {
&self.event_bus
}
pub fn disconnect(&self) {
self.ws_listener.shutdown();
}
fn build_url(&self, path: &str) -> Result<String> {
let base = self.config.base_url.as_str().trim_end_matches('/');
let path = path.trim_start_matches('/');
Ok(format!("{base}/{path}"))
}
async fn check_response(response: reqwest::Response) -> Result<reqwest::Response> {
let status = response.status();
if status.is_client_error() || status.is_server_error() {
let status_code = status.as_u16();
let message = response
.text()
.await
.unwrap_or_else(|_| "failed to read error body".to_owned());
return Err(AriError::Api {
status: status_code,
message,
});
}
Ok(response)
}
}