use asterisk_rs_core::auth::Credentials;
use asterisk_rs_core::config::ReconnectPolicy;
use url::Url;
use zeroize::Zeroizing;
use crate::error::{AriError, Result};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[non_exhaustive]
pub enum TransportMode {
#[default]
Http,
WebSocket,
}
#[derive(Clone)]
pub struct AriConfig {
pub(crate) base_url: Url,
pub(crate) credentials: Credentials,
pub(crate) app_name: String,
pub(crate) ws_url: Url,
pub(crate) reconnect_policy: ReconnectPolicy,
pub(crate) transport_mode: TransportMode,
}
impl std::fmt::Debug for AriConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AriConfig")
.field("base_url", &self.base_url)
.field("credentials", &self.credentials)
.field("app_name", &self.app_name)
.field("ws_url", &"[redacted]")
.field("reconnect_policy", &self.reconnect_policy)
.field("transport_mode", &self.transport_mode)
.finish()
}
}
impl AriConfig {
pub fn base_url(&self) -> &Url {
&self.base_url
}
pub fn credentials(&self) -> &Credentials {
&self.credentials
}
pub fn app_name(&self) -> &str {
&self.app_name
}
pub(crate) fn ws_url(&self) -> &Url {
&self.ws_url
}
pub fn reconnect_policy(&self) -> &ReconnectPolicy {
&self.reconnect_policy
}
pub fn transport_mode(&self) -> TransportMode {
self.transport_mode
}
}
#[must_use]
pub struct AriConfigBuilder {
host: String,
port: u16,
username: String,
password: Zeroizing<String>,
app_name: String,
secure: bool,
reconnect_policy: ReconnectPolicy,
transport_mode: TransportMode,
}
impl std::fmt::Debug for AriConfigBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AriConfigBuilder")
.field("host", &self.host)
.field("port", &self.port)
.field("username", &self.username)
.field("password", &"[redacted]")
.field("app_name", &self.app_name)
.field("secure", &self.secure)
.field("transport_mode", &self.transport_mode)
.finish()
}
}
impl AriConfigBuilder {
pub fn new(app_name: impl Into<String>) -> Self {
Self {
host: "127.0.0.1".to_owned(),
port: 8088,
username: String::new(),
password: Zeroizing::new(String::new()),
app_name: app_name.into(),
secure: false,
reconnect_policy: ReconnectPolicy::default(),
transport_mode: TransportMode::default(),
}
}
pub fn host(mut self, host: impl Into<String>) -> Self {
self.host = host.into();
self
}
pub fn port(mut self, port: u16) -> Self {
self.port = port;
self
}
pub fn username(mut self, username: impl Into<String>) -> Self {
self.username = username.into();
self
}
pub fn password(mut self, password: impl Into<String>) -> Self {
self.password = Zeroizing::new(password.into());
self
}
pub fn app_name(mut self, app_name: impl Into<String>) -> Self {
self.app_name = app_name.into();
self
}
pub fn secure(mut self, secure: bool) -> Self {
self.secure = secure;
self
}
pub fn reconnect(mut self, policy: ReconnectPolicy) -> Self {
self.reconnect_policy = policy;
self
}
pub fn transport(mut self, mode: TransportMode) -> Self {
self.transport_mode = mode;
self
}
pub fn build(self) -> Result<AriConfig> {
if self.app_name.is_empty() {
return Err(AriError::InvalidUrl(
"app_name must not be empty".to_owned(),
));
}
if self.username.is_empty() {
return Err(AriError::InvalidUrl(
"username must not be empty".to_owned(),
));
}
if self.password.is_empty() {
return Err(AriError::InvalidUrl(
"password must not be empty".to_owned(),
));
}
let http_scheme = if self.secure { "https" } else { "http" };
let ws_scheme = if self.secure { "wss" } else { "ws" };
let base_url_str = format!("{http_scheme}://{}:{}/ari", self.host, self.port);
let base_url =
Url::parse(&base_url_str).map_err(|e| AriError::InvalidUrl(e.to_string()))?;
let query = url::form_urlencoded::Serializer::new(String::new())
.append_pair("app", &self.app_name)
.append_pair("api_key", &format!("{}:{}", self.username, &*self.password))
.finish();
let ws_url_str = format!(
"{ws_scheme}://{}:{}/ari/events?{query}",
self.host, self.port
);
let ws_url = Url::parse(&ws_url_str).map_err(|e| AriError::InvalidUrl(e.to_string()))?;
let credentials = Credentials::new(self.username, &*self.password);
Ok(AriConfig {
base_url,
credentials,
app_name: self.app_name,
ws_url,
reconnect_policy: self.reconnect_policy,
transport_mode: self.transport_mode,
})
}
}