From aa20a8bb05bab1a216aeeb8ffcca08e9bf187aaa Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Fri, 13 Sep 2024 19:53:41 +0200 Subject: [PATCH 01/61] refactor(ari): extract error to separate crate --- arirs/src/error.rs | 25 +++++++++++++++++++++++++ arirs/src/lib.rs | 26 ++------------------------ 2 files changed, 27 insertions(+), 24 deletions(-) create mode 100644 arirs/src/error.rs diff --git a/arirs/src/error.rs b/arirs/src/error.rs new file mode 100644 index 0000000..db9a5ca --- /dev/null +++ b/arirs/src/error.rs @@ -0,0 +1,25 @@ +use thiserror::Error; +use tokio::task::JoinError; +use tokio_tungstenite::tungstenite; + +pub type Result = std::result::Result; + +#[derive(Debug, Error)] +pub enum AriError { + #[error("URL parsing error")] + UrlParseError(#[from] url::ParseError), + #[error("WebSocket error")] + TungsteniteError(#[from] tungstenite::Error), + #[error("HTTP Request error")] + ReqwestError(#[from] reqwest::Error), + #[error("Join Error")] + JoinError(#[from] JoinError), + #[error("Unknown error occurred: {0}")] + Unknown(String), +} + +impl From for AriError { + fn from(err: tungstenite::error::UrlError) -> Self { + AriError::TungsteniteError(err.into()) + } +} diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index 939858a..e8d4025 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -4,9 +4,6 @@ use channel::{ }; use device::DeviceStateChanged; use serde::{Deserialize, Serialize}; -use thiserror::Error; -use tokio::task::JoinError; -use tokio_tungstenite::tungstenite; pub mod bridge; pub mod channel; @@ -17,27 +14,8 @@ pub mod recording; pub mod rtp_statistics; pub mod variable; -pub type Result = std::result::Result; - -#[derive(Debug, Error)] -pub enum AriError { - #[error("URL parsing error")] - UrlParseError(#[from] url::ParseError), - #[error("WebSocket error")] - TungsteniteError(#[from] tungstenite::Error), - #[error("HTTP Request error")] - ReqwestError(#[from] reqwest::Error), - #[error("Join Error")] - JoinError(#[from] JoinError), - #[error("Unknown error occurred: {0}")] - Unknown(String), -} - -impl From for AriError { - fn from(err: tungstenite::error::UrlError) -> Self { - AriError::TungsteniteError(err.into()) - } -} +mod error; +pub use error::{AriError, Result}; #[derive(Serialize, Deserialize, Debug)] #[serde(tag = "type")] From 67aaaab80bf963929fd3d7d81ddce4a88c9fc22b Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Fri, 13 Sep 2024 19:55:29 +0200 Subject: [PATCH 02/61] refactor(ari): extract `Event` to separate crate --- arirs/src/event.rs | 20 ++++++++++++++++++++ arirs/src/lib.rs | 25 ++----------------------- 2 files changed, 22 insertions(+), 23 deletions(-) create mode 100644 arirs/src/event.rs diff --git a/arirs/src/event.rs b/arirs/src/event.rs new file mode 100644 index 0000000..43ecf2b --- /dev/null +++ b/arirs/src/event.rs @@ -0,0 +1,20 @@ +use serde::{Deserialize, Serialize}; + +use crate::{channel::*, device::DeviceStateChanged}; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(tag = "type")] +pub enum Event { + StasisStart(StasisStart), + StasisEnd(StasisEnd), + ChannelCreated(ChannelCreated), + ChannelDestroyed(ChannelDestroyed), + ChannelVarset(ChannelVarset), + ChannelHangupRequest(ChannelHangupRequest), + ChannelDialplan(ChannelDialplan), + ChannelStateChange(ChannelStateChange), + ChannelDtmfReceived(ChannelDtmfReceived), + DeviceStateChanged(DeviceStateChanged), + #[serde(other)] + Unknown, +} diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index e8d4025..94800c1 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -1,10 +1,3 @@ -use channel::{ - ChannelCreated, ChannelDestroyed, ChannelDialplan, ChannelDtmfReceived, ChannelHangupRequest, ChannelStateChange, ChannelVarset, - StasisEnd, StasisStart, -}; -use device::DeviceStateChanged; -use serde::{Deserialize, Serialize}; - pub mod bridge; pub mod channel; pub mod client; @@ -17,19 +10,5 @@ pub mod variable; mod error; pub use error::{AriError, Result}; -#[derive(Serialize, Deserialize, Debug)] -#[serde(tag = "type")] -pub enum Event { - StasisStart(StasisStart), - StasisEnd(StasisEnd), - ChannelCreated(ChannelCreated), - ChannelDestroyed(ChannelDestroyed), - ChannelVarset(ChannelVarset), - ChannelHangupRequest(ChannelHangupRequest), - ChannelDialplan(ChannelDialplan), - ChannelStateChange(ChannelStateChange), - ChannelDtmfReceived(ChannelDtmfReceived), - DeviceStateChanged(DeviceStateChanged), - #[serde(other)] - Unknown, -} +mod event; +pub use event::Event; From 1be37d6cbe83580132c053b4a8200ab4e5dc058b Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Fri, 13 Sep 2024 20:02:33 +0200 Subject: [PATCH 03/61] feat(ari): temporarily remove `#[instrument]` annotations --- arirs/src/bridge.rs | 15 --------------- arirs/src/channel.rs | 32 +------------------------------- arirs/src/client.rs | 5 +---- arirs/src/playback.rs | 4 ---- arirs/src/recording.rs | 12 ------------ 5 files changed, 2 insertions(+), 66 deletions(-) diff --git a/arirs/src/bridge.rs b/arirs/src/bridge.rs index eee2d90..cd006f4 100644 --- a/arirs/src/bridge.rs +++ b/arirs/src/bridge.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use tracing::instrument; use crate::{client::Client, playback::Playback, recording::LiveRecording, Result}; @@ -9,72 +8,58 @@ pub struct Bridge { } impl Bridge { - #[instrument(level = "debug")] pub async fn destroy(&self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn add_channel(&self, _client: &Client, _channel_id: &str) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn remove_channel(&self, _client: &Client, _channel_id: &str) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn set_channel_as_video_source(&self, _client: &Client, _channel_id: &str, _video_source_id: &str) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn unset_video_source(&self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn start_moh(&self, _client: &Client, _moh_class: &str) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn stop_moh(&self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn play_media(&self, _client: &Client, _playback: &Playback) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn stop_media(&self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn start_recording(&self, _client: &Client, _recording: &LiveRecording) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn list_bridges(_client: &Client) -> Result> { unimplemented!() } - #[instrument(level = "debug")] pub async fn create_bridge(_client: &Client, _bridge_id: &str) -> Result { unimplemented!() } - #[instrument(level = "debug")] pub async fn create_bridge_with_id(_client: &Client, _bridge_id: &str, _bridge: &Bridge) -> Result { unimplemented!() } - #[instrument(level = "debug")] pub async fn get_bridge(_client: &Client, _bridge_id: &str) -> Result { unimplemented!() } diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index 0e430a1..dd4c787 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -4,7 +4,7 @@ use chrono::{DateTime, Duration}; use derive_more::Display; use serde::{Deserialize, Serialize}; use serde_json::json; -use tracing::{event, instrument, Level}; +use tracing::{event, Level}; use url::Url; use crate::{client::Client, playback::Playback, recording::LiveRecording, rtp_statistics::RtpStatistics, variable::Variable, Result}; @@ -212,7 +212,6 @@ pub struct Dialplan { } impl Channel { - #[instrument(level = "debug")] pub async fn hangup(self, client: &Client, reason: Reason) -> Result<()> { let mut url = client.url.join(&format!("channels/{}", self.id))?; let mut url = url.query_pairs_mut(); @@ -229,19 +228,16 @@ impl Channel { Ok(()) } - #[instrument(level = "debug")] pub fn continue_in_dialplan(self, _client: &Client) -> Result<()> { unimplemented!() } /// Transfer the channel to another ARI application. /// Same as `move` in Asterisk - #[instrument(level = "debug")] pub fn transfer(self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn answer(&self, client: &Client) -> Result<()> { let url = client .url @@ -256,7 +252,6 @@ impl Channel { Ok(()) } - #[instrument(level = "debug")] pub async fn start_ringing(&self, client: &Client) -> Result<()> { let url = client .url @@ -271,7 +266,6 @@ impl Channel { Ok(()) } - #[instrument(level = "debug")] pub async fn stop_ringing(&self, client: &Client) -> Result<()> { let url = client .url @@ -286,7 +280,6 @@ impl Channel { Ok(()) } - #[instrument(level = "debug")] pub async fn send_dtmf( &self, client: &Client, @@ -319,7 +312,6 @@ impl Channel { Ok(()) } - #[instrument(level = "debug")] pub async fn mute(&self, client: &Client, direction: Direction) -> Result<()> { let url = client .url @@ -335,7 +327,6 @@ impl Channel { Ok(()) } - #[instrument(level = "debug")] pub async fn unmute(&self, client: &Client, direction: Direction) -> Result<()> { let url = client .url @@ -351,7 +342,6 @@ impl Channel { Ok(()) } - #[instrument(level = "debug")] pub async fn hold(&self, client: &Client) -> Result<()> { let url = client .url @@ -366,7 +356,6 @@ impl Channel { Ok(()) } - #[instrument(level = "debug")] pub async fn unhold(&self, client: &Client) -> Result<()> { let url = client .url @@ -381,27 +370,22 @@ impl Channel { Ok(()) } - #[instrument(level = "debug")] pub fn start_moh(&self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub fn stop_moh(&self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub fn start_silence(&self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub fn stop_silence(&self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn play_media( &self, client: &Client, @@ -449,7 +433,6 @@ impl Channel { Ok(playback) } - #[instrument(level = "debug")] pub async fn play_media_with_id( &self, client: &Client, @@ -496,7 +479,6 @@ impl Channel { } #[allow(clippy::too_many_arguments)] - #[instrument(level = "debug")] pub async fn record( &self, client: &Client, @@ -543,17 +525,14 @@ impl Channel { Ok(recording) } - #[instrument(level = "debug")] pub fn get_variable(&self, _client: &Client) -> Result { unimplemented!() } - #[instrument(level = "debug")] pub fn set_variable(&self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn dial(&self, client: &Client, caller_id: Option<&str>, timeout: Option) -> Result<()> { let mut url = client.url.join(&format!("channels/{}/dial", self.id))?; let mut url = url.query_pairs_mut(); @@ -573,12 +552,10 @@ impl Channel { Ok(()) } - #[instrument(level = "debug")] pub fn get_rtp_statistics(&self, _client: &Client) -> Result { unimplemented!() } - #[instrument(level = "debug")] pub async fn list(client: &Client) -> Result> { let url: Url = client .url @@ -594,7 +571,6 @@ impl Channel { } #[allow(clippy::too_many_arguments)] - #[instrument(level = "debug")] pub async fn create( client: &Client, endpoint: &str, @@ -649,7 +625,6 @@ impl Channel { Ok(channel) } - #[instrument(level = "debug")] pub async fn get(client: &Client, channel_id: &str) -> Result { let url = client .url @@ -665,7 +640,6 @@ impl Channel { } #[allow(clippy::too_many_arguments)] - #[instrument(level = "debug")] pub async fn originate<'a>( client: &Client, endpoint: &str, @@ -754,7 +728,6 @@ impl Channel { } #[allow(clippy::too_many_arguments)] - #[instrument(level = "debug")] pub async fn originate_with_id<'a>( client: &Client, channel_id: &str, @@ -838,17 +811,14 @@ impl Channel { Ok(channel) } - #[instrument(level = "debug")] pub fn snoop(&self, _channel_id: &str) -> Result { unimplemented!() } - #[instrument(level = "debug")] pub fn snoop_with_id(&self, _channel_id: &str) -> Result { unimplemented!() } - #[instrument(level = "debug")] pub fn start_external_media(&self, _channel_id: &str) -> Result { unimplemented!() } diff --git a/arirs/src/client.rs b/arirs/src/client.rs index 6ad5bd2..66ba478 100644 --- a/arirs/src/client.rs +++ b/arirs/src/client.rs @@ -4,7 +4,7 @@ use futures_util::{SinkExt, StreamExt}; use rand::Rng; use tokio::{sync::mpsc::Sender, time::interval}; use tokio_tungstenite::{connect_async, tungstenite}; -use tracing::{event, instrument, Level}; +use tracing::{event, Level}; use url::Url; use crate::{Event, Result}; @@ -35,7 +35,6 @@ impl ClientBuilder { self } - #[instrument(level = "debug")] pub fn build(self) -> Result { let mut ws_url = self.0.url.join("events")?; @@ -82,7 +81,6 @@ impl Client { ClientBuilder(Client::default()) } - #[instrument(level = "debug")] pub fn handle_message(&self, message: Vec) { let data = String::from_utf8(message).unwrap(); @@ -107,7 +105,6 @@ impl Client { } } - #[instrument(level = "debug")] pub async fn run(&self) -> Result<()> { event!(Level::INFO, "Connecting to Asterisk"); diff --git a/arirs/src/playback.rs b/arirs/src/playback.rs index 9c7e30a..ccf607f 100644 --- a/arirs/src/playback.rs +++ b/arirs/src/playback.rs @@ -1,6 +1,5 @@ use derive_more::Display; use serde::{Deserialize, Serialize}; -use tracing::instrument; use crate::{client::Client, Result}; @@ -11,17 +10,14 @@ pub struct Playback { } impl Playback { - #[instrument(level = "debug")] pub async fn get_playback(_client: &Client, _playback_id: &str) -> Result { unimplemented!() } - #[instrument(level = "debug")] pub async fn control(&self, _client: &Client, _operation: Operation) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn stop(&self, _client: &Client) -> Result<()> { unimplemented!() } diff --git a/arirs/src/recording.rs b/arirs/src/recording.rs index a00f67a..e4d2e55 100644 --- a/arirs/src/recording.rs +++ b/arirs/src/recording.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use tracing::instrument; use crate::{client::Client, Result}; @@ -11,38 +10,31 @@ pub struct LiveRecording { } impl LiveRecording { - #[instrument(level = "debug")] pub async fn get(_recording_name: &str) -> Result { unimplemented!() } - #[instrument(level = "debug")] pub async fn discard(&self, _client: &Client) -> Result<()> { unimplemented!() } // TODO: explore if it's possible to return a StoredRecording - #[instrument(level = "debug")] pub async fn stop(&self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn pause(&self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn resume(&self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn mute(&self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn unmute(&self, _client: &Client) -> Result<()> { unimplemented!() } @@ -56,22 +48,18 @@ pub struct StoredRecording { } impl StoredRecording { - #[instrument(level = "debug")] pub async fn list(_client: &Client) -> Result> { unimplemented!() } - #[instrument(level = "debug")] pub async fn get(_recording_name: &str) -> Result { unimplemented!() } - #[instrument(level = "debug")] pub async fn delete(&self, _client: &Client) -> Result<()> { unimplemented!() } - #[instrument(level = "debug")] pub async fn download(&self, _client: &Client) -> Result<&[u8]> { unimplemented!() } From 00dcd0aa5fe47afef97a8136739bb8f5e193c2f4 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Fri, 13 Sep 2024 20:07:40 +0200 Subject: [PATCH 04/61] reactor!(ari): use top module star imports --- arirs/examples/dtmf.rs | 2 +- arirs/examples/list-channels.rs | 2 +- arirs/examples/originate.rs | 6 +----- arirs/examples/playback.rs | 2 +- arirs/src/bridge.rs | 2 +- arirs/src/channel.rs | 2 +- arirs/src/client.rs | 2 +- arirs/src/event.rs | 2 +- arirs/src/lib.rs | 31 +++++++++++++++++++++++-------- arirs/src/playback.rs | 2 +- arirs/src/recording.rs | 2 +- 11 files changed, 33 insertions(+), 22 deletions(-) diff --git a/arirs/examples/dtmf.rs b/arirs/examples/dtmf.rs index 33e197e..a5851c0 100644 --- a/arirs/examples/dtmf.rs +++ b/arirs/examples/dtmf.rs @@ -1,6 +1,6 @@ use std::sync::{Arc, Mutex}; -use arirs::{client::Client, Event}; +use arirs::{Client, Event}; use tracing::{debug, error}; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; diff --git a/arirs/examples/list-channels.rs b/arirs/examples/list-channels.rs index 0a67fdd..e978587 100644 --- a/arirs/examples/list-channels.rs +++ b/arirs/examples/list-channels.rs @@ -1,4 +1,4 @@ -use arirs::{channel::Channel, client::Client, Result}; +use arirs::{Channel, Client, Result}; use tracing::debug; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; diff --git a/arirs/examples/originate.rs b/arirs/examples/originate.rs index b8cb799..31c44db 100644 --- a/arirs/examples/originate.rs +++ b/arirs/examples/originate.rs @@ -1,10 +1,6 @@ use std::collections::HashMap; -use arirs::{ - channel::{Channel, OriginateParams}, - client::Client, - Result, -}; +use arirs::{Channel, Client, OriginateParams, Result}; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; const APP_NAME: &str = "ari"; diff --git a/arirs/examples/playback.rs b/arirs/examples/playback.rs index a13d9d1..8ee9389 100644 --- a/arirs/examples/playback.rs +++ b/arirs/examples/playback.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use arirs::{client::Client, Event}; +use arirs::{Client, Event}; use tracing::{error, level_filters::LevelFilter}; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; diff --git a/arirs/src/bridge.rs b/arirs/src/bridge.rs index cd006f4..e15744e 100644 --- a/arirs/src/bridge.rs +++ b/arirs/src/bridge.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::{client::Client, playback::Playback, recording::LiveRecording, Result}; +use crate::*; #[derive(Debug, Serialize, Deserialize)] pub struct Bridge { diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index dd4c787..628bf73 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -7,7 +7,7 @@ use serde_json::json; use tracing::{event, Level}; use url::Url; -use crate::{client::Client, playback::Playback, recording::LiveRecording, rtp_statistics::RtpStatistics, variable::Variable, Result}; +use crate::*; #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "snake_case")] diff --git a/arirs/src/client.rs b/arirs/src/client.rs index 66ba478..fbae6d4 100644 --- a/arirs/src/client.rs +++ b/arirs/src/client.rs @@ -7,7 +7,7 @@ use tokio_tungstenite::{connect_async, tungstenite}; use tracing::{event, Level}; use url::Url; -use crate::{Event, Result}; +use crate::*; impl ClientBuilder { pub fn url(mut self, url: Url) -> Self { diff --git a/arirs/src/event.rs b/arirs/src/event.rs index 43ecf2b..c65341e 100644 --- a/arirs/src/event.rs +++ b/arirs/src/event.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::{channel::*, device::DeviceStateChanged}; +use crate::*; #[derive(Serialize, Deserialize, Debug)] #[serde(tag = "type")] diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index 94800c1..e485c16 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -1,11 +1,26 @@ -pub mod bridge; -pub mod channel; -pub mod client; -pub mod device; -pub mod playback; -pub mod recording; -pub mod rtp_statistics; -pub mod variable; +mod bridge; +pub use bridge::Bridge; + +mod channel; +pub use channel::*; + +mod client; +pub use client::{Client, ClientBuilder}; + +mod device; +pub use device::{DeviceState, DeviceStateChanged}; + +mod playback; +pub use playback::{Operation, Playback}; + +mod recording; +pub use recording::{LiveRecording, StoredRecording}; + +mod rtp_statistics; +pub use rtp_statistics::RtpStatistics; + +mod variable; +pub use variable::Variable; mod error; pub use error::{AriError, Result}; diff --git a/arirs/src/playback.rs b/arirs/src/playback.rs index ccf607f..a4a8bcd 100644 --- a/arirs/src/playback.rs +++ b/arirs/src/playback.rs @@ -1,7 +1,7 @@ use derive_more::Display; use serde::{Deserialize, Serialize}; -use crate::{client::Client, Result}; +use crate::*; #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "snake_case")] diff --git a/arirs/src/recording.rs b/arirs/src/recording.rs index e4d2e55..892c4a1 100644 --- a/arirs/src/recording.rs +++ b/arirs/src/recording.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::{client::Client, Result}; +use crate::*; #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "snake_case")] From 991b0ee707a7ef8c203c4bf1709c8b9e6d360c5b Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Fri, 13 Sep 2024 20:09:19 +0200 Subject: [PATCH 05/61] reactor(ari): use idiomatic code ordering in `client.rs` --- arirs/src/client.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/arirs/src/client.rs b/arirs/src/client.rs index fbae6d4..dd6cb1d 100644 --- a/arirs/src/client.rs +++ b/arirs/src/client.rs @@ -9,6 +9,9 @@ use url::Url; use crate::*; +#[derive(Debug, Default)] +pub struct ClientBuilder(Client); + impl ClientBuilder { pub fn url(mut self, url: Url) -> Self { self.0.url = url; @@ -72,8 +75,15 @@ impl ClientBuilder { } } -#[derive(Debug, Default)] -pub struct ClientBuilder(Client); +#[derive(Debug)] +pub struct Client { + pub url: Url, + pub ws_url: Url, + pub username: String, + pub password: String, + pub app_name: String, + pub ws_channel: Option>, +} impl Client { #[allow(clippy::new_ret_no_self)] @@ -194,13 +204,3 @@ impl Default for Client { } } } - -#[derive(Debug)] -pub struct Client { - pub url: Url, - pub ws_url: Url, - pub username: String, - pub password: String, - pub app_name: String, - pub ws_channel: Option>, -} From 7dd2e9bd68d25f1443f138ec73908b91b0c5aa42 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Fri, 13 Sep 2024 20:11:27 +0200 Subject: [PATCH 06/61] feat(ari): remove tracing events from client builder --- arirs/src/client.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arirs/src/client.rs b/arirs/src/client.rs index dd6cb1d..3c2e2f0 100644 --- a/arirs/src/client.rs +++ b/arirs/src/client.rs @@ -45,7 +45,6 @@ impl ClientBuilder { "http" => "ws", "https" => "wss", _ => { - event!(Level::ERROR, "Unsupported scheme '{}'", ws_url.scheme()); return Err(tungstenite::error::UrlError::UnsupportedUrlScheme.into()); } }; @@ -60,10 +59,6 @@ impl ClientBuilder { .append_pair("api_key", &format!("{}:{}", self.0.username, self.0.password)) .append_pair("subscribeAll", "true"); - event!(Level::TRACE, "Using REST API server with URL '{}'", self.0.url); - - event!(Level::TRACE, "Using WebSocket server with URL '{}'", ws_url); - Ok(Client { url: self.0.url, ws_url, From c1cee132d13729b51570b324c074e966335b63b4 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Fri, 13 Sep 2024 20:36:20 +0200 Subject: [PATCH 07/61] refactor!(ari): encapsulate `Client` fields --- Cargo.lock | 12 ++++ arirs/Cargo.toml | 1 + arirs/examples/dtmf.rs | 8 +-- arirs/examples/list-channels.rs | 7 +-- arirs/examples/originate.rs | 7 +-- arirs/examples/playback.rs | 21 ++----- arirs/src/channel.rs | 38 ++++++------- arirs/src/client.rs | 97 ++++++++++++--------------------- arirs/src/lib.rs | 2 +- 9 files changed, 74 insertions(+), 119 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 16b58ae..b265e3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,6 +54,7 @@ name = "arirs" version = "0.1.0" dependencies = [ "chrono", + "derive-getters", "derive_more", "futures-channel", "futures-util", @@ -206,6 +207,17 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "derive-getters" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74ef43543e701c01ad77d3a5922755c6a1d71b22d942cb8042be4994b380caff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_more" version = "1.0.0" diff --git a/arirs/Cargo.toml b/arirs/Cargo.toml index d851e06..aba90f0 100644 --- a/arirs/Cargo.toml +++ b/arirs/Cargo.toml @@ -7,6 +7,7 @@ version = "0.1.0" [dependencies] chrono = { version = "0.4.38", features = ["serde"] } +derive-getters = "0.5" derive_more = { version = "1.0.0", features = ["display"] } futures-channel = "0.3.30" futures-util = "0.3.30" diff --git a/arirs/examples/dtmf.rs b/arirs/examples/dtmf.rs index a5851c0..4c24135 100644 --- a/arirs/examples/dtmf.rs +++ b/arirs/examples/dtmf.rs @@ -14,13 +14,7 @@ async fn main() -> Result<(), Box> { let (tx, mut rx) = tokio::sync::mpsc::channel(1024); let dtmf_buffer = Arc::new(Mutex::new(String::new())); - let client = Client::new() - .url(url::Url::parse("http://localhost:8088/ari/")?) - .username("asterisk") - .password("asterisk") - .app_name("ari") - .handler(tx) - .build()?; + let client = Client::new("http://localhost:8088/", "asterisk", "asterisk", "ari", Some(tx))?; tokio::spawn(async move { if let Err(e) = client.run().await { diff --git a/arirs/examples/list-channels.rs b/arirs/examples/list-channels.rs index e978587..ba90f1e 100644 --- a/arirs/examples/list-channels.rs +++ b/arirs/examples/list-channels.rs @@ -9,12 +9,7 @@ async fn main() -> Result<()> { .with(EnvFilter::from_default_env()) .init(); - let client = Client::new() - .url(url::Url::parse("http://localhost:8088/ari")?) - .username("asterisk") - .password("asterisk") - .app_name("ari") - .build()?; + let client = Client::default(); for channel in Channel::list(&client).await? { debug!("Channel ID: {}", channel.id); diff --git a/arirs/examples/originate.rs b/arirs/examples/originate.rs index 31c44db..0bde198 100644 --- a/arirs/examples/originate.rs +++ b/arirs/examples/originate.rs @@ -12,12 +12,7 @@ async fn main() -> Result<()> { .with(EnvFilter::from_default_env()) .init(); - let client = Client::new() - .url(url::Url::parse("http://localhost:8088/ari")?) - .username("asterisk") - .password("asterisk") - .app_name(APP_NAME) - .build()?; + let client = Client::default(); Channel::originate( &client, diff --git a/arirs/examples/playback.rs b/arirs/examples/playback.rs index 8ee9389..4f29267 100644 --- a/arirs/examples/playback.rs +++ b/arirs/examples/playback.rs @@ -4,23 +4,13 @@ use arirs::{Client, Event}; use tracing::{error, level_filters::LevelFilter}; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; -const APP_NAME: &str = "ari"; - #[tokio::main] async fn main() -> Result<(), Box> { tracing_subscriber::registry().with(fmt::layer()).with(LevelFilter::TRACE).init(); let (tx, mut rx) = tokio::sync::mpsc::channel(1024); - let client = Arc::new( - Client::new() - .url(url::Url::parse("http://localhost:8088/ari")?) - .username("asterisk") - .password("asterisk") - .app_name(APP_NAME) - .handler(tx) - .build()?, - ); + let client = Arc::new(Client::new("http://localhost:8088/", "asterisk", "asterisk", "ari", Some(tx))?); let client_clone = client.clone(); tokio::spawn(async move { @@ -30,12 +20,9 @@ async fn main() -> Result<(), Box> { }); while let Some(event) = rx.recv().await { - match event { - Event::StasisStart(e) => { - let channel = e.channel; - channel.play_media(&client, "sound:hello", Some("en"), None, None, None).await?; - } - _ => {} + if let Event::StasisStart(e) = event { + let channel = e.channel; + channel.play_media(&client, "sound:hello", Some("en"), None, None, None).await?; } } diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index 628bf73..3676703 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -213,7 +213,7 @@ pub struct Dialplan { impl Channel { pub async fn hangup(self, client: &Client, reason: Reason) -> Result<()> { - let mut url = client.url.join(&format!("channels/{}", self.id))?; + let mut url = client.url().join(&format!("channels/{}", self.id))?; let mut url = url.query_pairs_mut(); client.add_api_key(&mut url); @@ -240,7 +240,7 @@ impl Channel { pub async fn answer(&self, client: &Client) -> Result<()> { let url = client - .url + .url() .join(&format!("channels/{}/answer", self.id))? .query_pairs_mut() .append_pair("api_key", &client.get_api_key()) @@ -254,7 +254,7 @@ impl Channel { pub async fn start_ringing(&self, client: &Client) -> Result<()> { let url = client - .url + .url() .join(&format!("channels/{}/ring", self.id))? .query_pairs_mut() .append_pair("api_key", &client.get_api_key()) @@ -268,10 +268,10 @@ impl Channel { pub async fn stop_ringing(&self, client: &Client) -> Result<()> { let url = client - .url + .url() .join(&format!("channels/{}/ring", self.id))? .query_pairs_mut() - .append_pair("api_key", &format!("{}:{}", client.username, client.password)) + .append_pair("api_key", &client.get_api_key()) .finish() .to_owned(); @@ -289,7 +289,7 @@ impl Channel { duration: Option, after: Option, ) -> Result<()> { - let mut url = client.url.join(&format!("channels/{}/dtmf", self.id))?; + let mut url = client.url().join(&format!("channels/{}/dtmf", self.id))?; let mut url = url.query_pairs_mut(); client.add_api_key(&mut url); @@ -314,7 +314,7 @@ impl Channel { pub async fn mute(&self, client: &Client, direction: Direction) -> Result<()> { let url = client - .url + .url() .join(&format!("channels/{}/mute", self.id))? .query_pairs_mut() .append_pair("api_key", &client.get_api_key()) @@ -329,7 +329,7 @@ impl Channel { pub async fn unmute(&self, client: &Client, direction: Direction) -> Result<()> { let url = client - .url + .url() .join(&format!("channels/{}/mute", self.id))? .query_pairs_mut() .append_pair("api_key", &client.get_api_key()) @@ -344,7 +344,7 @@ impl Channel { pub async fn hold(&self, client: &Client) -> Result<()> { let url = client - .url + .url() .join(&format!("channels/{}/hold", self.id))? .query_pairs_mut() .append_pair("api_key", &client.get_api_key()) @@ -358,7 +358,7 @@ impl Channel { pub async fn unhold(&self, client: &Client) -> Result<()> { let url = client - .url + .url() .join(&format!("channels/{}/hold", self.id))? .query_pairs_mut() .append_pair("api_key", &client.get_api_key()) @@ -395,7 +395,7 @@ impl Channel { skip_ms: Option, playback_id: Option<&str>, ) -> Result { - let mut url = client.url.join(&format!("channels/{}/play", self.id))?; + let mut url = client.url().join(&format!("channels/{}/play", self.id))?; let mut url = url.query_pairs_mut(); client.add_api_key(&mut url); url.append_pair("media", media); @@ -442,7 +442,7 @@ impl Channel { offset_ms: Option, skip_ms: Option, ) -> Result { - let mut url = client.url.join(&format!("channels/{}/play/{}/media", self.id, playback_id))?; + let mut url = client.url().join(&format!("channels/{}/play/{}/media", self.id, playback_id))?; let mut url = url.query_pairs_mut(); client.add_api_key(&mut url); @@ -490,7 +490,7 @@ impl Channel { beep: bool, terminate_on: RecordingTermination, ) -> Result { - let mut url = client.url.join(&format!("channels/{}/record", self.id))?; + let mut url = client.url().join(&format!("channels/{}/record", self.id))?; let mut url = url.query_pairs_mut(); client.add_api_key(&mut url); @@ -534,7 +534,7 @@ impl Channel { } pub async fn dial(&self, client: &Client, caller_id: Option<&str>, timeout: Option) -> Result<()> { - let mut url = client.url.join(&format!("channels/{}/dial", self.id))?; + let mut url = client.url().join(&format!("channels/{}/dial", self.id))?; let mut url = url.query_pairs_mut(); client.add_api_key(&mut url); @@ -558,7 +558,7 @@ impl Channel { pub async fn list(client: &Client) -> Result> { let url: Url = client - .url + .url() .join("channels")? .query_pairs_mut() .append_pair("api_key", &client.get_api_key()) @@ -582,7 +582,7 @@ impl Channel { formats: Vec<&str>, variables: HashMap<&str, &str>, ) -> Result { - let mut url = client.url.join("channels")?; + let mut url = client.url().join("channels")?; let mut url = url.query_pairs_mut(); client.add_api_key(&mut url); url.append_pair("endpoint", endpoint).append_pair("app", app); @@ -627,7 +627,7 @@ impl Channel { pub async fn get(client: &Client, channel_id: &str) -> Result { let url = client - .url + .url() .join(&format!("channels/{}", channel_id))? .query_pairs_mut() .append_pair("api_key", &client.get_api_key()) @@ -652,7 +652,7 @@ impl Channel { formats: Vec<&str>, variables: HashMap<&str, &str>, ) -> Result { - let mut url = client.url.join("channels")?; + let mut url = client.url().join("channels")?; let mut url = url.query_pairs_mut(); client.add_api_key(&mut url); @@ -740,7 +740,7 @@ impl Channel { formats: Vec<&str>, variables: HashMap<&str, &str>, ) -> Result { - let mut url = client.url.join(&format!("channels/{}", channel_id))?; + let mut url = client.url().join(&format!("channels/{}", channel_id))?; let mut url = url.query_pairs_mut(); client.add_api_key(&mut url); diff --git a/arirs/src/client.rs b/arirs/src/client.rs index 3c2e2f0..ba96eaf 100644 --- a/arirs/src/client.rs +++ b/arirs/src/client.rs @@ -1,5 +1,6 @@ use std::time::Duration; +use derive_getters::Getters; use futures_util::{SinkExt, StreamExt}; use rand::Rng; use tokio::{sync::mpsc::Sender, time::interval}; @@ -9,82 +10,53 @@ use url::Url; use crate::*; -#[derive(Debug, Default)] -pub struct ClientBuilder(Client); - -impl ClientBuilder { - pub fn url(mut self, url: Url) -> Self { - self.0.url = url; - self - } - - pub fn username(mut self, username: &str) -> Self { - self.0.username = username.to_string(); - self - } - - pub fn password(mut self, password: &str) -> Self { - self.0.password = password.to_string(); - self - } - - pub fn app_name(mut self, app_name: &str) -> Self { - self.0.app_name = app_name.to_string(); - self - } - - pub fn handler(mut self, tx: Sender) -> Self { - self.0.ws_channel = Some(tx); - self - } - - pub fn build(self) -> Result { - let mut ws_url = self.0.url.join("events")?; +#[derive(Debug, Getters)] +pub struct Client { + url: Url, + ws_url: Url, + username: String, + password: String, + ws_channel: Option>, +} +impl Client { + /// Create a new client + /// + /// `url` should end in `/`, `ari/` will be appended to it. + pub fn new( + url: impl AsRef, + app_name: impl AsRef, + username: impl Into, + password: impl Into, + event_sender: Option>, + ) -> Result { + let url = Url::parse(url.as_ref())?.join("ari")?; + + let mut ws_url = url.join("events")?; let scheme = match ws_url.scheme() { "http" => "ws", "https" => "wss", - _ => { - return Err(tungstenite::error::UrlError::UnsupportedUrlScheme.into()); - } + _ => Err(tungstenite::error::UrlError::UnsupportedUrlScheme)?, }; - if ws_url.set_scheme(scheme).is_err() { - return Err(tungstenite::error::UrlError::UnsupportedUrlScheme.into()); - } + let username = username.into(); + let password = password.into(); + ws_url.set_scheme(scheme).expect("invalid url scheme"); ws_url .query_pairs_mut() - .append_pair("app", &self.0.app_name) - .append_pair("api_key", &format!("{}:{}", self.0.username, self.0.password)) + .append_pair("app", app_name.as_ref()) + .append_pair("api_key", &format!("{}:{}", username, password)) .append_pair("subscribeAll", "true"); - Ok(Client { - url: self.0.url, + Ok(Self { + url, ws_url, - username: self.0.username, - password: self.0.password, - app_name: self.0.app_name, - ws_channel: self.0.ws_channel, + username, + password, + ws_channel: event_sender, }) } -} - -#[derive(Debug)] -pub struct Client { - pub url: Url, - pub ws_url: Url, - pub username: String, - pub password: String, - pub app_name: String, - pub ws_channel: Option>, -} - -impl Client { - #[allow(clippy::new_ret_no_self)] - pub fn new() -> ClientBuilder { - ClientBuilder(Client::default()) - } pub fn handle_message(&self, message: Vec) { let data = String::from_utf8(message).unwrap(); @@ -194,7 +166,6 @@ impl Default for Client { }, username: "asterisk".to_string(), password: "asterisk".to_string(), - app_name: "ari".to_string(), ws_channel: None, } } diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index e485c16..323780b 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -5,7 +5,7 @@ mod channel; pub use channel::*; mod client; -pub use client::{Client, ClientBuilder}; +pub use client::Client; mod device; pub use device::{DeviceState, DeviceStateChanged}; From 2fb8f3b40bd8841fd8cc326386bfe7f7a0cd10c5 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Fri, 13 Sep 2024 20:40:42 +0200 Subject: [PATCH 08/61] feat(ari): use unbounded event channels --- arirs/examples/dtmf.rs | 2 +- arirs/examples/playback.rs | 2 +- arirs/src/client.rs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arirs/examples/dtmf.rs b/arirs/examples/dtmf.rs index 4c24135..d001689 100644 --- a/arirs/examples/dtmf.rs +++ b/arirs/examples/dtmf.rs @@ -11,7 +11,7 @@ async fn main() -> Result<(), Box> { .with(EnvFilter::from_default_env()) .init(); - let (tx, mut rx) = tokio::sync::mpsc::channel(1024); + let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel(); let dtmf_buffer = Arc::new(Mutex::new(String::new())); let client = Client::new("http://localhost:8088/", "asterisk", "asterisk", "ari", Some(tx))?; diff --git a/arirs/examples/playback.rs b/arirs/examples/playback.rs index 4f29267..48bfe6a 100644 --- a/arirs/examples/playback.rs +++ b/arirs/examples/playback.rs @@ -8,7 +8,7 @@ use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; async fn main() -> Result<(), Box> { tracing_subscriber::registry().with(fmt::layer()).with(LevelFilter::TRACE).init(); - let (tx, mut rx) = tokio::sync::mpsc::channel(1024); + let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel(); let client = Arc::new(Client::new("http://localhost:8088/", "asterisk", "asterisk", "ari", Some(tx))?); diff --git a/arirs/src/client.rs b/arirs/src/client.rs index ba96eaf..050be01 100644 --- a/arirs/src/client.rs +++ b/arirs/src/client.rs @@ -3,7 +3,7 @@ use std::time::Duration; use derive_getters::Getters; use futures_util::{SinkExt, StreamExt}; use rand::Rng; -use tokio::{sync::mpsc::Sender, time::interval}; +use tokio::{sync::mpsc::UnboundedSender, time::interval}; use tokio_tungstenite::{connect_async, tungstenite}; use tracing::{event, Level}; use url::Url; @@ -16,7 +16,7 @@ pub struct Client { ws_url: Url, username: String, password: String, - ws_channel: Option>, + ws_channel: Option>, } impl Client { @@ -28,7 +28,7 @@ impl Client { app_name: impl AsRef, username: impl Into, password: impl Into, - event_sender: Option>, + event_sender: Option>, ) -> Result { let url = Url::parse(url.as_ref())?.join("ari")?; @@ -76,7 +76,7 @@ impl Client { if let Some(tx) = &self.ws_channel { event!(Level::INFO, "Sending event to channel"); - if let Err(e) = tx.try_send(event) { + if let Err(e) = tx.send(event) { event!(Level::ERROR, "Error sending event: {}", e); } } From 87161d831277a73c4e62e579a5e93fa9a3d44116 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Fri, 13 Sep 2024 21:13:47 +0200 Subject: [PATCH 09/61] feat!(ari): inline websocket channel setup in client --- arirs/examples/dtmf.rs | 13 +-- arirs/examples/list-channels.rs | 4 +- arirs/examples/originate.rs | 4 +- arirs/examples/playback.rs | 21 ++-- arirs/src/bridge.rs | 28 +++--- arirs/src/channel.rs | 54 +++++----- arirs/src/client.rs | 171 +++++++++++++------------------- arirs/src/error.rs | 3 - arirs/src/lib.rs | 2 +- arirs/src/playback.rs | 6 +- arirs/src/recording.rs | 18 ++-- 11 files changed, 136 insertions(+), 188 deletions(-) diff --git a/arirs/examples/dtmf.rs b/arirs/examples/dtmf.rs index d001689..df59eb8 100644 --- a/arirs/examples/dtmf.rs +++ b/arirs/examples/dtmf.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, Mutex}; use arirs::{Client, Event}; -use tracing::{debug, error}; +use tracing::debug; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; #[tokio::main] @@ -11,18 +11,11 @@ async fn main() -> Result<(), Box> { .with(EnvFilter::from_default_env()) .init(); - let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel(); let dtmf_buffer = Arc::new(Mutex::new(String::new())); - let client = Client::new("http://localhost:8088/", "asterisk", "asterisk", "ari", Some(tx))?; + let (_, mut event_listener) = Client::connect("http://localhost:8088/", "asterisk", "asterisk", "ari").await?; - tokio::spawn(async move { - if let Err(e) = client.run().await { - error!("Error: {}", e); - } - }); - - while let Some(event) = rx.recv().await { + while let Some(event) = event_listener.recv().await { match event { Event::ChannelDtmfReceived(event) => { debug!("Received DTMF: {}", event.digit); diff --git a/arirs/examples/list-channels.rs b/arirs/examples/list-channels.rs index ba90f1e..5836ad5 100644 --- a/arirs/examples/list-channels.rs +++ b/arirs/examples/list-channels.rs @@ -1,4 +1,4 @@ -use arirs::{Channel, Client, Result}; +use arirs::{Channel, RequestClient, Result}; use tracing::debug; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; @@ -9,7 +9,7 @@ async fn main() -> Result<()> { .with(EnvFilter::from_default_env()) .init(); - let client = Client::default(); + let client = RequestClient::default(); for channel in Channel::list(&client).await? { debug!("Channel ID: {}", channel.id); diff --git a/arirs/examples/originate.rs b/arirs/examples/originate.rs index 0bde198..2f1b74a 100644 --- a/arirs/examples/originate.rs +++ b/arirs/examples/originate.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use arirs::{Channel, Client, OriginateParams, Result}; +use arirs::{Channel, OriginateParams, RequestClient, Result}; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; const APP_NAME: &str = "ari"; @@ -12,7 +12,7 @@ async fn main() -> Result<()> { .with(EnvFilter::from_default_env()) .init(); - let client = Client::default(); + let client = RequestClient::default(); Channel::originate( &client, diff --git a/arirs/examples/playback.rs b/arirs/examples/playback.rs index 48bfe6a..e14a0e6 100644 --- a/arirs/examples/playback.rs +++ b/arirs/examples/playback.rs @@ -1,28 +1,19 @@ -use std::sync::Arc; - use arirs::{Client, Event}; -use tracing::{error, level_filters::LevelFilter}; +use tracing::level_filters::LevelFilter; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] async fn main() -> Result<(), Box> { tracing_subscriber::registry().with(fmt::layer()).with(LevelFilter::TRACE).init(); - let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel(); - - let client = Arc::new(Client::new("http://localhost:8088/", "asterisk", "asterisk", "ari", Some(tx))?); - - let client_clone = client.clone(); - tokio::spawn(async move { - if let Err(e) = client_clone.run().await { - error!("Error: {}", e); - } - }); + let (request_client, mut event_listener) = Client::connect("http://localhost:8088/", "asterisk", "asterisk", "ari").await?; - while let Some(event) = rx.recv().await { + while let Some(event) = event_listener.recv().await { if let Event::StasisStart(e) = event { let channel = e.channel; - channel.play_media(&client, "sound:hello", Some("en"), None, None, None).await?; + channel + .play_media(&request_client, "sound:hello", Some("en"), None, None, None) + .await?; } } diff --git a/arirs/src/bridge.rs b/arirs/src/bridge.rs index e15744e..8429793 100644 --- a/arirs/src/bridge.rs +++ b/arirs/src/bridge.rs @@ -8,59 +8,59 @@ pub struct Bridge { } impl Bridge { - pub async fn destroy(&self, _client: &Client) -> Result<()> { + pub async fn destroy(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn add_channel(&self, _client: &Client, _channel_id: &str) -> Result<()> { + pub async fn add_channel(&self, _client: &RequestClient, _channel_id: &str) -> Result<()> { unimplemented!() } - pub async fn remove_channel(&self, _client: &Client, _channel_id: &str) -> Result<()> { + pub async fn remove_channel(&self, _client: &RequestClient, _channel_id: &str) -> Result<()> { unimplemented!() } - pub async fn set_channel_as_video_source(&self, _client: &Client, _channel_id: &str, _video_source_id: &str) -> Result<()> { + pub async fn set_channel_as_video_source(&self, _client: &RequestClient, _channel_id: &str, _video_source_id: &str) -> Result<()> { unimplemented!() } - pub async fn unset_video_source(&self, _client: &Client) -> Result<()> { + pub async fn unset_video_source(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn start_moh(&self, _client: &Client, _moh_class: &str) -> Result<()> { + pub async fn start_moh(&self, _client: &RequestClient, _moh_class: &str) -> Result<()> { unimplemented!() } - pub async fn stop_moh(&self, _client: &Client) -> Result<()> { + pub async fn stop_moh(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn play_media(&self, _client: &Client, _playback: &Playback) -> Result<()> { + pub async fn play_media(&self, _client: &RequestClient, _playback: &Playback) -> Result<()> { unimplemented!() } - pub async fn stop_media(&self, _client: &Client) -> Result<()> { + pub async fn stop_media(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn start_recording(&self, _client: &Client, _recording: &LiveRecording) -> Result<()> { + pub async fn start_recording(&self, _client: &RequestClient, _recording: &LiveRecording) -> Result<()> { unimplemented!() } - pub async fn list_bridges(_client: &Client) -> Result> { + pub async fn list_bridges(_client: &RequestClient) -> Result> { unimplemented!() } - pub async fn create_bridge(_client: &Client, _bridge_id: &str) -> Result { + pub async fn create_bridge(_client: &RequestClient, _bridge_id: &str) -> Result { unimplemented!() } - pub async fn create_bridge_with_id(_client: &Client, _bridge_id: &str, _bridge: &Bridge) -> Result { + pub async fn create_bridge_with_id(_client: &RequestClient, _bridge_id: &str, _bridge: &Bridge) -> Result { unimplemented!() } - pub async fn get_bridge(_client: &Client, _bridge_id: &str) -> Result { + pub async fn get_bridge(_client: &RequestClient, _bridge_id: &str) -> Result { unimplemented!() } } diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index 3676703..06f9d43 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -212,7 +212,7 @@ pub struct Dialplan { } impl Channel { - pub async fn hangup(self, client: &Client, reason: Reason) -> Result<()> { + pub async fn hangup(self, client: &RequestClient, reason: Reason) -> Result<()> { let mut url = client.url().join(&format!("channels/{}", self.id))?; let mut url = url.query_pairs_mut(); client.add_api_key(&mut url); @@ -228,17 +228,17 @@ impl Channel { Ok(()) } - pub fn continue_in_dialplan(self, _client: &Client) -> Result<()> { + pub fn continue_in_dialplan(self, _client: &RequestClient) -> Result<()> { unimplemented!() } /// Transfer the channel to another ARI application. /// Same as `move` in Asterisk - pub fn transfer(self, _client: &Client) -> Result<()> { + pub fn transfer(self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn answer(&self, client: &Client) -> Result<()> { + pub async fn answer(&self, client: &RequestClient) -> Result<()> { let url = client .url() .join(&format!("channels/{}/answer", self.id))? @@ -252,7 +252,7 @@ impl Channel { Ok(()) } - pub async fn start_ringing(&self, client: &Client) -> Result<()> { + pub async fn start_ringing(&self, client: &RequestClient) -> Result<()> { let url = client .url() .join(&format!("channels/{}/ring", self.id))? @@ -266,7 +266,7 @@ impl Channel { Ok(()) } - pub async fn stop_ringing(&self, client: &Client) -> Result<()> { + pub async fn stop_ringing(&self, client: &RequestClient) -> Result<()> { let url = client .url() .join(&format!("channels/{}/ring", self.id))? @@ -282,7 +282,7 @@ impl Channel { pub async fn send_dtmf( &self, - client: &Client, + client: &RequestClient, dtmf: &str, before: Option, between: Option, @@ -312,7 +312,7 @@ impl Channel { Ok(()) } - pub async fn mute(&self, client: &Client, direction: Direction) -> Result<()> { + pub async fn mute(&self, client: &RequestClient, direction: Direction) -> Result<()> { let url = client .url() .join(&format!("channels/{}/mute", self.id))? @@ -327,7 +327,7 @@ impl Channel { Ok(()) } - pub async fn unmute(&self, client: &Client, direction: Direction) -> Result<()> { + pub async fn unmute(&self, client: &RequestClient, direction: Direction) -> Result<()> { let url = client .url() .join(&format!("channels/{}/mute", self.id))? @@ -342,7 +342,7 @@ impl Channel { Ok(()) } - pub async fn hold(&self, client: &Client) -> Result<()> { + pub async fn hold(&self, client: &RequestClient) -> Result<()> { let url = client .url() .join(&format!("channels/{}/hold", self.id))? @@ -356,7 +356,7 @@ impl Channel { Ok(()) } - pub async fn unhold(&self, client: &Client) -> Result<()> { + pub async fn unhold(&self, client: &RequestClient) -> Result<()> { let url = client .url() .join(&format!("channels/{}/hold", self.id))? @@ -370,25 +370,25 @@ impl Channel { Ok(()) } - pub fn start_moh(&self, _client: &Client) -> Result<()> { + pub fn start_moh(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub fn stop_moh(&self, _client: &Client) -> Result<()> { + pub fn stop_moh(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub fn start_silence(&self, _client: &Client) -> Result<()> { + pub fn start_silence(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub fn stop_silence(&self, _client: &Client) -> Result<()> { + pub fn stop_silence(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } pub async fn play_media( &self, - client: &Client, + client: &RequestClient, media: &str, lang: Option<&str>, offset_ms: Option, @@ -435,7 +435,7 @@ impl Channel { pub async fn play_media_with_id( &self, - client: &Client, + client: &RequestClient, playback_id: &str, media: Vec<&str>, lang: Option<&str>, @@ -481,7 +481,7 @@ impl Channel { #[allow(clippy::too_many_arguments)] pub async fn record( &self, - client: &Client, + client: &RequestClient, name: &str, format: &str, max_duration_seconds: Option, @@ -525,15 +525,15 @@ impl Channel { Ok(recording) } - pub fn get_variable(&self, _client: &Client) -> Result { + pub fn get_variable(&self, _client: &RequestClient) -> Result { unimplemented!() } - pub fn set_variable(&self, _client: &Client) -> Result<()> { + pub fn set_variable(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn dial(&self, client: &Client, caller_id: Option<&str>, timeout: Option) -> Result<()> { + pub async fn dial(&self, client: &RequestClient, caller_id: Option<&str>, timeout: Option) -> Result<()> { let mut url = client.url().join(&format!("channels/{}/dial", self.id))?; let mut url = url.query_pairs_mut(); client.add_api_key(&mut url); @@ -552,11 +552,11 @@ impl Channel { Ok(()) } - pub fn get_rtp_statistics(&self, _client: &Client) -> Result { + pub fn get_rtp_statistics(&self, _client: &RequestClient) -> Result { unimplemented!() } - pub async fn list(client: &Client) -> Result> { + pub async fn list(client: &RequestClient) -> Result> { let url: Url = client .url() .join("channels")? @@ -572,7 +572,7 @@ impl Channel { #[allow(clippy::too_many_arguments)] pub async fn create( - client: &Client, + client: &RequestClient, endpoint: &str, app: &str, app_args: Vec<&str>, @@ -625,7 +625,7 @@ impl Channel { Ok(channel) } - pub async fn get(client: &Client, channel_id: &str) -> Result { + pub async fn get(client: &RequestClient, channel_id: &str) -> Result { let url = client .url() .join(&format!("channels/{}", channel_id))? @@ -641,7 +641,7 @@ impl Channel { #[allow(clippy::too_many_arguments)] pub async fn originate<'a>( - client: &Client, + client: &RequestClient, endpoint: &str, params: OriginateParams<'a>, caller_id: Option<&str>, @@ -729,7 +729,7 @@ impl Channel { #[allow(clippy::too_many_arguments)] pub async fn originate_with_id<'a>( - client: &Client, + client: &RequestClient, channel_id: &str, endpoint: &str, params: OriginateParams<'a>, diff --git a/arirs/src/client.rs b/arirs/src/client.rs index 050be01..cb53452 100644 --- a/arirs/src/client.rs +++ b/arirs/src/client.rs @@ -3,7 +3,7 @@ use std::time::Duration; use derive_getters::Getters; use futures_util::{SinkExt, StreamExt}; use rand::Rng; -use tokio::{sync::mpsc::UnboundedSender, time::interval}; +use tokio::{sync::mpsc::UnboundedReceiver, task::JoinHandle, time::interval}; use tokio_tungstenite::{connect_async, tungstenite}; use tracing::{event, Level}; use url::Url; @@ -11,25 +11,34 @@ use url::Url; use crate::*; #[derive(Debug, Getters)] -pub struct Client { +pub struct RequestClient { url: Url, - ws_url: Url, username: String, password: String, - ws_channel: Option>, } +impl RequestClient { + pub(crate) fn get_api_key(&self) -> String { + format!("{}:{}", self.username, self.password) + } + + pub(crate) fn add_api_key(&self, url: &mut url::form_urlencoded::Serializer) { + url.append_pair("api_key", &self.get_api_key()); + } +} + +pub struct Client; + impl Client { /// Create a new client /// /// `url` should end in `/`, `ari/` will be appended to it. - pub fn new( + pub async fn connect( url: impl AsRef, app_name: impl AsRef, username: impl Into, password: impl Into, - event_sender: Option>, - ) -> Result { + ) -> Result<(RequestClient, UnboundedReceiver)> { let url = Url::parse(url.as_ref())?.join("ari")?; let mut ws_url = url.join("events")?; @@ -49,124 +58,82 @@ impl Client { .append_pair("api_key", &format!("{}:{}", username, password)) .append_pair("subscribeAll", "true"); - Ok(Self { - url, - ws_url, - username, - password, - ws_channel: event_sender, - }) - } - - pub fn handle_message(&self, message: Vec) { - let data = String::from_utf8(message).unwrap(); - - event!(Level::TRACE, "Parsing event"); - - let event: Event = match serde_json::from_str(&data) { - Ok(data) => data, - Err(e) => { - event!(Level::ERROR, "Error: {}", e); - event!(Level::ERROR, "Data: {}", data); - return; - } - }; + let request_client = RequestClient { url, username, password }; - event!(Level::TRACE, "Event parsed successfully"); + let event_listener = Self::connect_ws(ws_url).await?; - if let Some(tx) = &self.ws_channel { - event!(Level::INFO, "Sending event to channel"); - if let Err(e) = tx.send(event) { - event!(Level::ERROR, "Error sending event: {}", e); - } - } + Ok((request_client, event_listener)) } - pub async fn run(&self) -> Result<()> { - event!(Level::INFO, "Connecting to Asterisk"); - - let (ws_stream, _) = match connect_async(&self.ws_url.to_string()).await { - Ok(stream) => stream, - Err(e) => { - event!(Level::ERROR, "Failed to connect to Asterisk: {}", e); - return Err(e.into()); - } - }; - - event!(Level::INFO, "WebSocket handshake has been successfully completed"); - - let (mut ws_sender, mut ws_receiver) = ws_stream.split(); - let mut interval = interval(Duration::from_millis(5000)); - - loop { - tokio::select! { - message = ws_receiver.next() => { - match message { - Some(message) => { - let message = message?; - match message { - tungstenite::Message::Text(_) => { - event!(Level::INFO, "Received WebSocket Text"); - self.handle_message(message.into_data()); - } - tungstenite::Message::Ping(data) => { - event!(Level::INFO, "Received WebSocket Ping, sending Pong"); - ws_sender.send(tungstenite::Message::Pong(data)).await?; - } - tungstenite::Message::Pong(_) => { - event!(Level::INFO, "Received WebSocket Pong"); - }, - tungstenite::Message::Close(frame) => { - event!(Level::INFO, "WebSocket closed: {:?}", frame); - break; - }, - _ => { - event!(Level::INFO, "Unknown WebSocket message"); + async fn connect_ws(ws_url: Url) -> Result> { + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + + let (ws_stream, _) = connect_async(&ws_url.to_string()).await?; + + let _join_task: JoinHandle> = tokio::task::spawn(async move { + let (mut ws_sender, mut ws_receiver) = ws_stream.split(); + let mut interval = interval(Duration::from_millis(5000)); + + loop { + tokio::select! { + message = ws_receiver.next() => { + match message { + Some(message) => { + let message = message?; + match message { + tungstenite::Message::Text(_) => { + if let Err(e) = tx.send(serde_json::from_slice(message.into_data().as_slice()).map_err(|err| AriError::Unknown(err.to_string()))?) { + event!(Level::ERROR, "Error sending event: {}", e); + } + } + tungstenite::Message::Ping(data) => { + event!(Level::TRACE, "Received WebSocket Ping, sending Pong"); + ws_sender.send(tungstenite::Message::Pong(data)).await?; + } + tungstenite::Message::Pong(_) => { + event!(Level::TRACE, "Received WebSocket Pong"); + }, + tungstenite::Message::Close(frame) => { + event!(Level::INFO, "WebSocket closed: {:?}", frame); + break; + }, + _ => { + event!(Level::INFO, "Unknown WebSocket message"); + } } } - } - None => { - event!(Level::INFO, "WebSocket closed"); - break; + None => { + tracing::error!("WebSocket closed"); + break; + } } } - } - _ = interval.tick() => { - // every 5 seconds we are sending ping to keep connection alive - // https://rust-lang-nursery.github.io/rust-cookbook/algorithms/randomness.html - let random_bytes = rand::thread_rng().gen::<[u8; 32]>().to_vec(); - let _ = ws_sender.send(tungstenite::Message::Ping(random_bytes)).await; - event!(Level::DEBUG, "ARI connection ping sent"); + _ = interval.tick() => { + // every 5 seconds we are sending ping to keep connection alive + // https://rust-lang-nursery.github.io/rust-cookbook/algorithms/randomness.html + let random_bytes = rand::thread_rng().gen::<[u8; 32]>().to_vec(); + let _ = ws_sender.send(tungstenite::Message::Ping(random_bytes)).await; + event!(Level::DEBUG, "ARI connection ping sent"); + } } } - } - Ok(()) - } + Ok(()) + }); - pub(crate) fn get_api_key(&self) -> String { - format!("{}:{}", self.username, self.password) - } - - pub(crate) fn add_api_key(&self, url: &mut url::form_urlencoded::Serializer) { - url.append_pair("api_key", &self.get_api_key()); + Ok(rx) } } -impl Default for Client { +impl Default for RequestClient { fn default() -> Self { Self { url: match Url::parse("http://localhost:8088/") { Ok(url) => url, Err(_) => panic!("Failed to parse URL"), }, - ws_url: match Url::parse("ws://localhost:8088/ari/events?app=ari&api_key=asterisk:asterisk&subscribeAll=true") { - Ok(url) => url, - Err(_) => panic!("Failed to parse URL"), - }, username: "asterisk".to_string(), password: "asterisk".to_string(), - ws_channel: None, } } } diff --git a/arirs/src/error.rs b/arirs/src/error.rs index db9a5ca..f9e6817 100644 --- a/arirs/src/error.rs +++ b/arirs/src/error.rs @@ -1,5 +1,4 @@ use thiserror::Error; -use tokio::task::JoinError; use tokio_tungstenite::tungstenite; pub type Result = std::result::Result; @@ -12,8 +11,6 @@ pub enum AriError { TungsteniteError(#[from] tungstenite::Error), #[error("HTTP Request error")] ReqwestError(#[from] reqwest::Error), - #[error("Join Error")] - JoinError(#[from] JoinError), #[error("Unknown error occurred: {0}")] Unknown(String), } diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index 323780b..e949339 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -5,7 +5,7 @@ mod channel; pub use channel::*; mod client; -pub use client::Client; +pub use client::{Client, RequestClient}; mod device; pub use device::{DeviceState, DeviceStateChanged}; diff --git a/arirs/src/playback.rs b/arirs/src/playback.rs index a4a8bcd..d785a45 100644 --- a/arirs/src/playback.rs +++ b/arirs/src/playback.rs @@ -10,15 +10,15 @@ pub struct Playback { } impl Playback { - pub async fn get_playback(_client: &Client, _playback_id: &str) -> Result { + pub async fn get_playback(_client: &RequestClient, _playback_id: &str) -> Result { unimplemented!() } - pub async fn control(&self, _client: &Client, _operation: Operation) -> Result<()> { + pub async fn control(&self, _client: &RequestClient, _operation: Operation) -> Result<()> { unimplemented!() } - pub async fn stop(&self, _client: &Client) -> Result<()> { + pub async fn stop(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } } diff --git a/arirs/src/recording.rs b/arirs/src/recording.rs index 892c4a1..3e85cf1 100644 --- a/arirs/src/recording.rs +++ b/arirs/src/recording.rs @@ -14,28 +14,28 @@ impl LiveRecording { unimplemented!() } - pub async fn discard(&self, _client: &Client) -> Result<()> { + pub async fn discard(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } // TODO: explore if it's possible to return a StoredRecording - pub async fn stop(&self, _client: &Client) -> Result<()> { + pub async fn stop(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn pause(&self, _client: &Client) -> Result<()> { + pub async fn pause(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn resume(&self, _client: &Client) -> Result<()> { + pub async fn resume(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn mute(&self, _client: &Client) -> Result<()> { + pub async fn mute(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn unmute(&self, _client: &Client) -> Result<()> { + pub async fn unmute(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } } @@ -48,7 +48,7 @@ pub struct StoredRecording { } impl StoredRecording { - pub async fn list(_client: &Client) -> Result> { + pub async fn list(_client: &RequestClient) -> Result> { unimplemented!() } @@ -56,11 +56,11 @@ impl StoredRecording { unimplemented!() } - pub async fn delete(&self, _client: &Client) -> Result<()> { + pub async fn delete(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn download(&self, _client: &Client) -> Result<&[u8]> { + pub async fn download(&self, _client: &RequestClient) -> Result<&[u8]> { unimplemented!() } } From 853fccbb680f014d3eae06f79af6b31bc49e86c4 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Fri, 13 Sep 2024 21:22:35 +0200 Subject: [PATCH 10/61] refactor(ari): move `RequestClient` to its own file --- arirs/src/client.rs | 18 ------------------ arirs/src/lib.rs | 5 ++++- arirs/src/request_client/mod.rs | 22 ++++++++++++++++++++++ 3 files changed, 26 insertions(+), 19 deletions(-) create mode 100644 arirs/src/request_client/mod.rs diff --git a/arirs/src/client.rs b/arirs/src/client.rs index cb53452..225bcfc 100644 --- a/arirs/src/client.rs +++ b/arirs/src/client.rs @@ -1,6 +1,5 @@ use std::time::Duration; -use derive_getters::Getters; use futures_util::{SinkExt, StreamExt}; use rand::Rng; use tokio::{sync::mpsc::UnboundedReceiver, task::JoinHandle, time::interval}; @@ -10,23 +9,6 @@ use url::Url; use crate::*; -#[derive(Debug, Getters)] -pub struct RequestClient { - url: Url, - username: String, - password: String, -} - -impl RequestClient { - pub(crate) fn get_api_key(&self) -> String { - format!("{}:{}", self.username, self.password) - } - - pub(crate) fn add_api_key(&self, url: &mut url::form_urlencoded::Serializer) { - url.append_pair("api_key", &self.get_api_key()); - } -} - pub struct Client; impl Client { diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index e949339..e72b00b 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -1,3 +1,6 @@ +mod request_client; +pub use request_client::RequestClient; + mod bridge; pub use bridge::Bridge; @@ -5,7 +8,7 @@ mod channel; pub use channel::*; mod client; -pub use client::{Client, RequestClient}; +pub use client::Client; mod device; pub use device::{DeviceState, DeviceStateChanged}; diff --git a/arirs/src/request_client/mod.rs b/arirs/src/request_client/mod.rs new file mode 100644 index 0000000..1de7596 --- /dev/null +++ b/arirs/src/request_client/mod.rs @@ -0,0 +1,22 @@ +pub use core::RequestClient; +mod core { + use derive_getters::Getters; + use url::Url; + + #[derive(Debug, Getters)] + pub struct RequestClient { + pub(crate) url: Url, + pub(crate) username: String, + pub(crate) password: String, + } + + impl RequestClient { + pub(crate) fn get_api_key(&self) -> String { + format!("{}:{}", self.username, self.password) + } + + pub(crate) fn add_api_key(&self, url: &mut url::form_urlencoded::Serializer) { + url.append_pair("api_key", &self.get_api_key()); + } + } +} From c77b1bac47c5633a6b9c4020f9f36ad50c1f0826 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Fri, 13 Sep 2024 21:39:26 +0200 Subject: [PATCH 11/61] refactor!(ari): invert ownership if `Channel` requests --- arirs/examples/list-channels.rs | 4 +- arirs/examples/originate.rs | 34 +++--- arirs/examples/playback.rs | 4 +- arirs/src/channel.rs | 190 ++++++++++++++++---------------- 4 files changed, 116 insertions(+), 116 deletions(-) diff --git a/arirs/examples/list-channels.rs b/arirs/examples/list-channels.rs index 5836ad5..68023b3 100644 --- a/arirs/examples/list-channels.rs +++ b/arirs/examples/list-channels.rs @@ -1,4 +1,4 @@ -use arirs::{Channel, RequestClient, Result}; +use arirs::{RequestClient, Result}; use tracing::debug; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; @@ -11,7 +11,7 @@ async fn main() -> Result<()> { let client = RequestClient::default(); - for channel in Channel::list(&client).await? { + for channel in client.list().await? { debug!("Channel ID: {}", channel.id); } diff --git a/arirs/examples/originate.rs b/arirs/examples/originate.rs index 2f1b74a..392663e 100644 --- a/arirs/examples/originate.rs +++ b/arirs/examples/originate.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use arirs::{Channel, OriginateParams, RequestClient, Result}; +use arirs::{OriginateParams, RequestClient, Result}; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; const APP_NAME: &str = "ari"; @@ -14,22 +14,22 @@ async fn main() -> Result<()> { let client = RequestClient::default(); - Channel::originate( - &client, - "PJSIP/1000", - OriginateParams::Application { - app: APP_NAME, - app_args: vec![], - }, - None, - None, - None, - None, - None, - vec!["alaw,ulaw"], - HashMap::new(), - ) - .await?; + client + .originate( + "PJSIP/1000", + OriginateParams::Application { + app: APP_NAME, + app_args: vec![], + }, + None, + None, + None, + None, + None, + vec!["alaw,ulaw"], + HashMap::new(), + ) + .await?; Ok(()) } diff --git a/arirs/examples/playback.rs b/arirs/examples/playback.rs index e14a0e6..1c2a13d 100644 --- a/arirs/examples/playback.rs +++ b/arirs/examples/playback.rs @@ -11,8 +11,8 @@ async fn main() -> Result<(), Box> { while let Some(event) = event_listener.recv().await { if let Event::StasisStart(e) = event { let channel = e.channel; - channel - .play_media(&request_client, "sound:hello", Some("en"), None, None, None) + request_client + .play_media(&channel, "sound:hello", Some("en"), None, None, None) .await?; } } diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index 06f9d43..c41b3bf 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -211,11 +211,11 @@ pub struct Dialplan { pub app_data: String, } -impl Channel { - pub async fn hangup(self, client: &RequestClient, reason: Reason) -> Result<()> { - let mut url = client.url().join(&format!("channels/{}", self.id))?; +impl RequestClient { + pub async fn hangup(&self, channel: &Channel, reason: Reason) -> Result<()> { + let mut url = self.url().join(&format!("channels/{}", channel.id))?; let mut url = url.query_pairs_mut(); - client.add_api_key(&mut url); + self.add_api_key(&mut url); match reason { Reason::Code(_) => url.append_pair("reason_code", &format!("{}", reason)), @@ -224,74 +224,64 @@ impl Channel { reqwest::Client::new().delete(url.finish().to_owned()).send().await?; - event!(Level::INFO, "hung up channel with id {}", self.id); + event!(Level::INFO, "hung up channel with id {}", channel.id); Ok(()) } - pub fn continue_in_dialplan(self, _client: &RequestClient) -> Result<()> { - unimplemented!() - } - - /// Transfer the channel to another ARI application. - /// Same as `move` in Asterisk - pub fn transfer(self, _client: &RequestClient) -> Result<()> { - unimplemented!() - } - - pub async fn answer(&self, client: &RequestClient) -> Result<()> { - let url = client + pub async fn answer(&self, channel: &Channel) -> Result<()> { + let url = self .url() - .join(&format!("channels/{}/answer", self.id))? + .join(&format!("channels/{}/answer", channel.id))? .query_pairs_mut() - .append_pair("api_key", &client.get_api_key()) + .append_pair("api_key", &self.get_api_key()) .finish() .to_owned(); reqwest::Client::new().post(url).send().await?; - event!(Level::INFO, "answered channel with id {}", self.id); + event!(Level::INFO, "answered channel with id {}", channel.id); Ok(()) } - pub async fn start_ringing(&self, client: &RequestClient) -> Result<()> { - let url = client + pub async fn start_ringing(&self, channel: &Channel) -> Result<()> { + let url = self .url() - .join(&format!("channels/{}/ring", self.id))? + .join(&format!("channels/{}/ring", channel.id))? .query_pairs_mut() - .append_pair("api_key", &client.get_api_key()) + .append_pair("api_key", &self.get_api_key()) .finish() .to_owned(); reqwest::Client::new().post(url).send().await?; - event!(Level::INFO, "started ringing channel with id {}", self.id); + event!(Level::INFO, "started ringing channel with id {}", channel.id); Ok(()) } - pub async fn stop_ringing(&self, client: &RequestClient) -> Result<()> { - let url = client + pub async fn stop_ringing(&self, channel: &Channel) -> Result<()> { + let url = self .url() - .join(&format!("channels/{}/ring", self.id))? + .join(&format!("channels/{}/ring", channel.id))? .query_pairs_mut() - .append_pair("api_key", &client.get_api_key()) + .append_pair("api_key", &self.get_api_key()) .finish() .to_owned(); reqwest::Client::new().delete(url).send().await?; - event!(Level::INFO, "stopped ringing channel with id {}", self.id); + event!(Level::INFO, "stopped ringing channel with id {}", channel.id); Ok(()) } pub async fn send_dtmf( &self, - client: &RequestClient, + channel: &Channel, dtmf: &str, before: Option, between: Option, duration: Option, after: Option, ) -> Result<()> { - let mut url = client.url().join(&format!("channels/{}/dtmf", self.id))?; + let mut url = self.url().join(&format!("channels/{}/dtmf", channel.id))?; let mut url = url.query_pairs_mut(); - client.add_api_key(&mut url); + self.add_api_key(&mut url); url.append_pair("dtmf", dtmf) .append_pair("between", &between.map(|d| d.num_milliseconds()).unwrap_or(100).to_string()) @@ -307,97 +297,97 @@ impl Channel { reqwest::Client::new().post(url.finish().to_owned()).send().await?; - event!(Level::INFO, "sent dtmf '{}' to channel with id {}", dtmf, self.id); + event!(Level::INFO, "sent dtmf '{}' to channel with id {}", dtmf, channel.id); Ok(()) } - pub async fn mute(&self, client: &RequestClient, direction: Direction) -> Result<()> { - let url = client + pub async fn mute(&self, channel: &Channel, direction: Direction) -> Result<()> { + let url = self .url() - .join(&format!("channels/{}/mute", self.id))? + .join(&format!("channels/{}/mute", channel.id))? .query_pairs_mut() - .append_pair("api_key", &client.get_api_key()) + .append_pair("api_key", &self.get_api_key()) .append_pair("direction", &format!("{}", direction)) .finish() .to_owned(); reqwest::Client::new().post(url).send().await?; - event!(Level::INFO, "muted channel with id {}", self.id); + event!(Level::INFO, "muted channel with id {}", channel.id); Ok(()) } - pub async fn unmute(&self, client: &RequestClient, direction: Direction) -> Result<()> { - let url = client + pub async fn unmute(&self, channel: &Channel, direction: Direction) -> Result<()> { + let url = self .url() - .join(&format!("channels/{}/mute", self.id))? + .join(&format!("channels/{}/mute", channel.id))? .query_pairs_mut() - .append_pair("api_key", &client.get_api_key()) + .append_pair("api_key", &self.get_api_key()) .append_pair("direction", &format!("{}", direction)) .finish() .to_owned(); reqwest::Client::new().delete(url).send().await?; - event!(Level::INFO, "unmuted channel with id {}", self.id); + event!(Level::INFO, "unmuted channel with id {}", channel.id); Ok(()) } - pub async fn hold(&self, client: &RequestClient) -> Result<()> { - let url = client + pub async fn hold(&self, channel: &Channel) -> Result<()> { + let url = self .url() - .join(&format!("channels/{}/hold", self.id))? + .join(&format!("channels/{}/hold", channel.id))? .query_pairs_mut() - .append_pair("api_key", &client.get_api_key()) + .append_pair("api_key", &self.get_api_key()) .finish() .to_owned(); reqwest::Client::new().post(url).send().await?; - event!(Level::INFO, "started hold on channel with id {}", self.id); + event!(Level::INFO, "started hold on channel with id {}", channel.id); Ok(()) } - pub async fn unhold(&self, client: &RequestClient) -> Result<()> { - let url = client + pub async fn unhold(&self, channel: &Channel) -> Result<()> { + let url = self .url() - .join(&format!("channels/{}/hold", self.id))? + .join(&format!("channels/{}/hold", channel.id))? .query_pairs_mut() - .append_pair("api_key", &client.get_api_key()) + .append_pair("api_key", &self.get_api_key()) .finish() .to_owned(); reqwest::Client::new().delete(url).send().await?; - event!(Level::INFO, "stopped hold on channel with id {}", self.id); + event!(Level::INFO, "stopped hold on channel with id {}", channel.id); Ok(()) } - pub fn start_moh(&self, _client: &RequestClient) -> Result<()> { + pub fn start_moh(&self, _channel: &Channel) -> Result<()> { unimplemented!() } - pub fn stop_moh(&self, _client: &RequestClient) -> Result<()> { + pub fn stop_moh(&self, _channel: &Channel) -> Result<()> { unimplemented!() } - pub fn start_silence(&self, _client: &RequestClient) -> Result<()> { + pub fn start_silence(&self, _channel: &Channel) -> Result<()> { unimplemented!() } - pub fn stop_silence(&self, _client: &RequestClient) -> Result<()> { + pub fn stop_silence(&self, _channel: &Channel) -> Result<()> { unimplemented!() } pub async fn play_media( &self, - client: &RequestClient, + channel: &Channel, media: &str, lang: Option<&str>, offset_ms: Option, skip_ms: Option, playback_id: Option<&str>, ) -> Result { - let mut url = client.url().join(&format!("channels/{}/play", self.id))?; + let mut url = self.url().join(&format!("channels/{}/play", channel.id))?; let mut url = url.query_pairs_mut(); - client.add_api_key(&mut url); + self.add_api_key(&mut url); url.append_pair("media", media); if let Some(lang) = lang { @@ -427,7 +417,7 @@ impl Channel { Level::INFO, "started media playback with id {} on channel with id {}", playback.id, - self.id + channel.id ); Ok(playback) @@ -435,17 +425,17 @@ impl Channel { pub async fn play_media_with_id( &self, - client: &RequestClient, + channel: &Channel, playback_id: &str, media: Vec<&str>, lang: Option<&str>, offset_ms: Option, skip_ms: Option, ) -> Result { - let mut url = client.url().join(&format!("channels/{}/play/{}/media", self.id, playback_id))?; + let mut url = self.url().join(&format!("channels/{}/play/{}/media", channel.id, playback_id))?; let mut url = url.query_pairs_mut(); - client.add_api_key(&mut url); + self.add_api_key(&mut url); let media = media.join(","); url.append_pair("media", &media); @@ -472,7 +462,7 @@ impl Channel { Level::INFO, "started media playback with id {} on channel with id {}", playback.id, - self.id + channel.id ); Ok(playback) @@ -481,7 +471,7 @@ impl Channel { #[allow(clippy::too_many_arguments)] pub async fn record( &self, - client: &RequestClient, + channel: &Channel, name: &str, format: &str, max_duration_seconds: Option, @@ -490,9 +480,9 @@ impl Channel { beep: bool, terminate_on: RecordingTermination, ) -> Result { - let mut url = client.url().join(&format!("channels/{}/record", self.id))?; + let mut url = self.url().join(&format!("channels/{}/record", channel.id))?; let mut url = url.query_pairs_mut(); - client.add_api_key(&mut url); + self.add_api_key(&mut url); url.append_pair("name", name) .append_pair("format", format) @@ -519,24 +509,24 @@ impl Channel { Level::INFO, "started recording with id {} on channel with id {}", recording.id, - self.id + channel.id ); Ok(recording) } - pub fn get_variable(&self, _client: &RequestClient) -> Result { + pub fn get_variable(&self, _channel: &Channel) -> Result { unimplemented!() } - pub fn set_variable(&self, _client: &RequestClient) -> Result<()> { + pub fn set_variable(&self, _channel: &Channel) -> Result<()> { unimplemented!() } - pub async fn dial(&self, client: &RequestClient, caller_id: Option<&str>, timeout: Option) -> Result<()> { - let mut url = client.url().join(&format!("channels/{}/dial", self.id))?; + pub async fn dial(&self, channel: &Channel, caller_id: Option<&str>, timeout: Option) -> Result<()> { + let mut url = self.url().join(&format!("channels/{}/dial", channel.id))?; let mut url = url.query_pairs_mut(); - client.add_api_key(&mut url); + self.add_api_key(&mut url); if let Some(caller_id) = caller_id { url.append_pair("callerId", caller_id); @@ -548,20 +538,16 @@ impl Channel { reqwest::Client::new().post(url.finish().to_owned()).send().await?; - event!(Level::INFO, "dialed channel with id {}", self.id); + event!(Level::INFO, "dialed channel with id {}", channel.id); Ok(()) } - pub fn get_rtp_statistics(&self, _client: &RequestClient) -> Result { - unimplemented!() - } - - pub async fn list(client: &RequestClient) -> Result> { - let url: Url = client + pub async fn list(&self) -> Result> { + let url: Url = self .url() .join("channels")? .query_pairs_mut() - .append_pair("api_key", &client.get_api_key()) + .append_pair("api_key", &self.get_api_key()) .finish() .to_owned(); @@ -572,7 +558,7 @@ impl Channel { #[allow(clippy::too_many_arguments)] pub async fn create( - client: &RequestClient, + &self, endpoint: &str, app: &str, app_args: Vec<&str>, @@ -582,9 +568,9 @@ impl Channel { formats: Vec<&str>, variables: HashMap<&str, &str>, ) -> Result { - let mut url = client.url().join("channels")?; + let mut url = self.url().join("channels")?; let mut url = url.query_pairs_mut(); - client.add_api_key(&mut url); + self.add_api_key(&mut url); url.append_pair("endpoint", endpoint).append_pair("app", app); if !formats.is_empty() { @@ -625,12 +611,12 @@ impl Channel { Ok(channel) } - pub async fn get(client: &RequestClient, channel_id: &str) -> Result { - let url = client + pub async fn get(self, channel_id: &str) -> Result { + let url = self .url() .join(&format!("channels/{}", channel_id))? .query_pairs_mut() - .append_pair("api_key", &client.get_api_key()) + .append_pair("api_key", &self.get_api_key()) .finish() .to_owned(); @@ -641,7 +627,7 @@ impl Channel { #[allow(clippy::too_many_arguments)] pub async fn originate<'a>( - client: &RequestClient, + &self, endpoint: &str, params: OriginateParams<'a>, caller_id: Option<&str>, @@ -652,9 +638,9 @@ impl Channel { formats: Vec<&str>, variables: HashMap<&str, &str>, ) -> Result { - let mut url = client.url().join("channels")?; + let mut url = self.url().join("channels")?; let mut url = url.query_pairs_mut(); - client.add_api_key(&mut url); + self.add_api_key(&mut url); url.append_pair("endpoint", endpoint) .append_pair("timeout", &timeout.unwrap_or(30).to_string()); @@ -729,7 +715,7 @@ impl Channel { #[allow(clippy::too_many_arguments)] pub async fn originate_with_id<'a>( - client: &RequestClient, + &self, channel_id: &str, endpoint: &str, params: OriginateParams<'a>, @@ -740,9 +726,9 @@ impl Channel { formats: Vec<&str>, variables: HashMap<&str, &str>, ) -> Result { - let mut url = client.url().join(&format!("channels/{}", channel_id))?; + let mut url = self.url().join(&format!("channels/{}", channel_id))?; let mut url = url.query_pairs_mut(); - client.add_api_key(&mut url); + self.add_api_key(&mut url); url.append_pair("endpoint", endpoint) .append_pair("timeout", &timeout.unwrap_or(30).to_string()); @@ -811,6 +797,20 @@ impl Channel { Ok(channel) } + pub fn continue_in_dialplan(&self, _channel_id: &str) -> Result<()> { + unimplemented!() + } + + /// Transfer the channel to another ARI application. + /// Same as `move` in Asterisk + pub fn transfer(&self, _channel_id: &str) -> Result<()> { + unimplemented!() + } + + pub fn get_rtp_statistics(&self, _channel: &Channel) -> Result { + unimplemented!() + } + pub fn snoop(&self, _channel_id: &str) -> Result { unimplemented!() } From 218d52cf78e5c76e30242e454d791652659f9a21 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Fri, 13 Sep 2024 21:44:16 +0200 Subject: [PATCH 12/61] refactor!(ari/channel): require `channel_id` only in request calls --- arirs/examples/playback.rs | 2 +- arirs/src/channel.rs | 96 +++++++++++++++++++------------------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/arirs/examples/playback.rs b/arirs/examples/playback.rs index 1c2a13d..bbe2ec2 100644 --- a/arirs/examples/playback.rs +++ b/arirs/examples/playback.rs @@ -12,7 +12,7 @@ async fn main() -> Result<(), Box> { if let Event::StasisStart(e) = event { let channel = e.channel; request_client - .play_media(&channel, "sound:hello", Some("en"), None, None, None) + .play_media(&channel.id, "sound:hello", Some("en"), None, None, None) .await?; } } diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index c41b3bf..0d97b8a 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -212,8 +212,8 @@ pub struct Dialplan { } impl RequestClient { - pub async fn hangup(&self, channel: &Channel, reason: Reason) -> Result<()> { - let mut url = self.url().join(&format!("channels/{}", channel.id))?; + pub async fn hangup(&self, channel_id: &str, reason: Reason) -> Result<()> { + let mut url = self.url().join(&format!("channels/{}", channel_id))?; let mut url = url.query_pairs_mut(); self.add_api_key(&mut url); @@ -224,62 +224,62 @@ impl RequestClient { reqwest::Client::new().delete(url.finish().to_owned()).send().await?; - event!(Level::INFO, "hung up channel with id {}", channel.id); + event!(Level::INFO, "hung up channel with id {}", channel_id); Ok(()) } - pub async fn answer(&self, channel: &Channel) -> Result<()> { + pub async fn answer(&self, channel_id: &str) -> Result<()> { let url = self .url() - .join(&format!("channels/{}/answer", channel.id))? + .join(&format!("channels/{}/answer", channel_id))? .query_pairs_mut() .append_pair("api_key", &self.get_api_key()) .finish() .to_owned(); reqwest::Client::new().post(url).send().await?; - event!(Level::INFO, "answered channel with id {}", channel.id); + event!(Level::INFO, "answered channel with id {}", channel_id); Ok(()) } - pub async fn start_ringing(&self, channel: &Channel) -> Result<()> { + pub async fn start_ringing(&self, channel_id: &str) -> Result<()> { let url = self .url() - .join(&format!("channels/{}/ring", channel.id))? + .join(&format!("channels/{}/ring", channel_id))? .query_pairs_mut() .append_pair("api_key", &self.get_api_key()) .finish() .to_owned(); reqwest::Client::new().post(url).send().await?; - event!(Level::INFO, "started ringing channel with id {}", channel.id); + event!(Level::INFO, "started ringing channel with id {}", channel_id); Ok(()) } - pub async fn stop_ringing(&self, channel: &Channel) -> Result<()> { + pub async fn stop_ringing(&self, channel_id: &str) -> Result<()> { let url = self .url() - .join(&format!("channels/{}/ring", channel.id))? + .join(&format!("channels/{}/ring", channel_id))? .query_pairs_mut() .append_pair("api_key", &self.get_api_key()) .finish() .to_owned(); reqwest::Client::new().delete(url).send().await?; - event!(Level::INFO, "stopped ringing channel with id {}", channel.id); + event!(Level::INFO, "stopped ringing channel with id {}", channel_id); Ok(()) } pub async fn send_dtmf( &self, - channel: &Channel, + channel_id: &str, dtmf: &str, before: Option, between: Option, duration: Option, after: Option, ) -> Result<()> { - let mut url = self.url().join(&format!("channels/{}/dtmf", channel.id))?; + let mut url = self.url().join(&format!("channels/{}/dtmf", channel_id))?; let mut url = url.query_pairs_mut(); self.add_api_key(&mut url); @@ -297,15 +297,15 @@ impl RequestClient { reqwest::Client::new().post(url.finish().to_owned()).send().await?; - event!(Level::INFO, "sent dtmf '{}' to channel with id {}", dtmf, channel.id); + event!(Level::INFO, "sent dtmf '{}' to channel with id {}", dtmf, channel_id); Ok(()) } - pub async fn mute(&self, channel: &Channel, direction: Direction) -> Result<()> { + pub async fn mute(&self, channel_id: &str, direction: Direction) -> Result<()> { let url = self .url() - .join(&format!("channels/{}/mute", channel.id))? + .join(&format!("channels/{}/mute", channel_id))? .query_pairs_mut() .append_pair("api_key", &self.get_api_key()) .append_pair("direction", &format!("{}", direction)) @@ -313,14 +313,14 @@ impl RequestClient { .to_owned(); reqwest::Client::new().post(url).send().await?; - event!(Level::INFO, "muted channel with id {}", channel.id); + event!(Level::INFO, "muted channel with id {}", channel_id); Ok(()) } - pub async fn unmute(&self, channel: &Channel, direction: Direction) -> Result<()> { + pub async fn unmute(&self, channel_id: &str, direction: Direction) -> Result<()> { let url = self .url() - .join(&format!("channels/{}/mute", channel.id))? + .join(&format!("channels/{}/mute", channel_id))? .query_pairs_mut() .append_pair("api_key", &self.get_api_key()) .append_pair("direction", &format!("{}", direction)) @@ -328,64 +328,64 @@ impl RequestClient { .to_owned(); reqwest::Client::new().delete(url).send().await?; - event!(Level::INFO, "unmuted channel with id {}", channel.id); + event!(Level::INFO, "unmuted channel with id {}", channel_id); Ok(()) } - pub async fn hold(&self, channel: &Channel) -> Result<()> { + pub async fn hold(&self, channel_id: &str) -> Result<()> { let url = self .url() - .join(&format!("channels/{}/hold", channel.id))? + .join(&format!("channels/{}/hold", channel_id))? .query_pairs_mut() .append_pair("api_key", &self.get_api_key()) .finish() .to_owned(); reqwest::Client::new().post(url).send().await?; - event!(Level::INFO, "started hold on channel with id {}", channel.id); + event!(Level::INFO, "started hold on channel with id {}", channel_id); Ok(()) } - pub async fn unhold(&self, channel: &Channel) -> Result<()> { + pub async fn unhold(&self, channel_id: &str) -> Result<()> { let url = self .url() - .join(&format!("channels/{}/hold", channel.id))? + .join(&format!("channels/{}/hold", channel_id))? .query_pairs_mut() .append_pair("api_key", &self.get_api_key()) .finish() .to_owned(); reqwest::Client::new().delete(url).send().await?; - event!(Level::INFO, "stopped hold on channel with id {}", channel.id); + event!(Level::INFO, "stopped hold on channel with id {}", channel_id); Ok(()) } - pub fn start_moh(&self, _channel: &Channel) -> Result<()> { + pub fn start_moh(&self, _channel_id: &str) -> Result<()> { unimplemented!() } - pub fn stop_moh(&self, _channel: &Channel) -> Result<()> { + pub fn stop_moh(&self, _channel_id: &str) -> Result<()> { unimplemented!() } - pub fn start_silence(&self, _channel: &Channel) -> Result<()> { + pub fn start_silence(&self, _channel_id: &str) -> Result<()> { unimplemented!() } - pub fn stop_silence(&self, _channel: &Channel) -> Result<()> { + pub fn stop_silence(&self, _channel_id: &str) -> Result<()> { unimplemented!() } pub async fn play_media( &self, - channel: &Channel, + channel_id: &str, media: &str, lang: Option<&str>, offset_ms: Option, skip_ms: Option, playback_id: Option<&str>, ) -> Result { - let mut url = self.url().join(&format!("channels/{}/play", channel.id))?; + let mut url = self.url().join(&format!("channels/{}/play", channel_id))?; let mut url = url.query_pairs_mut(); self.add_api_key(&mut url); url.append_pair("media", media); @@ -417,7 +417,7 @@ impl RequestClient { Level::INFO, "started media playback with id {} on channel with id {}", playback.id, - channel.id + channel_id ); Ok(playback) @@ -425,14 +425,14 @@ impl RequestClient { pub async fn play_media_with_id( &self, - channel: &Channel, + channel_id: &str, playback_id: &str, media: Vec<&str>, lang: Option<&str>, offset_ms: Option, skip_ms: Option, ) -> Result { - let mut url = self.url().join(&format!("channels/{}/play/{}/media", channel.id, playback_id))?; + let mut url = self.url().join(&format!("channels/{}/play/{}/media", channel_id, playback_id))?; let mut url = url.query_pairs_mut(); self.add_api_key(&mut url); @@ -462,7 +462,7 @@ impl RequestClient { Level::INFO, "started media playback with id {} on channel with id {}", playback.id, - channel.id + channel_id ); Ok(playback) @@ -471,7 +471,7 @@ impl RequestClient { #[allow(clippy::too_many_arguments)] pub async fn record( &self, - channel: &Channel, + channel_id: &str, name: &str, format: &str, max_duration_seconds: Option, @@ -480,7 +480,7 @@ impl RequestClient { beep: bool, terminate_on: RecordingTermination, ) -> Result { - let mut url = self.url().join(&format!("channels/{}/record", channel.id))?; + let mut url = self.url().join(&format!("channels/{}/record", channel_id))?; let mut url = url.query_pairs_mut(); self.add_api_key(&mut url); @@ -509,22 +509,22 @@ impl RequestClient { Level::INFO, "started recording with id {} on channel with id {}", recording.id, - channel.id + channel_id ); Ok(recording) } - pub fn get_variable(&self, _channel: &Channel) -> Result { + pub fn get_variable(&self, _channel_id: &str) -> Result { unimplemented!() } - pub fn set_variable(&self, _channel: &Channel) -> Result<()> { + pub fn set_variable(&self, _channel_id: &str) -> Result<()> { unimplemented!() } - pub async fn dial(&self, channel: &Channel, caller_id: Option<&str>, timeout: Option) -> Result<()> { - let mut url = self.url().join(&format!("channels/{}/dial", channel.id))?; + pub async fn dial(&self, channel_id: &str, caller_id: Option<&str>, timeout: Option) -> Result<()> { + let mut url = self.url().join(&format!("channels/{}/dial", channel_id))?; let mut url = url.query_pairs_mut(); self.add_api_key(&mut url); @@ -538,7 +538,7 @@ impl RequestClient { reqwest::Client::new().post(url.finish().to_owned()).send().await?; - event!(Level::INFO, "dialed channel with id {}", channel.id); + event!(Level::INFO, "dialed channel with id {}", channel_id); Ok(()) } @@ -621,7 +621,7 @@ impl RequestClient { .to_owned(); let channel = reqwest::get(url).await?.json::().await?; - event!(Level::INFO, "received channel with id {}", channel.id); + event!(Level::INFO, "received channel with id {}", channel_id); Ok(channel) } @@ -793,7 +793,7 @@ impl RequestClient { .json::() .await?; - event!(Level::INFO, "originated channel with id {}", channel.id); + event!(Level::INFO, "originated channel with id {}", channel_id); Ok(channel) } @@ -807,7 +807,7 @@ impl RequestClient { unimplemented!() } - pub fn get_rtp_statistics(&self, _channel: &Channel) -> Result { + pub fn get_rtp_statistics(&self, _channel_id: &str) -> Result { unimplemented!() } From 3994668ab458122e9e24d525a78a00fab1bdf9a2 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Fri, 13 Sep 2024 21:49:09 +0200 Subject: [PATCH 13/61] refactor!(ari/channel): make fields private and supply getters instead --- arirs/examples/dtmf.rs | 4 +- arirs/examples/list-channels.rs | 2 +- arirs/examples/playback.rs | 4 +- arirs/src/channel.rs | 153 ++++++++++++++++---------------- 4 files changed, 82 insertions(+), 81 deletions(-) diff --git a/arirs/examples/dtmf.rs b/arirs/examples/dtmf.rs index df59eb8..a5f8db7 100644 --- a/arirs/examples/dtmf.rs +++ b/arirs/examples/dtmf.rs @@ -18,8 +18,8 @@ async fn main() -> Result<(), Box> { while let Some(event) = event_listener.recv().await { match event { Event::ChannelDtmfReceived(event) => { - debug!("Received DTMF: {}", event.digit); - dtmf_buffer.lock().unwrap().push_str(&event.digit); + debug!("Received DTMF: {}", event.digit()); + dtmf_buffer.lock().unwrap().push_str(event.digit()); } Event::StasisEnd(_) => { debug!("Stasis ended, DTMF buffer: {}", dtmf_buffer.lock().unwrap()); diff --git a/arirs/examples/list-channels.rs b/arirs/examples/list-channels.rs index 68023b3..178a8b7 100644 --- a/arirs/examples/list-channels.rs +++ b/arirs/examples/list-channels.rs @@ -12,7 +12,7 @@ async fn main() -> Result<()> { let client = RequestClient::default(); for channel in client.list().await? { - debug!("Channel ID: {}", channel.id); + debug!("Channel ID: {}", channel.id()); } Ok(()) diff --git a/arirs/examples/playback.rs b/arirs/examples/playback.rs index bbe2ec2..027e708 100644 --- a/arirs/examples/playback.rs +++ b/arirs/examples/playback.rs @@ -10,9 +10,9 @@ async fn main() -> Result<(), Box> { while let Some(event) = event_listener.recv().await { if let Event::StasisStart(e) = event { - let channel = e.channel; + let channel = e.channel(); request_client - .play_media(&channel.id, "sound:hello", Some("en"), None, None, None) + .play_media(channel.id(), "sound:hello", Some("en"), None, None, None) .await?; } } diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index 0d97b8a..fd5b815 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use chrono::{DateTime, Duration}; +use derive_getters::Getters; use derive_more::Display; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -9,19 +10,19 @@ use url::Url; use crate::*; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Getters)] #[serde(rename_all = "snake_case")] pub struct Channel { - pub id: String, - pub name: String, - pub state: String, - pub protocol_id: String, - pub caller: Caller, - pub connected: Caller, - pub accountcode: String, - pub dialplan: Dialplan, - pub creationtime: String, - pub language: String, + id: String, + name: String, + state: String, + protocol_id: String, + caller: Caller, + connected: Caller, + accountcode: String, + dialplan: Dialplan, + creationtime: String, + language: String, } #[derive(Debug)] @@ -102,113 +103,113 @@ pub enum RecordingTermination { Octothorpe, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Getters)] #[serde(rename_all = "snake_case")] pub struct StasisStart { - pub timestamp: DateTime, - pub args: Vec, - pub channel: Channel, - pub asterisk_id: String, - pub application: String, + timestamp: DateTime, + args: Vec, + channel: Channel, + asterisk_id: String, + application: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Getters)] #[serde(rename_all = "snake_case")] pub struct StasisEnd { - pub timestamp: DateTime, - pub channel: Channel, - pub asterisk_id: String, - pub application: String, + timestamp: DateTime, + channel: Channel, + asterisk_id: String, + application: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelCreated { - pub timestamp: DateTime, - pub channel: Option, - pub asterisk_id: String, - pub application: String, + timestamp: DateTime, + channel: Option, + asterisk_id: String, + application: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelDestroyed { - pub timestamp: DateTime, - pub cause: i32, - pub cause_txt: String, - pub channel: Channel, - pub asterisk_id: String, - pub application: String, + timestamp: DateTime, + cause: i32, + cause_txt: String, + channel: Channel, + asterisk_id: String, + application: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelVarset { - pub timestamp: DateTime, - pub variable: String, - pub value: String, - pub channel: Option, - pub asterisk_id: String, - pub application: String, + timestamp: DateTime, + variable: String, + value: String, + channel: Option, + asterisk_id: String, + application: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelHangupRequest { - pub timestamp: DateTime, - pub soft: Option, - pub cause: i32, - pub channel: Channel, - pub asterisk_id: String, - pub application: String, + timestamp: DateTime, + soft: Option, + cause: i32, + channel: Channel, + asterisk_id: String, + application: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelDialplan { - pub timestamp: DateTime, - pub dialplan_app: String, - pub dialplan_app_data: String, - pub channel: Channel, - pub asterisk_id: String, - pub application: String, + timestamp: DateTime, + dialplan_app: String, + dialplan_app_data: String, + channel: Channel, + asterisk_id: String, + application: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelStateChange { - pub timestamp: DateTime, - pub channel: Channel, - pub asterisk_id: String, - pub application: String, + timestamp: DateTime, + channel: Channel, + asterisk_id: String, + application: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelDtmfReceived { - pub timestamp: DateTime, - pub digit: String, - pub duration_ms: i32, - pub channel: Channel, - pub asterisk_id: String, - pub application: String, + timestamp: DateTime, + digit: String, + duration_ms: i32, + channel: Channel, + asterisk_id: String, + application: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Getters)] #[serde(rename_all = "snake_case")] pub struct Caller { - pub name: String, - pub number: String, + name: String, + number: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Getters)] #[serde(rename_all = "snake_case")] pub struct Dialplan { - pub context: String, - pub exten: String, - pub priority: i32, - pub app_name: String, - pub app_data: String, + context: String, + exten: String, + priority: i32, + app_name: String, + app_data: String, } impl RequestClient { From 776aabe43135fc7adc8fd06afad88ba49dd7bea7 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 11:29:25 +0200 Subject: [PATCH 14/61] refactor!(ari/channel): initial `serde_qs` integration --- Cargo.lock | 47 +++ arirs/Cargo.toml | 2 + arirs/examples/originate.rs | 33 +- arirs/examples/playback.rs | 16 +- arirs/src/channel.rs | 661 ++++++++++++-------------------- arirs/src/request_client/mod.rs | 21 +- 6 files changed, 337 insertions(+), 443 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b265e3a..30a52ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,6 +62,8 @@ dependencies = [ "reqwest", "serde", "serde_json", + "serde_qs", + "strum", "thiserror", "tokio", "tokio-tungstenite", @@ -415,6 +417,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1069,6 +1077,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "ryu" version = "1.0.18" @@ -1145,6 +1159,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_qs" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd34f36fe4c5ba9654417139a9b3a20d2e1de6012ee678ad14d240c22c78d8d6" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1223,6 +1248,28 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "subtle" version = "2.6.1" diff --git a/arirs/Cargo.toml b/arirs/Cargo.toml index aba90f0..4fa3ec2 100644 --- a/arirs/Cargo.toml +++ b/arirs/Cargo.toml @@ -15,6 +15,8 @@ rand = "0.8.5" reqwest = { version = "0.12.7", features = ["json"] } serde = { version = "1.0.210", features = ["derive"] } serde_json = "1.0.128" +serde_qs = "0.13" +strum = { version = "0.26", features = ["derive"] } thiserror = "1.0.63" tokio = { version = "1.40.0", features = ["full"] } tokio-tungstenite = "0.23.1" diff --git a/arirs/examples/originate.rs b/arirs/examples/originate.rs index 392663e..fb23568 100644 --- a/arirs/examples/originate.rs +++ b/arirs/examples/originate.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use arirs::{OriginateParams, RequestClient, Result}; +use arirs::{OriginateChannelParams, OriginateParams, RequestClient, Result}; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; const APP_NAME: &str = "ari"; @@ -14,22 +14,21 @@ async fn main() -> Result<()> { let client = RequestClient::default(); - client - .originate( - "PJSIP/1000", - OriginateParams::Application { - app: APP_NAME, - app_args: vec![], - }, - None, - None, - None, - None, - None, - vec!["alaw,ulaw"], - HashMap::new(), - ) - .await?; + let originate_params = OriginateChannelParams { + endpoint: "PJSIP/1000", + params: OriginateParams::Application { + app: APP_NAME, + app_args: &[], + }, + caller_id: None, + timeout: None, + channel_id: None, + other_channel_id: None, + originator: None, + formats: &["alaw,ulaw"], + }; + + client.originate(originate_params, &HashMap::new()).await?; Ok(()) } diff --git a/arirs/examples/playback.rs b/arirs/examples/playback.rs index 027e708..4efd5e1 100644 --- a/arirs/examples/playback.rs +++ b/arirs/examples/playback.rs @@ -1,4 +1,4 @@ -use arirs::{Client, Event}; +use arirs::{Client, Event, PlayMediaParams}; use tracing::level_filters::LevelFilter; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; @@ -9,10 +9,18 @@ async fn main() -> Result<(), Box> { let (request_client, mut event_listener) = Client::connect("http://localhost:8088/", "asterisk", "asterisk", "ari").await?; while let Some(event) = event_listener.recv().await { - if let Event::StasisStart(e) = event { - let channel = e.channel(); + if let Event::StasisStart(event) = event { request_client - .play_media(channel.id(), "sound:hello", Some("en"), None, None, None) + .play_media( + event.channel().id(), + PlayMediaParams { + media: "sound:hello", + lang: Some("en"), + offset_ms: None, + skip_ms: None, + playback_id: None, + }, + ) .await?; } } diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index fd5b815..184d6d5 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -1,9 +1,9 @@ use std::collections::HashMap; -use chrono::{DateTime, Duration}; +use chrono::DateTime; use derive_getters::Getters; use derive_more::Display; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, Serializer}; use serde_json::json; use tracing::{event, Level}; use url::Url; @@ -25,7 +25,37 @@ pub struct Channel { language: String, } -#[derive(Debug)] +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OriginateChannelParams<'a> { + pub endpoint: &'a str, + pub params: OriginateParams<'a>, + pub caller_id: Option<&'a str>, + pub timeout: Option, + pub channel_id: Option<&'a str>, + pub other_channel_id: Option<&'a str>, + pub originator: Option<&'a str>, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + #[serde(serialize_with = "join_serialize")] + pub formats: &'a [&'a str], +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OriginateChannelWithIdParams<'a> { + pub endpoint: &'a str, + pub params: OriginateParams<'a>, + pub caller_id: Option<&'a str>, + pub timeout: Option, + pub other_channel_id: Option<&'a str>, + pub originator: Option<&'a str>, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + #[serde(serialize_with = "join_serialize")] + pub formats: &'a [&'a str], +} + +#[derive(Debug, Serialize)] +#[serde(untagged)] pub enum OriginateParams<'a> { Extension { extension: &'a str, @@ -35,40 +65,63 @@ pub enum OriginateParams<'a> { }, Application { app: &'a str, - app_args: Vec<&'a str>, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + #[serde(serialize_with = "join_serialize")] + app_args: &'a [&'a str], }, } -#[derive(Debug, Display)] -pub enum Reason { - #[display("{}", _0)] - Code(u16), - #[display("normal")] - Normal, - #[display("busy")] - Busy, - #[display("congestion")] - Congestion, - #[display("no_answer")] - NoAnswer, - #[display("timeout")] - Timeout, - #[display("rejected")] - Rejected, - #[display("unallocated")] - Unallocated, - #[display("normal_unspecified")] - NormalUnspecified, - #[display("number_incomplete")] - NumberIncomplete, - #[display("codec_mismatch")] - CodecMismatch, - #[display("interworking")] - Interworking, - #[display("failure")] - Failure, - #[display("answered_elsewhere")] - AnsweredElsewhere, +pub use reason::Reason; +mod reason { + use serde::ser::SerializeMap; + use strum::AsRefStr; + + use super::*; + + #[derive(Debug, AsRefStr)] + pub enum Reason { + Code(u16), + #[strum(serialize = "normal")] + Normal, + #[strum(serialize = "busy")] + Busy, + #[strum(serialize = "congestion")] + Congestion, + #[strum(serialize = "no_answer")] + NoAnswer, + #[strum(serialize = "timeout")] + Timeout, + #[strum(serialize = "rejected")] + Rejected, + #[strum(serialize = "unallocated")] + Unallocated, + #[strum(serialize = "normal_unspecified")] + NormalUnspecified, + #[strum(serialize = "number_incomplete")] + NumberIncomplete, + #[strum(serialize = "codec_mismatch")] + CodecMismatch, + #[strum(serialize = "interworking")] + Interworking, + #[strum(serialize = "failure")] + Failure, + #[strum(serialize = "answered_elsewhere")] + AnsweredElsewhere, + } + + impl Serialize for Reason { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + let mut map = serializer.serialize_map(Some(1))?; + match self { + Reason::Code(code) => map.serialize_entry("reason_code", code)?, + _ => map.serialize_entry("reason", self.as_ref())?, + }; + map.end() + } + } } #[derive(Debug, Display)] @@ -81,28 +134,6 @@ pub enum Direction { Both, } -#[derive(Debug, Display)] -pub enum RecordingAction { - #[display("overwrite")] - Overwrite, - #[display("append")] - Append, - #[display("fail")] - Fail, -} - -#[derive(Debug, Display)] -pub enum RecordingTermination { - #[display("none")] - None, - #[display("any")] - Any, - #[display("*")] - Asterisk, - #[display("#")] - Octothorpe, -} - #[derive(Serialize, Deserialize, Debug, Getters)] #[serde(rename_all = "snake_case")] pub struct StasisStart { @@ -212,18 +243,97 @@ pub struct Dialplan { app_data: String, } +#[derive(Serialize)] +pub struct SendDtmfParams<'a> { + pub dtmf: &'a str, + /// in milliseconds + pub between: Option, + /// in milliseconds + pub duration: Option, + pub before: Option, + pub after: Option, +} + +#[derive(Serialize)] +pub struct PlayMediaParams<'a> { + pub media: &'a str, + pub lang: Option<&'a str>, + pub offset_ms: Option, + pub skip_ms: Option, + pub playback_id: Option<&'a str>, +} + +#[derive(Serialize)] +pub struct PlayMediaWithIdParams<'a> { + #[serde(serialize_with = "join_serialize")] + pub media: &'a [&'a str], + pub lang: Option<&'a str>, + pub offset_ms: Option, + pub skip_ms: Option, +} + +fn join_serialize(slice: &[&str], s: S) -> std::result::Result +where + S: Serializer, +{ + s.serialize_str(&slice.join(",")) +} + +#[derive(Serialize)] +pub struct RecordParams<'a> { + pub name: &'a str, + pub format: &'a str, + pub max_duration_seconds: Option, + pub max_silence_seconds: Option, + pub if_exists: RecordingAction, + pub beep: bool, + pub terminate_on: RecordingTermination, +} +#[derive(Debug, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum RecordingAction { + Overwrite, + Append, + Fail, +} +#[derive(Debug, Serialize)] +pub enum RecordingTermination { + None, + Any, + #[serde(rename = "*")] + Asterisk, + #[serde(rename = "#")] + Octothorpe, +} + +#[derive(Serialize)] +pub struct DialParams<'a> { + pub caller: Option<&'a str>, + pub timeout: Option, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ChannelCreateParams<'a> { + pub endpoint: &'a str, + pub app: &'a str, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + #[serde(serialize_with = "join_serialize")] + pub app_args: &'a [&'a str], + pub channel_id: Option<&'a str>, + pub other_channel_id: Option<&'a str>, + pub originator: Option<&'a str>, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + #[serde(serialize_with = "join_serialize")] + pub formats: &'a [&'a str], +} + impl RequestClient { pub async fn hangup(&self, channel_id: &str, reason: Reason) -> Result<()> { let mut url = self.url().join(&format!("channels/{}", channel_id))?; - let mut url = url.query_pairs_mut(); - self.add_api_key(&mut url); + self.set_authorized_query_params(&mut url, reason); - match reason { - Reason::Code(_) => url.append_pair("reason_code", &format!("{}", reason)), - _ => url.append_pair("reason", &format!("{}", reason)), - }; - - reqwest::Client::new().delete(url.finish().to_owned()).send().await?; + reqwest::Client::new().delete(url).send().await?; event!(Level::INFO, "hung up channel with id {}", channel_id); Ok(()) @@ -271,34 +381,11 @@ impl RequestClient { Ok(()) } - pub async fn send_dtmf( - &self, - channel_id: &str, - dtmf: &str, - before: Option, - between: Option, - duration: Option, - after: Option, - ) -> Result<()> { + pub async fn send_dtmf(&self, channel_id: &str, params: SendDtmfParams<'_>) -> Result<()> { let mut url = self.url().join(&format!("channels/{}/dtmf", channel_id))?; - let mut url = url.query_pairs_mut(); - self.add_api_key(&mut url); - - url.append_pair("dtmf", dtmf) - .append_pair("between", &between.map(|d| d.num_milliseconds()).unwrap_or(100).to_string()) - .append_pair("duration", &duration.map(|d| d.num_milliseconds()).unwrap_or(100).to_string()); - - if let Some(before) = before { - url.append_pair("before", &before.num_milliseconds().to_string()); - } - - if let Some(after) = after { - url.append_pair("after", &after.num_milliseconds().to_string()); - } + self.set_authorized_query_params(&mut url, params); - reqwest::Client::new().post(url.finish().to_owned()).send().await?; - - event!(Level::INFO, "sent dtmf '{}' to channel with id {}", dtmf, channel_id); + reqwest::Client::new().post(url).send().await?; Ok(()) } @@ -377,142 +464,27 @@ impl RequestClient { unimplemented!() } - pub async fn play_media( - &self, - channel_id: &str, - media: &str, - lang: Option<&str>, - offset_ms: Option, - skip_ms: Option, - playback_id: Option<&str>, - ) -> Result { + pub async fn play_media(&self, channel_id: &str, params: PlayMediaParams<'_>) -> Result { let mut url = self.url().join(&format!("channels/{}/play", channel_id))?; - let mut url = url.query_pairs_mut(); - self.add_api_key(&mut url); - url.append_pair("media", media); - - if let Some(lang) = lang { - url.append_pair("lang", lang); - } - - if let Some(offset_ms) = offset_ms { - url.append_pair("offset_ms", &offset_ms.to_string()); - } - - if let Some(skip_ms) = skip_ms { - url.append_pair("skip_ms", &skip_ms.to_string()); - } - - if let Some(playback_id) = playback_id { - url.append_pair("playback_id", playback_id); - } - - let playback = reqwest::Client::new() - .post(url.finish().to_owned()) - .send() - .await? - .json::() - .await?; - - event!( - Level::INFO, - "started media playback with id {} on channel with id {}", - playback.id, - channel_id - ); + self.set_authorized_query_params(&mut url, params); + let playback = reqwest::Client::new().post(url).send().await?.json::().await?; Ok(playback) } - pub async fn play_media_with_id( - &self, - channel_id: &str, - playback_id: &str, - media: Vec<&str>, - lang: Option<&str>, - offset_ms: Option, - skip_ms: Option, - ) -> Result { + pub async fn play_media_with_id(&self, channel_id: &str, playback_id: &str, params: PlayMediaWithIdParams<'_>) -> Result { let mut url = self.url().join(&format!("channels/{}/play/{}/media", channel_id, playback_id))?; + self.set_authorized_query_params(&mut url, params); - let mut url = url.query_pairs_mut(); - self.add_api_key(&mut url); - let media = media.join(","); - url.append_pair("media", &media); - - if let Some(lang) = lang { - url.append_pair("lang", lang); - } - - if let Some(offset_ms) = offset_ms { - url.append_pair("offset_ms", &offset_ms.to_string()); - } - - if let Some(skip_ms) = skip_ms { - url.append_pair("skip_ms", &skip_ms.to_string()); - } - - let playback = reqwest::Client::new() - .post(url.finish().to_owned()) - .send() - .await? - .json::() - .await?; - - event!( - Level::INFO, - "started media playback with id {} on channel with id {}", - playback.id, - channel_id - ); - + let playback = reqwest::Client::new().post(url).send().await?.json().await?; Ok(playback) } - #[allow(clippy::too_many_arguments)] - pub async fn record( - &self, - channel_id: &str, - name: &str, - format: &str, - max_duration_seconds: Option, - max_silence_seconds: Option, - if_exists: RecordingAction, - beep: bool, - terminate_on: RecordingTermination, - ) -> Result { + pub async fn record(&self, channel_id: &str, params: RecordParams<'_>) -> Result { let mut url = self.url().join(&format!("channels/{}/record", channel_id))?; - let mut url = url.query_pairs_mut(); - self.add_api_key(&mut url); - - url.append_pair("name", name) - .append_pair("format", format) - .append_pair("if_exists", &format!("{}", if_exists)) - .append_pair("beep", &beep.to_string()) - .append_pair("terminate_on", &format!("{}", terminate_on)); - - if let Some(max_duration_seconds) = max_duration_seconds { - url.append_pair("max_duration_seconds", &max_duration_seconds.to_string()); - } - - if let Some(max_silence_seconds) = max_silence_seconds { - url.append_pair("max_silence_seconds", &max_silence_seconds.to_string()); - } - - let recording = reqwest::Client::new() - .post(url.finish().to_owned()) - .send() - .await? - .json::() - .await?; - - event!( - Level::INFO, - "started recording with id {} on channel with id {}", - recording.id, - channel_id - ); + self.set_authorized_query_params(&mut url, params); + let recording = reqwest::Client::new().post(url).send().await?.json().await?; Ok(recording) } @@ -524,22 +496,11 @@ impl RequestClient { unimplemented!() } - pub async fn dial(&self, channel_id: &str, caller_id: Option<&str>, timeout: Option) -> Result<()> { + pub async fn dial(&self, channel_id: &str, params: DialParams<'_>) -> Result<()> { let mut url = self.url().join(&format!("channels/{}/dial", channel_id))?; - let mut url = url.query_pairs_mut(); - self.add_api_key(&mut url); - - if let Some(caller_id) = caller_id { - url.append_pair("callerId", caller_id); - } - - if let Some(timeout) = timeout { - url.append_pair("timeout", &timeout.to_string()); - } + self.set_authorized_query_params(&mut url, params); - reqwest::Client::new().post(url.finish().to_owned()).send().await?; - - event!(Level::INFO, "dialed channel with id {}", channel_id); + reqwest::Client::new().post(url).send().await?; Ok(()) } @@ -557,58 +518,20 @@ impl RequestClient { Ok(channels) } - #[allow(clippy::too_many_arguments)] - pub async fn create( - &self, - endpoint: &str, - app: &str, - app_args: Vec<&str>, - channel_id: Option<&str>, - other_channel_id: Option<&str>, - originator: Option<&str>, - formats: Vec<&str>, - variables: HashMap<&str, &str>, - ) -> Result { + pub async fn create(&self, params: ChannelCreateParams<'_>, variables: &HashMap<&str, &str>) -> Result { let mut url = self.url().join("channels")?; - let mut url = url.query_pairs_mut(); - self.add_api_key(&mut url); - url.append_pair("endpoint", endpoint).append_pair("app", app); - - if !formats.is_empty() { - let formats = formats.join(","); - url.append_pair("formats", &formats); - } - - if !app_args.is_empty() { - let app_args = app_args.join(","); - url.append_pair("app_args", &app_args); - } - - if let Some(channel_id) = channel_id { - url.append_pair("channel_id", channel_id); - } - - if let Some(other_channel_id) = other_channel_id { - url.append_pair("other_channel_id", other_channel_id); - } - - if let Some(originator) = originator { - url.append_pair("originator", originator); - } - - let body = json!({ - "variables": variables - }); + self.set_authorized_query_params(&mut url, params); let channel = reqwest::Client::new() - .post(url.finish().to_owned()) - .json(&body) + .post(url) + .json(&json!({ + "variables": variables + })) .send() .await? - .json::() + .json() .await?; - event!(Level::INFO, "created channel with id {}", channel.id); Ok(channel) } @@ -626,175 +549,43 @@ impl RequestClient { Ok(channel) } - #[allow(clippy::too_many_arguments)] - pub async fn originate<'a>( - &self, - endpoint: &str, - params: OriginateParams<'a>, - caller_id: Option<&str>, - timeout: Option, - channel_id: Option<&str>, - other_channel_id: Option<&str>, - originator: Option<&str>, - formats: Vec<&str>, - variables: HashMap<&str, &str>, - ) -> Result { + pub async fn originate<'a>(&self, params: OriginateChannelParams<'a>, variables: &HashMap<&str, &str>) -> Result { let mut url = self.url().join("channels")?; - let mut url = url.query_pairs_mut(); - self.add_api_key(&mut url); - - url.append_pair("endpoint", endpoint) - .append_pair("timeout", &timeout.unwrap_or(30).to_string()); - - if !formats.is_empty() { - let formats = formats.join(","); - url.append_pair("formats", &formats); - } - - match params { - OriginateParams::Extension { - extension, - context, - priority, - label, - } => { - url.append_pair("extension", extension); - - if let Some(context) = context { - url.append_pair("context", context); - } - - if let Some(priority) = priority { - url.append_pair("priority", &priority.to_string()); - } - - if let Some(label) = label { - url.append_pair("label", label); - } - } - OriginateParams::Application { app, app_args } => { - url.append_pair("app", app); - - if !app_args.is_empty() { - let app_args = app_args.join(","); - url.append_pair("app_args", &app_args); - } - } - } - - if let Some(caller_id) = caller_id { - url.append_pair("callerId", caller_id); - } - - if let Some(channel_id) = channel_id { - url.append_pair("channel_id", channel_id); - } - - if let Some(other_channel_id) = other_channel_id { - url.append_pair("other_channel_id", other_channel_id); - } - - if let Some(originator) = originator { - url.append_pair("originator", originator); - } - let body = json!({ - "variables": variables - }); + self.set_authorized_query_params(&mut url, params); let channel = reqwest::Client::new() - .post(url.finish().to_owned()) - .json(&body) + .post(url) + .json(&json!({ + "variables": variables + })) .send() .await? - .json::() + .json() .await?; - event!(Level::INFO, "originated channel with id {}", channel.id); Ok(channel) } - #[allow(clippy::too_many_arguments)] pub async fn originate_with_id<'a>( &self, channel_id: &str, - endpoint: &str, - params: OriginateParams<'a>, - caller_id: Option<&str>, - timeout: Option, - other_channel_id: Option<&str>, - originator: Option<&str>, - formats: Vec<&str>, - variables: HashMap<&str, &str>, + params: OriginateChannelWithIdParams<'a>, + variables: &HashMap<&str, &str>, ) -> Result { let mut url = self.url().join(&format!("channels/{}", channel_id))?; - let mut url = url.query_pairs_mut(); - self.add_api_key(&mut url); - - url.append_pair("endpoint", endpoint) - .append_pair("timeout", &timeout.unwrap_or(30).to_string()); - - if !formats.is_empty() { - let formats = formats.join(","); - url.append_pair("formats", &formats); - } - - match params { - OriginateParams::Extension { - extension, - context, - priority, - label, - } => { - url.append_pair("extension", extension); - - if let Some(context) = context { - url.append_pair("context", context); - } - - if let Some(priority) = priority { - url.append_pair("priority", &priority.to_string()); - } - - if let Some(label) = label { - url.append_pair("label", label); - } - } - OriginateParams::Application { app, app_args } => { - url.append_pair("app", app); - - if !app_args.is_empty() { - let app_args = app_args.join(","); - url.append_pair("app_args", &app_args); - } - } - } - - if let Some(caller_id) = caller_id { - url.append_pair("callerId", caller_id); - } - - if let Some(other_channel_id) = other_channel_id { - url.append_pair("otherChannelId", other_channel_id); - } - - if let Some(originator) = originator { - url.append_pair("originator", originator); - } - - let body = json!({ - "variables": variables - }); + self.set_authorized_query_params(&mut url, params); let channel = reqwest::Client::new() - .post(url.finish().to_owned()) - .json(&body) + .post(url) + .json(&json!({ + "variables": variables + })) .send() .await? - .json::() + .json() .await?; - event!(Level::INFO, "originated channel with id {}", channel_id); Ok(channel) } @@ -824,3 +615,33 @@ impl RequestClient { unimplemented!() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn serializes_parameters() { + let request_client = RequestClient { + url: "http://localhost:8080/".parse().unwrap(), + username: "asterisk".to_string(), + password: "asterisk".to_string(), + }; + + let mut url = request_client.url().join("channel").unwrap(); + + request_client.set_authorized_query_params( + &mut url, + PlayMediaParams { + media: "sound:hello", + lang: Some("en"), + offset_ms: None, + skip_ms: None, + playback_id: None, + }, + ); + + let expected = "http://localhost:8080/channel?api_key=asterisk%3Aasterisk&media=sound%3Ahello&lang=en"; + assert_eq!(expected, url.as_str()) + } +} diff --git a/arirs/src/request_client/mod.rs b/arirs/src/request_client/mod.rs index 1de7596..4f34a5a 100644 --- a/arirs/src/request_client/mod.rs +++ b/arirs/src/request_client/mod.rs @@ -1,8 +1,16 @@ pub use core::RequestClient; mod core { use derive_getters::Getters; + use serde::Serialize; use url::Url; + #[derive(Serialize)] + pub struct AuthorizedRequest { + api_key: String, + #[serde(flatten)] + inner: T, + } + #[derive(Debug, Getters)] pub struct RequestClient { pub(crate) url: Url, @@ -11,12 +19,21 @@ mod core { } impl RequestClient { + pub(crate) fn authorize_request(&self, inner: T) -> AuthorizedRequest { + AuthorizedRequest { + api_key: self.get_api_key(), + inner, + } + } + pub(crate) fn get_api_key(&self) -> String { format!("{}:{}", self.username, self.password) } - pub(crate) fn add_api_key(&self, url: &mut url::form_urlencoded::Serializer) { - url.append_pair("api_key", &self.get_api_key()); + pub(crate) fn set_authorized_query_params(&self, url: &mut Url, params: T) { + let authorized_request_params = self.authorize_request(params); + let query_string = serde_qs::to_string(&authorized_request_params).expect("failed to serialize query parameters"); + url.set_query(Some(&query_string)); } } } From 380ad356b64c81de7ad8b24d76a50a904084ea45 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 11:36:48 +0200 Subject: [PATCH 15/61] refactor!(ari/channel): continued `serde_qs` integration --- arirs/src/channel.rs | 89 ++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 56 deletions(-) diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index 184d6d5..c924803 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use chrono::DateTime; use derive_getters::Getters; -use derive_more::Display; use serde::{Deserialize, Serialize, Serializer}; use serde_json::json; use tracing::{event, Level}; @@ -124,13 +123,12 @@ mod reason { } } -#[derive(Debug, Display)] +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +#[serde(tag = "direction")] pub enum Direction { - #[display("in")] In, - #[display("out")] Out, - #[display("both")] Both, } @@ -340,13 +338,8 @@ impl RequestClient { } pub async fn answer(&self, channel_id: &str) -> Result<()> { - let url = self - .url() - .join(&format!("channels/{}/answer", channel_id))? - .query_pairs_mut() - .append_pair("api_key", &self.get_api_key()) - .finish() - .to_owned(); + let mut url = self.url().join(&format!("channels/{}/answer", channel_id))?; + self.set_authorized_query_params(&mut url, ()); reqwest::Client::new().post(url).send().await?; event!(Level::INFO, "answered channel with id {}", channel_id); @@ -354,13 +347,8 @@ impl RequestClient { } pub async fn start_ringing(&self, channel_id: &str) -> Result<()> { - let url = self - .url() - .join(&format!("channels/{}/ring", channel_id))? - .query_pairs_mut() - .append_pair("api_key", &self.get_api_key()) - .finish() - .to_owned(); + let mut url = self.url().join(&format!("channels/{}/ring", channel_id))?; + self.set_authorized_query_params(&mut url, ()); reqwest::Client::new().post(url).send().await?; event!(Level::INFO, "started ringing channel with id {}", channel_id); @@ -368,13 +356,8 @@ impl RequestClient { } pub async fn stop_ringing(&self, channel_id: &str) -> Result<()> { - let url = self - .url() - .join(&format!("channels/{}/ring", channel_id))? - .query_pairs_mut() - .append_pair("api_key", &self.get_api_key()) - .finish() - .to_owned(); + let mut url = self.url().join(&format!("channels/{}/ring", channel_id))?; + self.set_authorized_query_params(&mut url, ()); reqwest::Client::new().delete(url).send().await?; event!(Level::INFO, "stopped ringing channel with id {}", channel_id); @@ -391,14 +374,8 @@ impl RequestClient { } pub async fn mute(&self, channel_id: &str, direction: Direction) -> Result<()> { - let url = self - .url() - .join(&format!("channels/{}/mute", channel_id))? - .query_pairs_mut() - .append_pair("api_key", &self.get_api_key()) - .append_pair("direction", &format!("{}", direction)) - .finish() - .to_owned(); + let mut url = self.url().join(&format!("channels/{}/mute", channel_id))?; + self.set_authorized_query_params(&mut url, direction); reqwest::Client::new().post(url).send().await?; event!(Level::INFO, "muted channel with id {}", channel_id); @@ -406,14 +383,8 @@ impl RequestClient { } pub async fn unmute(&self, channel_id: &str, direction: Direction) -> Result<()> { - let url = self - .url() - .join(&format!("channels/{}/mute", channel_id))? - .query_pairs_mut() - .append_pair("api_key", &self.get_api_key()) - .append_pair("direction", &format!("{}", direction)) - .finish() - .to_owned(); + let mut url = self.url().join(&format!("channels/{}/mute", channel_id))?; + self.set_authorized_query_params(&mut url, direction); reqwest::Client::new().delete(url).send().await?; event!(Level::INFO, "unmuted channel with id {}", channel_id); @@ -421,13 +392,8 @@ impl RequestClient { } pub async fn hold(&self, channel_id: &str) -> Result<()> { - let url = self - .url() - .join(&format!("channels/{}/hold", channel_id))? - .query_pairs_mut() - .append_pair("api_key", &self.get_api_key()) - .finish() - .to_owned(); + let mut url = self.url().join(&format!("channels/{}/hold", channel_id))?; + self.set_authorized_query_params(&mut url, ()); reqwest::Client::new().post(url).send().await?; event!(Level::INFO, "started hold on channel with id {}", channel_id); @@ -435,13 +401,8 @@ impl RequestClient { } pub async fn unhold(&self, channel_id: &str) -> Result<()> { - let url = self - .url() - .join(&format!("channels/{}/hold", channel_id))? - .query_pairs_mut() - .append_pair("api_key", &self.get_api_key()) - .finish() - .to_owned(); + let mut url = self.url().join(&format!("channels/{}/ring", channel_id))?; + self.set_authorized_query_params(&mut url, ()); reqwest::Client::new().delete(url).send().await?; event!(Level::INFO, "stopped hold on channel with id {}", channel_id); @@ -644,4 +605,20 @@ mod tests { let expected = "http://localhost:8080/channel?api_key=asterisk%3Aasterisk&media=sound%3Ahello&lang=en"; assert_eq!(expected, url.as_str()) } + + #[test] + fn serializes_unit_type() { + let request_client = RequestClient { + url: "http://localhost:8080/".parse().unwrap(), + username: "asterisk".to_string(), + password: "asterisk".to_string(), + }; + + let mut url = request_client.url().join("channel").unwrap(); + + request_client.set_authorized_query_params(&mut url, ()); + + let expected = "http://localhost:8080/channel?api_key=asterisk%3Aasterisk"; + assert_eq!(expected, url.as_str()) + } } From b6d70ca25af0f7e6cd07da10418f61212f360379 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 11:40:21 +0200 Subject: [PATCH 16/61] feat!(ari/channel): remove tracing event logging Libraries should avoid locking in logging solutions. Future work could instead instrument the function behind a feature flag. --- arirs/src/channel.rs | 58 ++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index c924803..25b2e76 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -4,7 +4,6 @@ use chrono::DateTime; use derive_getters::Getters; use serde::{Deserialize, Serialize, Serializer}; use serde_json::json; -use tracing::{event, Level}; use url::Url; use crate::*; @@ -333,7 +332,6 @@ impl RequestClient { reqwest::Client::new().delete(url).send().await?; - event!(Level::INFO, "hung up channel with id {}", channel_id); Ok(()) } @@ -342,7 +340,6 @@ impl RequestClient { self.set_authorized_query_params(&mut url, ()); reqwest::Client::new().post(url).send().await?; - event!(Level::INFO, "answered channel with id {}", channel_id); Ok(()) } @@ -351,7 +348,6 @@ impl RequestClient { self.set_authorized_query_params(&mut url, ()); reqwest::Client::new().post(url).send().await?; - event!(Level::INFO, "started ringing channel with id {}", channel_id); Ok(()) } @@ -360,7 +356,6 @@ impl RequestClient { self.set_authorized_query_params(&mut url, ()); reqwest::Client::new().delete(url).send().await?; - event!(Level::INFO, "stopped ringing channel with id {}", channel_id); Ok(()) } @@ -378,7 +373,6 @@ impl RequestClient { self.set_authorized_query_params(&mut url, direction); reqwest::Client::new().post(url).send().await?; - event!(Level::INFO, "muted channel with id {}", channel_id); Ok(()) } @@ -387,7 +381,6 @@ impl RequestClient { self.set_authorized_query_params(&mut url, direction); reqwest::Client::new().delete(url).send().await?; - event!(Level::INFO, "unmuted channel with id {}", channel_id); Ok(()) } @@ -396,7 +389,6 @@ impl RequestClient { self.set_authorized_query_params(&mut url, ()); reqwest::Client::new().post(url).send().await?; - event!(Level::INFO, "started hold on channel with id {}", channel_id); Ok(()) } @@ -405,26 +397,9 @@ impl RequestClient { self.set_authorized_query_params(&mut url, ()); reqwest::Client::new().delete(url).send().await?; - event!(Level::INFO, "stopped hold on channel with id {}", channel_id); Ok(()) } - pub fn start_moh(&self, _channel_id: &str) -> Result<()> { - unimplemented!() - } - - pub fn stop_moh(&self, _channel_id: &str) -> Result<()> { - unimplemented!() - } - - pub fn start_silence(&self, _channel_id: &str) -> Result<()> { - unimplemented!() - } - - pub fn stop_silence(&self, _channel_id: &str) -> Result<()> { - unimplemented!() - } - pub async fn play_media(&self, channel_id: &str, params: PlayMediaParams<'_>) -> Result { let mut url = self.url().join(&format!("channels/{}/play", channel_id))?; self.set_authorized_query_params(&mut url, params); @@ -449,14 +424,6 @@ impl RequestClient { Ok(recording) } - pub fn get_variable(&self, _channel_id: &str) -> Result { - unimplemented!() - } - - pub fn set_variable(&self, _channel_id: &str) -> Result<()> { - unimplemented!() - } - pub async fn dial(&self, channel_id: &str, params: DialParams<'_>) -> Result<()> { let mut url = self.url().join(&format!("channels/{}/dial", channel_id))?; self.set_authorized_query_params(&mut url, params); @@ -475,7 +442,6 @@ impl RequestClient { .to_owned(); let channels = reqwest::get(url).await?.json::>().await?; - event!(Level::INFO, "received channels"); Ok(channels) } @@ -506,7 +472,6 @@ impl RequestClient { .to_owned(); let channel = reqwest::get(url).await?.json::().await?; - event!(Level::INFO, "received channel with id {}", channel_id); Ok(channel) } @@ -550,6 +515,29 @@ impl RequestClient { Ok(channel) } + pub fn start_moh(&self, _channel_id: &str) -> Result<()> { + unimplemented!() + } + + pub fn stop_moh(&self, _channel_id: &str) -> Result<()> { + unimplemented!() + } + + pub fn start_silence(&self, _channel_id: &str) -> Result<()> { + unimplemented!() + } + + pub fn stop_silence(&self, _channel_id: &str) -> Result<()> { + unimplemented!() + } + pub fn get_variable(&self, _channel_id: &str) -> Result { + unimplemented!() + } + + pub fn set_variable(&self, _channel_id: &str) -> Result<()> { + unimplemented!() + } + pub fn continue_in_dialplan(&self, _channel_id: &str) -> Result<()> { unimplemented!() } From a179808856208ec30f79113f1217ac814d287ae6 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 11:55:04 +0200 Subject: [PATCH 17/61] perf(ari/channel): avoid recreating `reqwest::Client` on every request --- Cargo.lock | 7 ----- arirs/Cargo.toml | 2 +- arirs/src/channel.rs | 47 +++++++++++++++------------------ arirs/src/client.rs | 15 +---------- arirs/src/playback.rs | 8 +----- arirs/src/request_client/mod.rs | 25 +++++++++++++++++- 6 files changed, 48 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30a52ec..d88eb74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -238,7 +238,6 @@ dependencies = [ "proc-macro2", "quote", "syn", - "unicode-xid", ] [[package]] @@ -1589,12 +1588,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-xid" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" - [[package]] name = "untrusted" version = "0.9.0" diff --git a/arirs/Cargo.toml b/arirs/Cargo.toml index 4fa3ec2..f4fbac9 100644 --- a/arirs/Cargo.toml +++ b/arirs/Cargo.toml @@ -8,7 +8,7 @@ version = "0.1.0" [dependencies] chrono = { version = "0.4.38", features = ["serde"] } derive-getters = "0.5" -derive_more = { version = "1.0.0", features = ["display"] } +derive_more = { version = "1.0.0", features = ["as_ref"] } futures-channel = "0.3.30" futures-util = "0.3.30" rand = "0.8.5" diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index 25b2e76..dd01f4c 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -330,7 +330,7 @@ impl RequestClient { let mut url = self.url().join(&format!("channels/{}", channel_id))?; self.set_authorized_query_params(&mut url, reason); - reqwest::Client::new().delete(url).send().await?; + self.as_ref().delete(url).send().await?; Ok(()) } @@ -339,7 +339,7 @@ impl RequestClient { let mut url = self.url().join(&format!("channels/{}/answer", channel_id))?; self.set_authorized_query_params(&mut url, ()); - reqwest::Client::new().post(url).send().await?; + self.as_ref().post(url).send().await?; Ok(()) } @@ -347,7 +347,7 @@ impl RequestClient { let mut url = self.url().join(&format!("channels/{}/ring", channel_id))?; self.set_authorized_query_params(&mut url, ()); - reqwest::Client::new().post(url).send().await?; + self.as_ref().post(url).send().await?; Ok(()) } @@ -355,7 +355,7 @@ impl RequestClient { let mut url = self.url().join(&format!("channels/{}/ring", channel_id))?; self.set_authorized_query_params(&mut url, ()); - reqwest::Client::new().delete(url).send().await?; + self.as_ref().delete(url).send().await?; Ok(()) } @@ -363,7 +363,7 @@ impl RequestClient { let mut url = self.url().join(&format!("channels/{}/dtmf", channel_id))?; self.set_authorized_query_params(&mut url, params); - reqwest::Client::new().post(url).send().await?; + self.as_ref().post(url).send().await?; Ok(()) } @@ -372,7 +372,7 @@ impl RequestClient { let mut url = self.url().join(&format!("channels/{}/mute", channel_id))?; self.set_authorized_query_params(&mut url, direction); - reqwest::Client::new().post(url).send().await?; + self.as_ref().post(url).send().await?; Ok(()) } @@ -380,7 +380,7 @@ impl RequestClient { let mut url = self.url().join(&format!("channels/{}/mute", channel_id))?; self.set_authorized_query_params(&mut url, direction); - reqwest::Client::new().delete(url).send().await?; + self.as_ref().delete(url).send().await?; Ok(()) } @@ -388,7 +388,7 @@ impl RequestClient { let mut url = self.url().join(&format!("channels/{}/hold", channel_id))?; self.set_authorized_query_params(&mut url, ()); - reqwest::Client::new().post(url).send().await?; + self.as_ref().post(url).send().await?; Ok(()) } @@ -396,7 +396,7 @@ impl RequestClient { let mut url = self.url().join(&format!("channels/{}/ring", channel_id))?; self.set_authorized_query_params(&mut url, ()); - reqwest::Client::new().delete(url).send().await?; + self.as_ref().delete(url).send().await?; Ok(()) } @@ -404,7 +404,7 @@ impl RequestClient { let mut url = self.url().join(&format!("channels/{}/play", channel_id))?; self.set_authorized_query_params(&mut url, params); - let playback = reqwest::Client::new().post(url).send().await?.json::().await?; + let playback = self.as_ref().post(url).send().await?.json::().await?; Ok(playback) } @@ -412,7 +412,7 @@ impl RequestClient { let mut url = self.url().join(&format!("channels/{}/play/{}/media", channel_id, playback_id))?; self.set_authorized_query_params(&mut url, params); - let playback = reqwest::Client::new().post(url).send().await?.json().await?; + let playback = self.as_ref().post(url).send().await?.json().await?; Ok(playback) } @@ -420,7 +420,7 @@ impl RequestClient { let mut url = self.url().join(&format!("channels/{}/record", channel_id))?; self.set_authorized_query_params(&mut url, params); - let recording = reqwest::Client::new().post(url).send().await?.json().await?; + let recording = self.as_ref().post(url).send().await?.json().await?; Ok(recording) } @@ -428,7 +428,7 @@ impl RequestClient { let mut url = self.url().join(&format!("channels/{}/dial", channel_id))?; self.set_authorized_query_params(&mut url, params); - reqwest::Client::new().post(url).send().await?; + self.as_ref().post(url).send().await?; Ok(()) } @@ -449,7 +449,8 @@ impl RequestClient { let mut url = self.url().join("channels")?; self.set_authorized_query_params(&mut url, params); - let channel = reqwest::Client::new() + let channel = self + .as_ref() .post(url) .json(&json!({ "variables": variables @@ -480,7 +481,8 @@ impl RequestClient { self.set_authorized_query_params(&mut url, params); - let channel = reqwest::Client::new() + let channel = self + .as_ref() .post(url) .json(&json!({ "variables": variables @@ -502,7 +504,8 @@ impl RequestClient { let mut url = self.url().join(&format!("channels/{}", channel_id))?; self.set_authorized_query_params(&mut url, params); - let channel = reqwest::Client::new() + let channel = self + .as_ref() .post(url) .json(&json!({ "variables": variables @@ -571,11 +574,7 @@ mod tests { #[test] fn serializes_parameters() { - let request_client = RequestClient { - url: "http://localhost:8080/".parse().unwrap(), - username: "asterisk".to_string(), - password: "asterisk".to_string(), - }; + let request_client = RequestClient::new("http://localhost:8080/".parse().unwrap(), "asterisk", "asterisk"); let mut url = request_client.url().join("channel").unwrap(); @@ -596,11 +595,7 @@ mod tests { #[test] fn serializes_unit_type() { - let request_client = RequestClient { - url: "http://localhost:8080/".parse().unwrap(), - username: "asterisk".to_string(), - password: "asterisk".to_string(), - }; + let request_client = RequestClient::new("http://localhost:8080/".parse().unwrap(), "asterisk", "asterisk"); let mut url = request_client.url().join("channel").unwrap(); diff --git a/arirs/src/client.rs b/arirs/src/client.rs index 225bcfc..81a21a0 100644 --- a/arirs/src/client.rs +++ b/arirs/src/client.rs @@ -40,7 +40,7 @@ impl Client { .append_pair("api_key", &format!("{}:{}", username, password)) .append_pair("subscribeAll", "true"); - let request_client = RequestClient { url, username, password }; + let request_client = RequestClient::new(url, username, password); let event_listener = Self::connect_ws(ws_url).await?; @@ -106,16 +106,3 @@ impl Client { Ok(rx) } } - -impl Default for RequestClient { - fn default() -> Self { - Self { - url: match Url::parse("http://localhost:8088/") { - Ok(url) => url, - Err(_) => panic!("Failed to parse URL"), - }, - username: "asterisk".to_string(), - password: "asterisk".to_string(), - } - } -} diff --git a/arirs/src/playback.rs b/arirs/src/playback.rs index d785a45..13b27d1 100644 --- a/arirs/src/playback.rs +++ b/arirs/src/playback.rs @@ -1,4 +1,3 @@ -use derive_more::Display; use serde::{Deserialize, Serialize}; use crate::*; @@ -23,17 +22,12 @@ impl Playback { } } -#[derive(Serialize, Deserialize, Debug, Display)] +#[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "snake_case")] pub enum Operation { - #[display("restart")] Restart, - #[display("pause")] Pause, - #[display("unpause")] Unpause, - #[display("reverse")] Reverse, - #[display("forward")] Forward, } diff --git a/arirs/src/request_client/mod.rs b/arirs/src/request_client/mod.rs index 4f34a5a..3913b10 100644 --- a/arirs/src/request_client/mod.rs +++ b/arirs/src/request_client/mod.rs @@ -1,6 +1,7 @@ pub use core::RequestClient; mod core { use derive_getters::Getters; + use derive_more::AsRef; use serde::Serialize; use url::Url; @@ -11,14 +12,25 @@ mod core { inner: T, } - #[derive(Debug, Getters)] + #[derive(Debug, Getters, AsRef)] pub struct RequestClient { pub(crate) url: Url, pub(crate) username: String, pub(crate) password: String, + #[as_ref] + inner: reqwest::Client, } impl RequestClient { + pub(crate) fn new(url: Url, username: impl Into, password: impl Into) -> Self { + Self { + url, + username: username.into(), + password: password.into(), + inner: reqwest::Client::new(), + } + } + pub(crate) fn authorize_request(&self, inner: T) -> AuthorizedRequest { AuthorizedRequest { api_key: self.get_api_key(), @@ -36,4 +48,15 @@ mod core { url.set_query(Some(&query_string)); } } + + impl Default for RequestClient { + fn default() -> Self { + Self { + url: "http://localhost:8088/".parse().expect("failed to parse url"), + username: "asterisk".to_string(), + password: "asterisk".to_string(), + inner: reqwest::Client::new(), + } + } + } } From ee6c5e281861dff8fb43316841c566b569585c27 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 12:05:46 +0200 Subject: [PATCH 18/61] refactor(ari): add `RequestClient.authorized_delete` --- arirs/src/channel.rs | 37 +++++++-------------------------- arirs/src/request_client/mod.rs | 12 +++++++++++ 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index dd01f4c..506126b 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -4,7 +4,6 @@ use chrono::DateTime; use derive_getters::Getters; use serde::{Deserialize, Serialize, Serializer}; use serde_json::json; -use url::Url; use crate::*; @@ -327,12 +326,7 @@ pub struct ChannelCreateParams<'a> { impl RequestClient { pub async fn hangup(&self, channel_id: &str, reason: Reason) -> Result<()> { - let mut url = self.url().join(&format!("channels/{}", channel_id))?; - self.set_authorized_query_params(&mut url, reason); - - self.as_ref().delete(url).send().await?; - - Ok(()) + self.authorized_delete(["channels", channel_id], reason).await } pub async fn answer(&self, channel_id: &str) -> Result<()> { @@ -352,11 +346,7 @@ impl RequestClient { } pub async fn stop_ringing(&self, channel_id: &str) -> Result<()> { - let mut url = self.url().join(&format!("channels/{}/ring", channel_id))?; - self.set_authorized_query_params(&mut url, ()); - - self.as_ref().delete(url).send().await?; - Ok(()) + self.authorized_delete(["channels", channel_id, "ring"], ()).await } pub async fn send_dtmf(&self, channel_id: &str, params: SendDtmfParams<'_>) -> Result<()> { @@ -377,11 +367,7 @@ impl RequestClient { } pub async fn unmute(&self, channel_id: &str, direction: Direction) -> Result<()> { - let mut url = self.url().join(&format!("channels/{}/mute", channel_id))?; - self.set_authorized_query_params(&mut url, direction); - - self.as_ref().delete(url).send().await?; - Ok(()) + self.authorized_delete(["channels", channel_id, "mute"], direction).await } pub async fn hold(&self, channel_id: &str) -> Result<()> { @@ -393,11 +379,7 @@ impl RequestClient { } pub async fn unhold(&self, channel_id: &str) -> Result<()> { - let mut url = self.url().join(&format!("channels/{}/ring", channel_id))?; - self.set_authorized_query_params(&mut url, ()); - - self.as_ref().delete(url).send().await?; - Ok(()) + self.authorized_delete(["channels", channel_id, "ring"], ()).await } pub async fn play_media(&self, channel_id: &str, params: PlayMediaParams<'_>) -> Result { @@ -433,15 +415,10 @@ impl RequestClient { } pub async fn list(&self) -> Result> { - let url: Url = self - .url() - .join("channels")? - .query_pairs_mut() - .append_pair("api_key", &self.get_api_key()) - .finish() - .to_owned(); + let mut url = self.url().join("channels")?; + self.set_authorized_query_params(&mut url, ()); - let channels = reqwest::get(url).await?.json::>().await?; + let channels = self.as_ref().get(url).send().await?.json().await?; Ok(channels) } diff --git a/arirs/src/request_client/mod.rs b/arirs/src/request_client/mod.rs index 3913b10..4f337ac 100644 --- a/arirs/src/request_client/mod.rs +++ b/arirs/src/request_client/mod.rs @@ -5,6 +5,8 @@ mod core { use serde::Serialize; use url::Url; + use crate::*; + #[derive(Serialize)] pub struct AuthorizedRequest { api_key: String, @@ -31,6 +33,16 @@ mod core { } } + pub(crate) async fn authorized_delete(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { + let mut url = self.url().join(&path.as_ref().join("/"))?; + + self.set_authorized_query_params(&mut url, params); + + self.as_ref().delete(url).send().await?; + + Ok(()) + } + pub(crate) fn authorize_request(&self, inner: T) -> AuthorizedRequest { AuthorizedRequest { api_key: self.get_api_key(), From ae76bfa59e90e49319403ab8e0ee59bc29a2c67d Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 12:14:46 +0200 Subject: [PATCH 19/61] refactor(ari): add `RequestClient.authorized_post` --- arirs/src/channel.rs | 45 ++++++++------------------------- arirs/src/request_client/mod.rs | 18 +++++++++---- 2 files changed, 23 insertions(+), 40 deletions(-) diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index 506126b..39668a4 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -325,24 +325,16 @@ pub struct ChannelCreateParams<'a> { } impl RequestClient { - pub async fn hangup(&self, channel_id: &str, reason: Reason) -> Result<()> { - self.authorized_delete(["channels", channel_id], reason).await - } - pub async fn answer(&self, channel_id: &str) -> Result<()> { - let mut url = self.url().join(&format!("channels/{}/answer", channel_id))?; - self.set_authorized_query_params(&mut url, ()); + self.authorized_post(["channels", channel_id, "answer"], ()).await + } - self.as_ref().post(url).send().await?; - Ok(()) + pub async fn hangup(&self, channel_id: &str, reason: Reason) -> Result<()> { + self.authorized_delete(["channels", channel_id], reason).await } pub async fn start_ringing(&self, channel_id: &str) -> Result<()> { - let mut url = self.url().join(&format!("channels/{}/ring", channel_id))?; - self.set_authorized_query_params(&mut url, ()); - - self.as_ref().post(url).send().await?; - Ok(()) + self.authorized_post(["channels", channel_id, "ring"], ()).await } pub async fn stop_ringing(&self, channel_id: &str) -> Result<()> { @@ -350,20 +342,11 @@ impl RequestClient { } pub async fn send_dtmf(&self, channel_id: &str, params: SendDtmfParams<'_>) -> Result<()> { - let mut url = self.url().join(&format!("channels/{}/dtmf", channel_id))?; - self.set_authorized_query_params(&mut url, params); - - self.as_ref().post(url).send().await?; - - Ok(()) + self.authorized_post(["channels", channel_id, "dtmf"], params).await } pub async fn mute(&self, channel_id: &str, direction: Direction) -> Result<()> { - let mut url = self.url().join(&format!("channels/{}/mute", channel_id))?; - self.set_authorized_query_params(&mut url, direction); - - self.as_ref().post(url).send().await?; - Ok(()) + self.authorized_post(["channels", channel_id, "mute"], direction).await } pub async fn unmute(&self, channel_id: &str, direction: Direction) -> Result<()> { @@ -371,15 +354,11 @@ impl RequestClient { } pub async fn hold(&self, channel_id: &str) -> Result<()> { - let mut url = self.url().join(&format!("channels/{}/hold", channel_id))?; - self.set_authorized_query_params(&mut url, ()); - - self.as_ref().post(url).send().await?; - Ok(()) + self.authorized_post(["channels", channel_id, "hold"], ()).await } pub async fn unhold(&self, channel_id: &str) -> Result<()> { - self.authorized_delete(["channels", channel_id, "ring"], ()).await + self.authorized_delete(["channels", channel_id, "hold"], ()).await } pub async fn play_media(&self, channel_id: &str, params: PlayMediaParams<'_>) -> Result { @@ -407,11 +386,7 @@ impl RequestClient { } pub async fn dial(&self, channel_id: &str, params: DialParams<'_>) -> Result<()> { - let mut url = self.url().join(&format!("channels/{}/dial", channel_id))?; - self.set_authorized_query_params(&mut url, params); - - self.as_ref().post(url).send().await?; - Ok(()) + self.authorized_post(["channels", channel_id, "dial"], params).await } pub async fn list(&self) -> Result> { diff --git a/arirs/src/request_client/mod.rs b/arirs/src/request_client/mod.rs index 4f337ac..a780670 100644 --- a/arirs/src/request_client/mod.rs +++ b/arirs/src/request_client/mod.rs @@ -33,16 +33,24 @@ mod core { } } - pub(crate) async fn authorized_delete(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { - let mut url = self.url().join(&path.as_ref().join("/"))?; - - self.set_authorized_query_params(&mut url, params); + pub(crate) async fn authorized_post(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { + let url = self.authorized_url(path, params)?; + self.as_ref().post(url).send().await?; + Ok(()) + } + pub(crate) async fn authorized_delete(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { + let url = self.authorized_url(path, params)?; self.as_ref().delete(url).send().await?; - Ok(()) } + fn authorized_url<'a, T: Serialize>(&self, path: impl AsRef<[&'a str]>, params: T) -> Result { + let mut url = self.url().join(&path.as_ref().join("/"))?; + self.set_authorized_query_params(&mut url, params); + Ok(url) + } + pub(crate) fn authorize_request(&self, inner: T) -> AuthorizedRequest { AuthorizedRequest { api_key: self.get_api_key(), From 1b32aca025eeb31d4c613a1d5da6ad6ec8d26fa9 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 12:19:44 +0200 Subject: [PATCH 20/61] refactor(ari): add `RequestClient.authorized_post_json_response` --- arirs/src/channel.rs | 19 ++++--------------- arirs/src/request_client/mod.rs | 12 +++++++++++- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index 39668a4..13d6e11 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -362,27 +362,16 @@ impl RequestClient { } pub async fn play_media(&self, channel_id: &str, params: PlayMediaParams<'_>) -> Result { - let mut url = self.url().join(&format!("channels/{}/play", channel_id))?; - self.set_authorized_query_params(&mut url, params); - - let playback = self.as_ref().post(url).send().await?.json::().await?; - Ok(playback) + self.authorized_post_json_response(["channels", channel_id, "play"], params).await } pub async fn play_media_with_id(&self, channel_id: &str, playback_id: &str, params: PlayMediaWithIdParams<'_>) -> Result { - let mut url = self.url().join(&format!("channels/{}/play/{}/media", channel_id, playback_id))?; - self.set_authorized_query_params(&mut url, params); - - let playback = self.as_ref().post(url).send().await?.json().await?; - Ok(playback) + self.authorized_post_json_response(["channels", channel_id, "play", playback_id, "media"], params) + .await } pub async fn record(&self, channel_id: &str, params: RecordParams<'_>) -> Result { - let mut url = self.url().join(&format!("channels/{}/record", channel_id))?; - self.set_authorized_query_params(&mut url, params); - - let recording = self.as_ref().post(url).send().await?.json().await?; - Ok(recording) + self.authorized_post_json_response(["channels", channel_id, "record"], params).await } pub async fn dial(&self, channel_id: &str, params: DialParams<'_>) -> Result<()> { diff --git a/arirs/src/request_client/mod.rs b/arirs/src/request_client/mod.rs index a780670..1a778a8 100644 --- a/arirs/src/request_client/mod.rs +++ b/arirs/src/request_client/mod.rs @@ -2,7 +2,7 @@ pub use core::RequestClient; mod core { use derive_getters::Getters; use derive_more::AsRef; - use serde::Serialize; + use serde::{de::DeserializeOwned, Serialize}; use url::Url; use crate::*; @@ -39,6 +39,16 @@ mod core { Ok(()) } + pub(crate) async fn authorized_post_json_response( + &self, + path: impl AsRef<[&str]>, + params: T, + ) -> Result { + let url = self.authorized_url(path, params)?; + let response = self.as_ref().post(url).send().await?.json().await?; + Ok(response) + } + pub(crate) async fn authorized_delete(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { let url = self.authorized_url(path, params)?; self.as_ref().delete(url).send().await?; From e4a3eb4f1378b2ecf61ffe12889333444764ff75 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 12:29:05 +0200 Subject: [PATCH 21/61] refactor(ari): add `RequestClient.authorized_post_variables` --- arirs/src/channel.rs | 50 ++------------------------------- arirs/src/request_client/mod.rs | 22 +++++++++++++++ 2 files changed, 25 insertions(+), 47 deletions(-) diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index 13d6e11..f38b5c0 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -3,7 +3,6 @@ use std::collections::HashMap; use chrono::DateTime; use derive_getters::Getters; use serde::{Deserialize, Serialize, Serializer}; -use serde_json::json; use crate::*; @@ -387,21 +386,7 @@ impl RequestClient { } pub async fn create(&self, params: ChannelCreateParams<'_>, variables: &HashMap<&str, &str>) -> Result { - let mut url = self.url().join("channels")?; - self.set_authorized_query_params(&mut url, params); - - let channel = self - .as_ref() - .post(url) - .json(&json!({ - "variables": variables - })) - .send() - .await? - .json() - .await?; - - Ok(channel) + self.authorized_post_variables(["channels", "create"], params, variables).await } pub async fn get(self, channel_id: &str) -> Result { @@ -418,22 +403,7 @@ impl RequestClient { } pub async fn originate<'a>(&self, params: OriginateChannelParams<'a>, variables: &HashMap<&str, &str>) -> Result { - let mut url = self.url().join("channels")?; - - self.set_authorized_query_params(&mut url, params); - - let channel = self - .as_ref() - .post(url) - .json(&json!({ - "variables": variables - })) - .send() - .await? - .json() - .await?; - - Ok(channel) + self.authorized_post_variables(["channels"], params, variables).await } pub async fn originate_with_id<'a>( @@ -442,21 +412,7 @@ impl RequestClient { params: OriginateChannelWithIdParams<'a>, variables: &HashMap<&str, &str>, ) -> Result { - let mut url = self.url().join(&format!("channels/{}", channel_id))?; - self.set_authorized_query_params(&mut url, params); - - let channel = self - .as_ref() - .post(url) - .json(&json!({ - "variables": variables - })) - .send() - .await? - .json() - .await?; - - Ok(channel) + self.authorized_post_variables(["channels", channel_id], params, variables).await } pub fn start_moh(&self, _channel_id: &str) -> Result<()> { diff --git a/arirs/src/request_client/mod.rs b/arirs/src/request_client/mod.rs index 1a778a8..4a98f58 100644 --- a/arirs/src/request_client/mod.rs +++ b/arirs/src/request_client/mod.rs @@ -1,5 +1,7 @@ pub use core::RequestClient; mod core { + use std::collections::HashMap; + use derive_getters::Getters; use derive_more::AsRef; use serde::{de::DeserializeOwned, Serialize}; @@ -49,6 +51,26 @@ mod core { Ok(response) } + pub(crate) async fn authorized_post_variables( + &self, + path: impl AsRef<[&str]>, + params: T, + variables: &HashMap<&str, &str>, + ) -> Result { + let url = self.authorized_url(path, params)?; + let response = self + .as_ref() + .post(url) + .json(&serde_json::json!({ + "variables": variables + })) + .send() + .await? + .json() + .await?; + Ok(response) + } + pub(crate) async fn authorized_delete(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { let url = self.authorized_url(path, params)?; self.as_ref().delete(url).send().await?; From 794b5886ee0402e0787d46c61bdb92241c96aa36 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 12:33:16 +0200 Subject: [PATCH 22/61] refactor(ari): add `RequestClient.authorized_get` --- arirs/src/channel.rs | 17 ++--------------- arirs/src/request_client/mod.rs | 6 ++++++ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index f38b5c0..2e01cf7 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -378,11 +378,7 @@ impl RequestClient { } pub async fn list(&self) -> Result> { - let mut url = self.url().join("channels")?; - self.set_authorized_query_params(&mut url, ()); - - let channels = self.as_ref().get(url).send().await?.json().await?; - Ok(channels) + self.authorized_get(["channels"], ()).await } pub async fn create(&self, params: ChannelCreateParams<'_>, variables: &HashMap<&str, &str>) -> Result { @@ -390,16 +386,7 @@ impl RequestClient { } pub async fn get(self, channel_id: &str) -> Result { - let url = self - .url() - .join(&format!("channels/{}", channel_id))? - .query_pairs_mut() - .append_pair("api_key", &self.get_api_key()) - .finish() - .to_owned(); - - let channel = reqwest::get(url).await?.json::().await?; - Ok(channel) + self.authorized_get(["channels", channel_id], ()).await } pub async fn originate<'a>(&self, params: OriginateChannelParams<'a>, variables: &HashMap<&str, &str>) -> Result { diff --git a/arirs/src/request_client/mod.rs b/arirs/src/request_client/mod.rs index 4a98f58..1ee1170 100644 --- a/arirs/src/request_client/mod.rs +++ b/arirs/src/request_client/mod.rs @@ -35,6 +35,12 @@ mod core { } } + pub(crate) async fn authorized_get(&self, path: impl AsRef<[&str]>, params: T) -> Result { + let url = self.authorized_url(path, params)?; + let response = self.as_ref().get(url).send().await?.json().await?; + Ok(response) + } + pub(crate) async fn authorized_post(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { let url = self.authorized_url(path, params)?; self.as_ref().post(url).send().await?; From 8f5860f67f93bf324146824ef5d83fb9096a8f9c Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 12:51:39 +0200 Subject: [PATCH 23/61] refactor!(ari): make `DeviceStateChanged` private in `event` module --- arirs/src/device.rs | 18 ------------------ arirs/src/event.rs | 22 ++++++++++++++++++++-- arirs/src/lib.rs | 3 --- 3 files changed, 20 insertions(+), 23 deletions(-) delete mode 100644 arirs/src/device.rs diff --git a/arirs/src/device.rs b/arirs/src/device.rs deleted file mode 100644 index 2ddfc8b..0000000 --- a/arirs/src/device.rs +++ /dev/null @@ -1,18 +0,0 @@ -use chrono::DateTime; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "snake_case")] -pub struct DeviceStateChanged { - pub application: String, - pub timestamp: DateTime, - pub device_state: DeviceState, - pub asterisk_id: String, -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "snake_case")] -pub struct DeviceState { - pub name: String, - pub state: String, -} diff --git a/arirs/src/event.rs b/arirs/src/event.rs index c65341e..04a0764 100644 --- a/arirs/src/event.rs +++ b/arirs/src/event.rs @@ -1,8 +1,10 @@ -use serde::{Deserialize, Serialize}; +use chrono::DateTime; +use derive_getters::Getters; +use serde::Deserialize; use crate::*; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Deserialize)] #[serde(tag = "type")] pub enum Event { StasisStart(StasisStart), @@ -18,3 +20,19 @@ pub enum Event { #[serde(other)] Unknown, } + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct DeviceStateChanged { + application: String, + timestamp: DateTime, + device_state: DeviceState, + asterisk_id: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct DeviceState { + name: String, + state: String, +} diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index e72b00b..5be84c4 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -10,9 +10,6 @@ pub use channel::*; mod client; pub use client::Client; -mod device; -pub use device::{DeviceState, DeviceStateChanged}; - mod playback; pub use playback::{Operation, Playback}; From 44cad89298c961666332c396e9d53eebd7c3030c Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 12:55:48 +0200 Subject: [PATCH 24/61] refactor!(ari): invert self to `RequestClient` in `playback` --- arirs/src/playback.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/arirs/src/playback.rs b/arirs/src/playback.rs index 13b27d1..4761353 100644 --- a/arirs/src/playback.rs +++ b/arirs/src/playback.rs @@ -2,26 +2,12 @@ use serde::{Deserialize, Serialize}; use crate::*; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "snake_case")] pub struct Playback { pub id: String, } -impl Playback { - pub async fn get_playback(_client: &RequestClient, _playback_id: &str) -> Result { - unimplemented!() - } - - pub async fn control(&self, _client: &RequestClient, _operation: Operation) -> Result<()> { - unimplemented!() - } - - pub async fn stop(&self, _client: &RequestClient) -> Result<()> { - unimplemented!() - } -} - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "snake_case")] pub enum Operation { @@ -31,3 +17,17 @@ pub enum Operation { Reverse, Forward, } + +impl RequestClient { + pub async fn get_playback(&self, _playback_id: &str) -> Result { + unimplemented!() + } + + pub async fn control(&self, _playback_id: &str, _operation: Operation) -> Result<()> { + unimplemented!() + } + + pub async fn stop(&self, _playback_id: &str) -> Result<()> { + unimplemented!() + } +} From ae8f77e731744434f913f54457bb49e2ef80e568 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 14:52:48 +0200 Subject: [PATCH 25/61] refactor!(ari): cleanup and place `RtpStastistics` in `channel` module --- arirs/src/channel.rs | 6 ++++++ arirs/src/lib.rs | 3 --- arirs/src/rtp_statistics.rs | 7 ------- 3 files changed, 6 insertions(+), 10 deletions(-) delete mode 100644 arirs/src/rtp_statistics.rs diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index 2e01cf7..fdb1e48 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -21,6 +21,12 @@ pub struct Channel { language: String, } +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct RtpStatistics { + id: String, +} + #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct OriginateChannelParams<'a> { diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index 5be84c4..f40f1f1 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -16,9 +16,6 @@ pub use playback::{Operation, Playback}; mod recording; pub use recording::{LiveRecording, StoredRecording}; -mod rtp_statistics; -pub use rtp_statistics::RtpStatistics; - mod variable; pub use variable::Variable; diff --git a/arirs/src/rtp_statistics.rs b/arirs/src/rtp_statistics.rs deleted file mode 100644 index ee1b32d..0000000 --- a/arirs/src/rtp_statistics.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "snake_case")] -pub struct RtpStatistics { - pub id: String, -} From c159aed4a9f1f16688209932c527b2c2ba32930e Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 14:56:21 +0200 Subject: [PATCH 26/61] refactor!(ari): invert self to `RequestClient` in `bridge` --- arirs/src/bridge.rs | 28 ++++++++++++++-------------- arirs/src/lib.rs | 6 +++--- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/arirs/src/bridge.rs b/arirs/src/bridge.rs index 8429793..d503bc7 100644 --- a/arirs/src/bridge.rs +++ b/arirs/src/bridge.rs @@ -7,56 +7,56 @@ pub struct Bridge { pub id: String, } -impl Bridge { - pub async fn destroy(&self, _client: &RequestClient) -> Result<()> { +impl RequestClient { + pub async fn destroy(&self, _bridge_id: &str) -> Result<()> { unimplemented!() } - pub async fn add_channel(&self, _client: &RequestClient, _channel_id: &str) -> Result<()> { + pub async fn add_channel(&self, _bridge_id: &str, _channel_id: &str) -> Result<()> { unimplemented!() } - pub async fn remove_channel(&self, _client: &RequestClient, _channel_id: &str) -> Result<()> { + pub async fn remove_channel(&self, _bridge_id: &str, _channel_id: &str) -> Result<()> { unimplemented!() } - pub async fn set_channel_as_video_source(&self, _client: &RequestClient, _channel_id: &str, _video_source_id: &str) -> Result<()> { + pub async fn set_channel_as_video_source(&self, _bridge_id: &str, _channel_id: &str, _video_source_id: &str) -> Result<()> { unimplemented!() } - pub async fn unset_video_source(&self, _client: &RequestClient) -> Result<()> { + pub async fn unset_video_source(&self, _bridge_id: &str) -> Result<()> { unimplemented!() } - pub async fn start_moh(&self, _client: &RequestClient, _moh_class: &str) -> Result<()> { + pub async fn bridge_start_moh(&self, _bridge_id: &str, _moh_class: &str) -> Result<()> { unimplemented!() } - pub async fn stop_moh(&self, _client: &RequestClient) -> Result<()> { + pub async fn bridge_stop_moh(&self, _bridge_id: &str) -> Result<()> { unimplemented!() } - pub async fn play_media(&self, _client: &RequestClient, _playback: &Playback) -> Result<()> { + pub async fn bridge_play_media(&self, _bridge_id: &str, _playback: &Playback) -> Result<()> { unimplemented!() } - pub async fn stop_media(&self, _client: &RequestClient) -> Result<()> { + pub async fn stop_media(&self, _bridge_id: &str) -> Result<()> { unimplemented!() } - pub async fn start_recording(&self, _client: &RequestClient, _recording: &LiveRecording) -> Result<()> { + pub async fn start_recording(&self, _bridge_id: &str, _recording: &LiveRecording) -> Result<()> { unimplemented!() } - pub async fn list_bridges(_client: &RequestClient) -> Result> { + pub async fn list_bridges(&self) -> Result> { unimplemented!() } - pub async fn create_bridge(_client: &RequestClient, _bridge_id: &str) -> Result { + pub async fn create_bridge(&self, _bridge_id: &str) -> Result { unimplemented!() } - pub async fn create_bridge_with_id(_client: &RequestClient, _bridge_id: &str, _bridge: &Bridge) -> Result { + pub async fn create_bridge_with_id(&self, _bridge_id: &str, _bridge: &Bridge) -> Result { unimplemented!() } diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index f40f1f1..c9fa09d 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -1,3 +1,6 @@ +mod client; +pub use client::Client; + mod request_client; pub use request_client::RequestClient; @@ -7,9 +10,6 @@ pub use bridge::Bridge; mod channel; pub use channel::*; -mod client; -pub use client::Client; - mod playback; pub use playback::{Operation, Playback}; From ae74c59d340d67aedd94d37f0a5ad3fc1a1e8b19 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 14:59:13 +0200 Subject: [PATCH 27/61] feat!(ari): prefix `live_recording_` to live recording requests --- arirs/src/recording.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arirs/src/recording.rs b/arirs/src/recording.rs index 3e85cf1..93a26cd 100644 --- a/arirs/src/recording.rs +++ b/arirs/src/recording.rs @@ -10,32 +10,32 @@ pub struct LiveRecording { } impl LiveRecording { - pub async fn get(_recording_name: &str) -> Result { + pub async fn live_recording_get(_recording_name: &str) -> Result { unimplemented!() } - pub async fn discard(&self, _client: &RequestClient) -> Result<()> { + pub async fn live_recording_discard(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } // TODO: explore if it's possible to return a StoredRecording - pub async fn stop(&self, _client: &RequestClient) -> Result<()> { + pub async fn live_recording_stop(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn pause(&self, _client: &RequestClient) -> Result<()> { + pub async fn live_recording_pause(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn resume(&self, _client: &RequestClient) -> Result<()> { + pub async fn live_recording_resume(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn mute(&self, _client: &RequestClient) -> Result<()> { + pub async fn live_recording_mute(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn unmute(&self, _client: &RequestClient) -> Result<()> { + pub async fn live_recording_unmute(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } } From 0026f81fc0adc339ba727374e925e4000a406dc1 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 14:59:42 +0200 Subject: [PATCH 28/61] feat!(ari): prefix `stored_recording_` to stored recording requests --- arirs/src/recording.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arirs/src/recording.rs b/arirs/src/recording.rs index 93a26cd..d4a806d 100644 --- a/arirs/src/recording.rs +++ b/arirs/src/recording.rs @@ -48,19 +48,19 @@ pub struct StoredRecording { } impl StoredRecording { - pub async fn list(_client: &RequestClient) -> Result> { + pub async fn stored_recording_list(_client: &RequestClient) -> Result> { unimplemented!() } - pub async fn get(_recording_name: &str) -> Result { + pub async fn stored_recording_get(_recording_name: &str) -> Result { unimplemented!() } - pub async fn delete(&self, _client: &RequestClient) -> Result<()> { + pub async fn stored_recording_delete(&self, _client: &RequestClient) -> Result<()> { unimplemented!() } - pub async fn download(&self, _client: &RequestClient) -> Result<&[u8]> { + pub async fn stored_recording_download(&self, _client: &RequestClient) -> Result<&[u8]> { unimplemented!() } } From eb604bee8aa5ee466f355c8dc41f24107e532697 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 15:03:15 +0200 Subject: [PATCH 29/61] refactor!(ari): invert self to `RequestClient` in `recording` --- arirs/src/recording.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/arirs/src/recording.rs b/arirs/src/recording.rs index d4a806d..3e05606 100644 --- a/arirs/src/recording.rs +++ b/arirs/src/recording.rs @@ -9,33 +9,33 @@ pub struct LiveRecording { pub name: String, } -impl LiveRecording { - pub async fn live_recording_get(_recording_name: &str) -> Result { +impl RequestClient { + pub async fn live_recording_get(&self, _recording_name: &str) -> Result { unimplemented!() } - pub async fn live_recording_discard(&self, _client: &RequestClient) -> Result<()> { + pub async fn live_recording_discard(&self, _recording_name: &str) -> Result<()> { unimplemented!() } // TODO: explore if it's possible to return a StoredRecording - pub async fn live_recording_stop(&self, _client: &RequestClient) -> Result<()> { + pub async fn live_recording_stop(&self, _recording_name: &str) -> Result<()> { unimplemented!() } - pub async fn live_recording_pause(&self, _client: &RequestClient) -> Result<()> { + pub async fn live_recording_pause(&self, _recording_name: &str) -> Result<()> { unimplemented!() } - pub async fn live_recording_resume(&self, _client: &RequestClient) -> Result<()> { + pub async fn live_recording_resume(&self, _recording_name: &str) -> Result<()> { unimplemented!() } - pub async fn live_recording_mute(&self, _client: &RequestClient) -> Result<()> { + pub async fn live_recording_mute(&self, _recording_name: &str) -> Result<()> { unimplemented!() } - pub async fn live_recording_unmute(&self, _client: &RequestClient) -> Result<()> { + pub async fn live_recording_unmute(&self, _recording_name: &str) -> Result<()> { unimplemented!() } } @@ -47,20 +47,20 @@ pub struct StoredRecording { pub format: String, } -impl StoredRecording { - pub async fn stored_recording_list(_client: &RequestClient) -> Result> { +impl RequestClient { + pub async fn stored_recording_list(&self, _recording_name: &str) -> Result> { unimplemented!() } - pub async fn stored_recording_get(_recording_name: &str) -> Result { + pub async fn stored_recording_get(&self, _recording_name: &str) -> Result { unimplemented!() } - pub async fn stored_recording_delete(&self, _client: &RequestClient) -> Result<()> { + pub async fn stored_recording_delete(&self, _recording_name: &str) -> Result<()> { unimplemented!() } - pub async fn stored_recording_download(&self, _client: &RequestClient) -> Result<&[u8]> { + pub async fn stored_recording_download(&self, _recording_name: &str) -> Result<&[u8]> { unimplemented!() } } From 4c55e5813df85a38af69b0d772f65bfe6419bdb9 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 15:05:53 +0200 Subject: [PATCH 30/61] refactor!(ari): cleanup and place `Variable` in `channel` module --- arirs/src/channel.rs | 8 +++++++- arirs/src/lib.rs | 3 --- arirs/src/variable.rs | 7 ------- 3 files changed, 7 insertions(+), 11 deletions(-) delete mode 100644 arirs/src/variable.rs diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index fdb1e48..e22dd03 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -27,6 +27,12 @@ pub struct RtpStatistics { id: String, } +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelVariable { + id: String, +} + #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct OriginateChannelParams<'a> { @@ -423,7 +429,7 @@ impl RequestClient { pub fn stop_silence(&self, _channel_id: &str) -> Result<()> { unimplemented!() } - pub fn get_variable(&self, _channel_id: &str) -> Result { + pub fn get_variable(&self, _channel_id: &str) -> Result { unimplemented!() } diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index c9fa09d..85cb8d1 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -16,9 +16,6 @@ pub use playback::{Operation, Playback}; mod recording; pub use recording::{LiveRecording, StoredRecording}; -mod variable; -pub use variable::Variable; - mod error; pub use error::{AriError, Result}; diff --git a/arirs/src/variable.rs b/arirs/src/variable.rs deleted file mode 100644 index e7be11c..0000000 --- a/arirs/src/variable.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "snake_case")] -pub struct Variable { - pub id: String, -} From f03df68c1c8e6780af0d7cdc031164d2bb242b7a Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 15:31:48 +0200 Subject: [PATCH 31/61] feat!(ari): go over struct derives and place them in separate files - Place `Debug` first - Request params derives `Serialize` and `Debug` only. Fields are all correctly serialized as "camelCase". (Rename all is sometimes superflously applied to avoid future compatibility issues.) - Response data derives `Deserialize` only. Their fields are made private, exposed through `Getters` derive. --- Cargo.toml | 6 + arirs/examples/playback.rs | 12 +- arirs/src/bridge.rs | 5 +- arirs/src/channel.rs | 347 +--------------------------- arirs/src/channel/request_params.rs | 195 ++++++++++++++++ arirs/src/channel/responses.rs | 138 +++++++++++ 6 files changed, 360 insertions(+), 343 deletions(-) create mode 100644 arirs/src/channel/request_params.rs create mode 100644 arirs/src/channel/responses.rs diff --git a/Cargo.toml b/Cargo.toml index 2eb8ac0..91db6cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,9 @@ [workspace] members = ["agirs", "amirs", "arirs"] resolver = "2" + +[workspace.lints.rust] +unused_must_use = "deny" + +[workspace.lints.clippy] +self_named_module_files = "deny" diff --git a/arirs/examples/playback.rs b/arirs/examples/playback.rs index 4efd5e1..1185a66 100644 --- a/arirs/examples/playback.rs +++ b/arirs/examples/playback.rs @@ -1,4 +1,4 @@ -use arirs::{Client, Event, PlayMediaParams}; +use arirs::{Client, Event, PlayMediaBaseParams, PlayMediaParams}; use tracing::level_filters::LevelFilter; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; @@ -14,11 +14,13 @@ async fn main() -> Result<(), Box> { .play_media( event.channel().id(), PlayMediaParams { - media: "sound:hello", - lang: Some("en"), - offset_ms: None, - skip_ms: None, playback_id: None, + base_params: PlayMediaBaseParams { + media: &["sound:hello"], + lang: Some("en"), + offset_ms: None, + skip_ms: None, + }, }, ) .await?; diff --git a/arirs/src/bridge.rs b/arirs/src/bridge.rs index d503bc7..20f0213 100644 --- a/arirs/src/bridge.rs +++ b/arirs/src/bridge.rs @@ -1,8 +1,9 @@ -use serde::{Deserialize, Serialize}; +use derive_getters::Getters; +use serde::Deserialize; use crate::*; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Deserialize, Getters)] pub struct Bridge { pub id: String, } diff --git a/arirs/src/channel.rs b/arirs/src/channel.rs index e22dd03..1dbb879 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/channel.rs @@ -1,339 +1,12 @@ use std::collections::HashMap; -use chrono::DateTime; -use derive_getters::Getters; -use serde::{Deserialize, Serialize, Serializer}; - use crate::*; -#[derive(Serialize, Deserialize, Debug, Getters)] -#[serde(rename_all = "snake_case")] -pub struct Channel { - id: String, - name: String, - state: String, - protocol_id: String, - caller: Caller, - connected: Caller, - accountcode: String, - dialplan: Dialplan, - creationtime: String, - language: String, -} - -#[derive(Debug, Deserialize, Getters)] -#[serde(rename_all = "snake_case")] -pub struct RtpStatistics { - id: String, -} - -#[derive(Debug, Deserialize, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelVariable { - id: String, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct OriginateChannelParams<'a> { - pub endpoint: &'a str, - pub params: OriginateParams<'a>, - pub caller_id: Option<&'a str>, - pub timeout: Option, - pub channel_id: Option<&'a str>, - pub other_channel_id: Option<&'a str>, - pub originator: Option<&'a str>, - #[serde(skip_serializing_if = "<[_]>::is_empty")] - #[serde(serialize_with = "join_serialize")] - pub formats: &'a [&'a str], -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct OriginateChannelWithIdParams<'a> { - pub endpoint: &'a str, - pub params: OriginateParams<'a>, - pub caller_id: Option<&'a str>, - pub timeout: Option, - pub other_channel_id: Option<&'a str>, - pub originator: Option<&'a str>, - #[serde(skip_serializing_if = "<[_]>::is_empty")] - #[serde(serialize_with = "join_serialize")] - pub formats: &'a [&'a str], -} - -#[derive(Debug, Serialize)] -#[serde(untagged)] -pub enum OriginateParams<'a> { - Extension { - extension: &'a str, - context: Option<&'a str>, - priority: Option, - label: Option<&'a str>, - }, - Application { - app: &'a str, - #[serde(skip_serializing_if = "<[_]>::is_empty")] - #[serde(serialize_with = "join_serialize")] - app_args: &'a [&'a str], - }, -} - -pub use reason::Reason; -mod reason { - use serde::ser::SerializeMap; - use strum::AsRefStr; - - use super::*; +mod responses; +pub use responses::*; - #[derive(Debug, AsRefStr)] - pub enum Reason { - Code(u16), - #[strum(serialize = "normal")] - Normal, - #[strum(serialize = "busy")] - Busy, - #[strum(serialize = "congestion")] - Congestion, - #[strum(serialize = "no_answer")] - NoAnswer, - #[strum(serialize = "timeout")] - Timeout, - #[strum(serialize = "rejected")] - Rejected, - #[strum(serialize = "unallocated")] - Unallocated, - #[strum(serialize = "normal_unspecified")] - NormalUnspecified, - #[strum(serialize = "number_incomplete")] - NumberIncomplete, - #[strum(serialize = "codec_mismatch")] - CodecMismatch, - #[strum(serialize = "interworking")] - Interworking, - #[strum(serialize = "failure")] - Failure, - #[strum(serialize = "answered_elsewhere")] - AnsweredElsewhere, - } - - impl Serialize for Reason { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - let mut map = serializer.serialize_map(Some(1))?; - match self { - Reason::Code(code) => map.serialize_entry("reason_code", code)?, - _ => map.serialize_entry("reason", self.as_ref())?, - }; - map.end() - } - } -} - -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -#[serde(tag = "direction")] -pub enum Direction { - In, - Out, - Both, -} - -#[derive(Serialize, Deserialize, Debug, Getters)] -#[serde(rename_all = "snake_case")] -pub struct StasisStart { - timestamp: DateTime, - args: Vec, - channel: Channel, - asterisk_id: String, - application: String, -} - -#[derive(Serialize, Deserialize, Debug, Getters)] -#[serde(rename_all = "snake_case")] -pub struct StasisEnd { - timestamp: DateTime, - channel: Channel, - asterisk_id: String, - application: String, -} - -#[derive(Serialize, Deserialize, Debug, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelCreated { - timestamp: DateTime, - channel: Option, - asterisk_id: String, - application: String, -} - -#[derive(Serialize, Deserialize, Debug, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelDestroyed { - timestamp: DateTime, - cause: i32, - cause_txt: String, - channel: Channel, - asterisk_id: String, - application: String, -} - -#[derive(Serialize, Deserialize, Debug, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelVarset { - timestamp: DateTime, - variable: String, - value: String, - channel: Option, - asterisk_id: String, - application: String, -} - -#[derive(Serialize, Deserialize, Debug, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelHangupRequest { - timestamp: DateTime, - soft: Option, - cause: i32, - channel: Channel, - asterisk_id: String, - application: String, -} - -#[derive(Serialize, Deserialize, Debug, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelDialplan { - timestamp: DateTime, - dialplan_app: String, - dialplan_app_data: String, - channel: Channel, - asterisk_id: String, - application: String, -} - -#[derive(Serialize, Deserialize, Debug, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelStateChange { - timestamp: DateTime, - channel: Channel, - asterisk_id: String, - application: String, -} - -#[derive(Serialize, Deserialize, Debug, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelDtmfReceived { - timestamp: DateTime, - digit: String, - duration_ms: i32, - channel: Channel, - asterisk_id: String, - application: String, -} - -#[derive(Serialize, Deserialize, Debug, Getters)] -#[serde(rename_all = "snake_case")] -pub struct Caller { - name: String, - number: String, -} - -#[derive(Serialize, Deserialize, Debug, Getters)] -#[serde(rename_all = "snake_case")] -pub struct Dialplan { - context: String, - exten: String, - priority: i32, - app_name: String, - app_data: String, -} - -#[derive(Serialize)] -pub struct SendDtmfParams<'a> { - pub dtmf: &'a str, - /// in milliseconds - pub between: Option, - /// in milliseconds - pub duration: Option, - pub before: Option, - pub after: Option, -} - -#[derive(Serialize)] -pub struct PlayMediaParams<'a> { - pub media: &'a str, - pub lang: Option<&'a str>, - pub offset_ms: Option, - pub skip_ms: Option, - pub playback_id: Option<&'a str>, -} - -#[derive(Serialize)] -pub struct PlayMediaWithIdParams<'a> { - #[serde(serialize_with = "join_serialize")] - pub media: &'a [&'a str], - pub lang: Option<&'a str>, - pub offset_ms: Option, - pub skip_ms: Option, -} - -fn join_serialize(slice: &[&str], s: S) -> std::result::Result -where - S: Serializer, -{ - s.serialize_str(&slice.join(",")) -} - -#[derive(Serialize)] -pub struct RecordParams<'a> { - pub name: &'a str, - pub format: &'a str, - pub max_duration_seconds: Option, - pub max_silence_seconds: Option, - pub if_exists: RecordingAction, - pub beep: bool, - pub terminate_on: RecordingTermination, -} -#[derive(Debug, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum RecordingAction { - Overwrite, - Append, - Fail, -} -#[derive(Debug, Serialize)] -pub enum RecordingTermination { - None, - Any, - #[serde(rename = "*")] - Asterisk, - #[serde(rename = "#")] - Octothorpe, -} - -#[derive(Serialize)] -pub struct DialParams<'a> { - pub caller: Option<&'a str>, - pub timeout: Option, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ChannelCreateParams<'a> { - pub endpoint: &'a str, - pub app: &'a str, - #[serde(skip_serializing_if = "<[_]>::is_empty")] - #[serde(serialize_with = "join_serialize")] - pub app_args: &'a [&'a str], - pub channel_id: Option<&'a str>, - pub other_channel_id: Option<&'a str>, - pub originator: Option<&'a str>, - #[serde(skip_serializing_if = "<[_]>::is_empty")] - #[serde(serialize_with = "join_serialize")] - pub formats: &'a [&'a str], -} +mod request_params; +pub use request_params::*; impl RequestClient { pub async fn answer(&self, channel_id: &str) -> Result<()> { @@ -376,7 +49,7 @@ impl RequestClient { self.authorized_post_json_response(["channels", channel_id, "play"], params).await } - pub async fn play_media_with_id(&self, channel_id: &str, playback_id: &str, params: PlayMediaWithIdParams<'_>) -> Result { + pub async fn play_media_with_id(&self, channel_id: &str, playback_id: &str, params: PlayMediaBaseParams<'_>) -> Result { self.authorized_post_json_response(["channels", channel_id, "play", playback_id, "media"], params) .await } @@ -477,11 +150,13 @@ mod tests { request_client.set_authorized_query_params( &mut url, PlayMediaParams { - media: "sound:hello", - lang: Some("en"), - offset_ms: None, - skip_ms: None, playback_id: None, + base_params: PlayMediaBaseParams { + media: &["sound:hello"], + lang: Some("en"), + offset_ms: None, + skip_ms: None, + }, }, ); diff --git a/arirs/src/channel/request_params.rs b/arirs/src/channel/request_params.rs new file mode 100644 index 0000000..5faf97d --- /dev/null +++ b/arirs/src/channel/request_params.rs @@ -0,0 +1,195 @@ +use serde::{ser::SerializeMap, Serialize, Serializer}; +use strum::AsRefStr; + +#[derive(Debug, Serialize)] +#[serde(tag = "direction", rename_all = "camelCase")] +pub enum Direction { + In, + Out, + Both, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OriginateChannelParams<'a> { + pub endpoint: &'a str, + pub params: OriginateParams<'a>, + pub caller_id: Option<&'a str>, + pub timeout: Option, + pub channel_id: Option<&'a str>, + pub other_channel_id: Option<&'a str>, + pub originator: Option<&'a str>, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + #[serde(serialize_with = "join_serialize")] + pub formats: &'a [&'a str], +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OriginateChannelWithIdParams<'a> { + pub endpoint: &'a str, + pub params: OriginateParams<'a>, + pub caller_id: Option<&'a str>, + pub timeout: Option, + pub other_channel_id: Option<&'a str>, + pub originator: Option<&'a str>, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + #[serde(serialize_with = "join_serialize")] + pub formats: &'a [&'a str], +} + +#[derive(Debug, Serialize)] +#[serde(untagged, rename_all = "camelCase")] +pub enum OriginateParams<'a> { + Extension { + extension: &'a str, + context: Option<&'a str>, + priority: Option, + label: Option<&'a str>, + }, + Application { + app: &'a str, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + #[serde(serialize_with = "join_serialize")] + app_args: &'a [&'a str], + }, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SendDtmfParams<'a> { + pub dtmf: &'a str, + /// in milliseconds + pub between: Option, + /// in milliseconds + pub duration: Option, + pub before: Option, + pub after: Option, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PlayMediaParams<'a> { + pub playback_id: Option<&'a str>, + #[serde(flatten)] + pub base_params: PlayMediaBaseParams<'a>, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PlayMediaBaseParams<'a> { + #[serde(serialize_with = "join_serialize")] + pub media: &'a [&'a str], + pub lang: Option<&'a str>, + #[serde(rename = "offsetms")] + pub offset_ms: Option, + #[serde(rename = "skipms")] + pub skip_ms: Option, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RecordParams<'a> { + pub name: &'a str, + pub format: &'a str, + pub max_duration_seconds: Option, + pub max_silence_seconds: Option, + pub if_exists: RecordingAction, + pub beep: bool, + pub terminate_on: RecordingTermination, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum RecordingAction { + Overwrite, + Append, + Fail, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum RecordingTermination { + None, + Any, + #[serde(rename = "*")] + Asterisk, + #[serde(rename = "#")] + Octothorpe, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct DialParams<'a> { + pub caller: Option<&'a str>, + pub timeout: Option, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ChannelCreateParams<'a> { + pub endpoint: &'a str, + pub app: &'a str, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + #[serde(serialize_with = "join_serialize")] + pub app_args: &'a [&'a str], + pub channel_id: Option<&'a str>, + pub other_channel_id: Option<&'a str>, + pub originator: Option<&'a str>, + #[serde(skip_serializing_if = "<[_]>::is_empty")] + #[serde(serialize_with = "join_serialize")] + pub formats: &'a [&'a str], +} + +fn join_serialize(slice: &[&str], s: S) -> std::result::Result +where + S: Serializer, +{ + s.serialize_str(&slice.join(",")) +} + +// NOTE: camelCase exception +#[derive(Debug, AsRefStr)] +pub enum Reason { + Code(u16), + #[strum(serialize = "normal")] + Normal, + #[strum(serialize = "busy")] + Busy, + #[strum(serialize = "congestion")] + Congestion, + #[strum(serialize = "no_answer")] + NoAnswer, + #[strum(serialize = "timeout")] + Timeout, + #[strum(serialize = "rejected")] + Rejected, + #[strum(serialize = "unallocated")] + Unallocated, + #[strum(serialize = "normal_unspecified")] + NormalUnspecified, + #[strum(serialize = "number_incomplete")] + NumberIncomplete, + #[strum(serialize = "codec_mismatch")] + CodecMismatch, + #[strum(serialize = "interworking")] + Interworking, + #[strum(serialize = "failure")] + Failure, + #[strum(serialize = "answered_elsewhere")] + AnsweredElsewhere, +} + +impl Serialize for Reason { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + let mut map = serializer.serialize_map(Some(1))?; + match self { + Reason::Code(code) => map.serialize_entry("reason_code", code)?, + _ => map.serialize_entry("reason", self.as_ref())?, + }; + map.end() + } +} diff --git a/arirs/src/channel/responses.rs b/arirs/src/channel/responses.rs new file mode 100644 index 0000000..9c02b65 --- /dev/null +++ b/arirs/src/channel/responses.rs @@ -0,0 +1,138 @@ +use chrono::DateTime; +use derive_getters::Getters; +use serde::Deserialize; + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct Channel { + id: String, + name: String, + state: String, + protocol_id: String, + caller: Caller, + connected: Caller, + accountcode: String, + dialplan: Dialplan, + creationtime: String, + language: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct RtpStatistics { + id: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelVariable { + id: String, +} +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct StasisStart { + timestamp: DateTime, + args: Vec, + channel: Channel, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct StasisEnd { + timestamp: DateTime, + channel: Channel, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelCreated { + timestamp: DateTime, + channel: Option, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelDestroyed { + timestamp: DateTime, + cause: i32, + cause_txt: String, + channel: Channel, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelVarset { + timestamp: DateTime, + variable: String, + value: String, + channel: Option, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelHangupRequest { + timestamp: DateTime, + soft: Option, + cause: i32, + channel: Channel, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelDialplan { + timestamp: DateTime, + dialplan_app: String, + dialplan_app_data: String, + channel: Channel, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelStateChange { + timestamp: DateTime, + channel: Channel, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelDtmfReceived { + timestamp: DateTime, + digit: String, + duration_ms: i32, + channel: Channel, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct Caller { + name: String, + number: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct Dialplan { + context: String, + exten: String, + priority: i32, + app_name: String, + app_data: String, +} From 5924b2993fa04a39846ec4850b22f2f0f07ece04 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 15:37:50 +0200 Subject: [PATCH 32/61] refactor(ari): place `channel` handling under `request_client` module --- arirs/src/lib.rs | 5 +- .../channel/core.rs} | 46 ------- arirs/src/request_client/channel/mod.rs | 7 + .../channel/request_params.rs | 40 ++++++ .../{ => request_client}/channel/responses.rs | 0 arirs/src/request_client/core.rs | 117 +++++++++++++++++ arirs/src/request_client/mod.rs | 121 +----------------- 7 files changed, 168 insertions(+), 168 deletions(-) rename arirs/src/{channel.rs => request_client/channel/core.rs} (77%) create mode 100644 arirs/src/request_client/channel/mod.rs rename arirs/src/{ => request_client}/channel/request_params.rs (80%) rename arirs/src/{ => request_client}/channel/responses.rs (100%) create mode 100644 arirs/src/request_client/core.rs diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index 85cb8d1..be4a058 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -2,14 +2,11 @@ mod client; pub use client::Client; mod request_client; -pub use request_client::RequestClient; +pub use request_client::*; mod bridge; pub use bridge::Bridge; -mod channel; -pub use channel::*; - mod playback; pub use playback::{Operation, Playback}; diff --git a/arirs/src/channel.rs b/arirs/src/request_client/channel/core.rs similarity index 77% rename from arirs/src/channel.rs rename to arirs/src/request_client/channel/core.rs index 1dbb879..b4b0ffc 100644 --- a/arirs/src/channel.rs +++ b/arirs/src/request_client/channel/core.rs @@ -2,12 +2,6 @@ use std::collections::HashMap; use crate::*; -mod responses; -pub use responses::*; - -mod request_params; -pub use request_params::*; - impl RequestClient { pub async fn answer(&self, channel_id: &str) -> Result<()> { self.authorized_post(["channels", channel_id, "answer"], ()).await @@ -136,43 +130,3 @@ impl RequestClient { unimplemented!() } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn serializes_parameters() { - let request_client = RequestClient::new("http://localhost:8080/".parse().unwrap(), "asterisk", "asterisk"); - - let mut url = request_client.url().join("channel").unwrap(); - - request_client.set_authorized_query_params( - &mut url, - PlayMediaParams { - playback_id: None, - base_params: PlayMediaBaseParams { - media: &["sound:hello"], - lang: Some("en"), - offset_ms: None, - skip_ms: None, - }, - }, - ); - - let expected = "http://localhost:8080/channel?api_key=asterisk%3Aasterisk&media=sound%3Ahello&lang=en"; - assert_eq!(expected, url.as_str()) - } - - #[test] - fn serializes_unit_type() { - let request_client = RequestClient::new("http://localhost:8080/".parse().unwrap(), "asterisk", "asterisk"); - - let mut url = request_client.url().join("channel").unwrap(); - - request_client.set_authorized_query_params(&mut url, ()); - - let expected = "http://localhost:8080/channel?api_key=asterisk%3Aasterisk"; - assert_eq!(expected, url.as_str()) - } -} diff --git a/arirs/src/request_client/channel/mod.rs b/arirs/src/request_client/channel/mod.rs new file mode 100644 index 0000000..bfae9bb --- /dev/null +++ b/arirs/src/request_client/channel/mod.rs @@ -0,0 +1,7 @@ +mod core; + +mod responses; +pub use responses::*; + +mod request_params; +pub use request_params::*; diff --git a/arirs/src/channel/request_params.rs b/arirs/src/request_client/channel/request_params.rs similarity index 80% rename from arirs/src/channel/request_params.rs rename to arirs/src/request_client/channel/request_params.rs index 5faf97d..6497f80 100644 --- a/arirs/src/channel/request_params.rs +++ b/arirs/src/request_client/channel/request_params.rs @@ -193,3 +193,43 @@ impl Serialize for Reason { map.end() } } + +#[cfg(test)] +mod tests { + use crate::*; + + #[test] + fn serializes_parameters() { + let request_client = RequestClient::new("http://localhost:8080/".parse().unwrap(), "asterisk", "asterisk"); + + let mut url = request_client.url().join("channel").unwrap(); + + request_client.set_authorized_query_params( + &mut url, + PlayMediaParams { + playback_id: None, + base_params: PlayMediaBaseParams { + media: &["sound:hello"], + lang: Some("en"), + offset_ms: None, + skip_ms: None, + }, + }, + ); + + let expected = "http://localhost:8080/channel?api_key=asterisk%3Aasterisk&media=sound%3Ahello&lang=en"; + assert_eq!(expected, url.as_str()) + } + + #[test] + fn serializes_unit_type() { + let request_client = RequestClient::new("http://localhost:8080/".parse().unwrap(), "asterisk", "asterisk"); + + let mut url = request_client.url().join("channel").unwrap(); + + request_client.set_authorized_query_params(&mut url, ()); + + let expected = "http://localhost:8080/channel?api_key=asterisk%3Aasterisk"; + assert_eq!(expected, url.as_str()) + } +} diff --git a/arirs/src/channel/responses.rs b/arirs/src/request_client/channel/responses.rs similarity index 100% rename from arirs/src/channel/responses.rs rename to arirs/src/request_client/channel/responses.rs diff --git a/arirs/src/request_client/core.rs b/arirs/src/request_client/core.rs new file mode 100644 index 0000000..25ffc63 --- /dev/null +++ b/arirs/src/request_client/core.rs @@ -0,0 +1,117 @@ +use std::collections::HashMap; + +use derive_getters::Getters; +use derive_more::AsRef; +use serde::{de::DeserializeOwned, Serialize}; +use url::Url; + +use crate::*; + +#[derive(Serialize)] +pub struct AuthorizedRequest { + api_key: String, + #[serde(flatten)] + inner: T, +} + +#[derive(Debug, Getters, AsRef)] +pub struct RequestClient { + pub(crate) url: Url, + pub(crate) username: String, + pub(crate) password: String, + #[as_ref] + inner: reqwest::Client, +} + +impl RequestClient { + pub(crate) fn new(url: Url, username: impl Into, password: impl Into) -> Self { + Self { + url, + username: username.into(), + password: password.into(), + inner: reqwest::Client::new(), + } + } + + pub(crate) async fn authorized_get(&self, path: impl AsRef<[&str]>, params: T) -> Result { + let url = self.authorized_url(path, params)?; + let response = self.as_ref().get(url).send().await?.json().await?; + Ok(response) + } + + pub(crate) async fn authorized_post(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { + let url = self.authorized_url(path, params)?; + self.as_ref().post(url).send().await?; + Ok(()) + } + + pub(crate) async fn authorized_post_json_response( + &self, + path: impl AsRef<[&str]>, + params: T, + ) -> Result { + let url = self.authorized_url(path, params)?; + let response = self.as_ref().post(url).send().await?.json().await?; + Ok(response) + } + + pub(crate) async fn authorized_post_variables( + &self, + path: impl AsRef<[&str]>, + params: T, + variables: &HashMap<&str, &str>, + ) -> Result { + let url = self.authorized_url(path, params)?; + let response = self + .as_ref() + .post(url) + .json(&serde_json::json!({ + "variables": variables + })) + .send() + .await? + .json() + .await?; + Ok(response) + } + + pub(crate) async fn authorized_delete(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { + let url = self.authorized_url(path, params)?; + self.as_ref().delete(url).send().await?; + Ok(()) + } + + fn authorized_url<'a, T: Serialize>(&self, path: impl AsRef<[&'a str]>, params: T) -> Result { + let mut url = self.url().join(&path.as_ref().join("/"))?; + self.set_authorized_query_params(&mut url, params); + Ok(url) + } + + pub(crate) fn authorize_request(&self, inner: T) -> AuthorizedRequest { + AuthorizedRequest { + api_key: self.get_api_key(), + inner, + } + } + + pub(crate) fn get_api_key(&self) -> String { + format!("{}:{}", self.username, self.password) + } + + pub(crate) fn set_authorized_query_params(&self, url: &mut Url, params: T) { + let authorized_request_params = self.authorize_request(params); + let query_string = serde_qs::to_string(&authorized_request_params).expect("failed to serialize query parameters"); + url.set_query(Some(&query_string)); + } +} + +impl Default for RequestClient { + fn default() -> Self { + Self { + url: "http://localhost:8088/".parse().expect("failed to parse url"), + username: "asterisk".to_string(), + password: "asterisk".to_string(), + inner: reqwest::Client::new(), + } + } +} diff --git a/arirs/src/request_client/mod.rs b/arirs/src/request_client/mod.rs index 1ee1170..f5f99ba 100644 --- a/arirs/src/request_client/mod.rs +++ b/arirs/src/request_client/mod.rs @@ -1,120 +1,5 @@ +mod core; pub use core::RequestClient; -mod core { - use std::collections::HashMap; - use derive_getters::Getters; - use derive_more::AsRef; - use serde::{de::DeserializeOwned, Serialize}; - use url::Url; - - use crate::*; - - #[derive(Serialize)] - pub struct AuthorizedRequest { - api_key: String, - #[serde(flatten)] - inner: T, - } - - #[derive(Debug, Getters, AsRef)] - pub struct RequestClient { - pub(crate) url: Url, - pub(crate) username: String, - pub(crate) password: String, - #[as_ref] - inner: reqwest::Client, - } - - impl RequestClient { - pub(crate) fn new(url: Url, username: impl Into, password: impl Into) -> Self { - Self { - url, - username: username.into(), - password: password.into(), - inner: reqwest::Client::new(), - } - } - - pub(crate) async fn authorized_get(&self, path: impl AsRef<[&str]>, params: T) -> Result { - let url = self.authorized_url(path, params)?; - let response = self.as_ref().get(url).send().await?.json().await?; - Ok(response) - } - - pub(crate) async fn authorized_post(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { - let url = self.authorized_url(path, params)?; - self.as_ref().post(url).send().await?; - Ok(()) - } - - pub(crate) async fn authorized_post_json_response( - &self, - path: impl AsRef<[&str]>, - params: T, - ) -> Result { - let url = self.authorized_url(path, params)?; - let response = self.as_ref().post(url).send().await?.json().await?; - Ok(response) - } - - pub(crate) async fn authorized_post_variables( - &self, - path: impl AsRef<[&str]>, - params: T, - variables: &HashMap<&str, &str>, - ) -> Result { - let url = self.authorized_url(path, params)?; - let response = self - .as_ref() - .post(url) - .json(&serde_json::json!({ - "variables": variables - })) - .send() - .await? - .json() - .await?; - Ok(response) - } - - pub(crate) async fn authorized_delete(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { - let url = self.authorized_url(path, params)?; - self.as_ref().delete(url).send().await?; - Ok(()) - } - - fn authorized_url<'a, T: Serialize>(&self, path: impl AsRef<[&'a str]>, params: T) -> Result { - let mut url = self.url().join(&path.as_ref().join("/"))?; - self.set_authorized_query_params(&mut url, params); - Ok(url) - } - - pub(crate) fn authorize_request(&self, inner: T) -> AuthorizedRequest { - AuthorizedRequest { - api_key: self.get_api_key(), - inner, - } - } - - pub(crate) fn get_api_key(&self) -> String { - format!("{}:{}", self.username, self.password) - } - - pub(crate) fn set_authorized_query_params(&self, url: &mut Url, params: T) { - let authorized_request_params = self.authorize_request(params); - let query_string = serde_qs::to_string(&authorized_request_params).expect("failed to serialize query parameters"); - url.set_query(Some(&query_string)); - } - } - - impl Default for RequestClient { - fn default() -> Self { - Self { - url: "http://localhost:8088/".parse().expect("failed to parse url"), - username: "asterisk".to_string(), - password: "asterisk".to_string(), - inner: reqwest::Client::new(), - } - } - } -} +mod channel; +pub use channel::*; From 12459a6118b18de1b09e76660e512bbe2ab7b230 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 15:40:47 +0200 Subject: [PATCH 33/61] refactor(ari): place `channel` handling under `request_client` module --- arirs/src/lib.rs | 3 --- arirs/src/{bridge.rs => request_client/bridge/core.rs} | 8 -------- arirs/src/request_client/bridge/mod.rs | 4 ++++ arirs/src/request_client/bridge/responses.rs | 7 +++++++ arirs/src/request_client/mod.rs | 3 +++ 5 files changed, 14 insertions(+), 11 deletions(-) rename arirs/src/{bridge.rs => request_client/bridge/core.rs} (92%) create mode 100644 arirs/src/request_client/bridge/mod.rs create mode 100644 arirs/src/request_client/bridge/responses.rs diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index be4a058..d4d2941 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -4,9 +4,6 @@ pub use client::Client; mod request_client; pub use request_client::*; -mod bridge; -pub use bridge::Bridge; - mod playback; pub use playback::{Operation, Playback}; diff --git a/arirs/src/bridge.rs b/arirs/src/request_client/bridge/core.rs similarity index 92% rename from arirs/src/bridge.rs rename to arirs/src/request_client/bridge/core.rs index 20f0213..b3cf9ff 100644 --- a/arirs/src/bridge.rs +++ b/arirs/src/request_client/bridge/core.rs @@ -1,13 +1,5 @@ -use derive_getters::Getters; -use serde::Deserialize; - use crate::*; -#[derive(Debug, Deserialize, Getters)] -pub struct Bridge { - pub id: String, -} - impl RequestClient { pub async fn destroy(&self, _bridge_id: &str) -> Result<()> { unimplemented!() diff --git a/arirs/src/request_client/bridge/mod.rs b/arirs/src/request_client/bridge/mod.rs new file mode 100644 index 0000000..eeb36ef --- /dev/null +++ b/arirs/src/request_client/bridge/mod.rs @@ -0,0 +1,4 @@ +mod core; + +mod responses; +pub use responses::*; diff --git a/arirs/src/request_client/bridge/responses.rs b/arirs/src/request_client/bridge/responses.rs new file mode 100644 index 0000000..bcf3eab --- /dev/null +++ b/arirs/src/request_client/bridge/responses.rs @@ -0,0 +1,7 @@ +use derive_getters::Getters; +use serde::Deserialize; + +#[derive(Debug, Deserialize, Getters)] +pub struct Bridge { + id: String, +} diff --git a/arirs/src/request_client/mod.rs b/arirs/src/request_client/mod.rs index f5f99ba..b596785 100644 --- a/arirs/src/request_client/mod.rs +++ b/arirs/src/request_client/mod.rs @@ -3,3 +3,6 @@ pub use core::RequestClient; mod channel; pub use channel::*; + +mod bridge; +pub use bridge::*; From d47bc0296198b4f187dd4117e8fb6d4b9aff3e0f Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 15:45:30 +0200 Subject: [PATCH 34/61] refactor(ari): place `bridge` and `channel` handling under `request_client` module --- arirs/src/lib.rs | 3 --- arirs/src/request_client/mod.rs | 7 +++++-- .../playback/core.rs} | 18 ------------------ arirs/src/request_client/playback/mod.rs | 7 +++++++ .../request_client/playback/request_params.rs | 11 +++++++++++ arirs/src/request_client/playback/responses.rs | 8 ++++++++ 6 files changed, 31 insertions(+), 23 deletions(-) rename arirs/src/{playback.rs => request_client/playback/core.rs} (54%) create mode 100644 arirs/src/request_client/playback/mod.rs create mode 100644 arirs/src/request_client/playback/request_params.rs create mode 100644 arirs/src/request_client/playback/responses.rs diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index d4d2941..0c78f95 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -4,9 +4,6 @@ pub use client::Client; mod request_client; pub use request_client::*; -mod playback; -pub use playback::{Operation, Playback}; - mod recording; pub use recording::{LiveRecording, StoredRecording}; diff --git a/arirs/src/request_client/mod.rs b/arirs/src/request_client/mod.rs index b596785..b35fbc1 100644 --- a/arirs/src/request_client/mod.rs +++ b/arirs/src/request_client/mod.rs @@ -1,8 +1,11 @@ mod core; pub use core::RequestClient; +mod bridge; +pub use bridge::*; + mod channel; pub use channel::*; -mod bridge; -pub use bridge::*; +mod playback; +pub use playback::*; diff --git a/arirs/src/playback.rs b/arirs/src/request_client/playback/core.rs similarity index 54% rename from arirs/src/playback.rs rename to arirs/src/request_client/playback/core.rs index 4761353..67f3c34 100644 --- a/arirs/src/playback.rs +++ b/arirs/src/request_client/playback/core.rs @@ -1,23 +1,5 @@ -use serde::{Deserialize, Serialize}; - use crate::*; -#[derive(Debug, Deserialize)] -#[serde(rename_all = "snake_case")] -pub struct Playback { - pub id: String, -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "snake_case")] -pub enum Operation { - Restart, - Pause, - Unpause, - Reverse, - Forward, -} - impl RequestClient { pub async fn get_playback(&self, _playback_id: &str) -> Result { unimplemented!() diff --git a/arirs/src/request_client/playback/mod.rs b/arirs/src/request_client/playback/mod.rs new file mode 100644 index 0000000..bfae9bb --- /dev/null +++ b/arirs/src/request_client/playback/mod.rs @@ -0,0 +1,7 @@ +mod core; + +mod responses; +pub use responses::*; + +mod request_params; +pub use request_params::*; diff --git a/arirs/src/request_client/playback/request_params.rs b/arirs/src/request_client/playback/request_params.rs new file mode 100644 index 0000000..7fba6d3 --- /dev/null +++ b/arirs/src/request_client/playback/request_params.rs @@ -0,0 +1,11 @@ +use serde::Serialize; + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum Operation { + Restart, + Pause, + Unpause, + Reverse, + Forward, +} diff --git a/arirs/src/request_client/playback/responses.rs b/arirs/src/request_client/playback/responses.rs new file mode 100644 index 0000000..714790a --- /dev/null +++ b/arirs/src/request_client/playback/responses.rs @@ -0,0 +1,8 @@ +use derive_getters::Getters; +use serde::Deserialize; + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct Playback { + id: String, +} From 0a269cdd256e9c17782bb978f83105abc032ae0d Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 15:48:41 +0200 Subject: [PATCH 35/61] refactor(ari): place `recording` handling under `request_client` module --- arirs/src/lib.rs | 3 --- arirs/src/request_client/mod.rs | 3 +++ .../recording/core.rs} | 16 ---------------- arirs/src/request_client/recording/mod.rs | 4 ++++ arirs/src/request_client/recording/responses.rs | 16 ++++++++++++++++ 5 files changed, 23 insertions(+), 19 deletions(-) rename arirs/src/{recording.rs => request_client/recording/core.rs} (81%) create mode 100644 arirs/src/request_client/recording/mod.rs create mode 100644 arirs/src/request_client/recording/responses.rs diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index 0c78f95..6678395 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -4,9 +4,6 @@ pub use client::Client; mod request_client; pub use request_client::*; -mod recording; -pub use recording::{LiveRecording, StoredRecording}; - mod error; pub use error::{AriError, Result}; diff --git a/arirs/src/request_client/mod.rs b/arirs/src/request_client/mod.rs index b35fbc1..9c8cc59 100644 --- a/arirs/src/request_client/mod.rs +++ b/arirs/src/request_client/mod.rs @@ -9,3 +9,6 @@ pub use channel::*; mod playback; pub use playback::*; + +mod recording; +pub use recording::*; diff --git a/arirs/src/recording.rs b/arirs/src/request_client/recording/core.rs similarity index 81% rename from arirs/src/recording.rs rename to arirs/src/request_client/recording/core.rs index 3e05606..7243ed3 100644 --- a/arirs/src/recording.rs +++ b/arirs/src/request_client/recording/core.rs @@ -1,14 +1,5 @@ -use serde::{Deserialize, Serialize}; - use crate::*; -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "snake_case")] -pub struct LiveRecording { - pub id: String, - pub name: String, -} - impl RequestClient { pub async fn live_recording_get(&self, _recording_name: &str) -> Result { unimplemented!() @@ -40,13 +31,6 @@ impl RequestClient { } } -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "snake_case")] -pub struct StoredRecording { - pub id: String, - pub format: String, -} - impl RequestClient { pub async fn stored_recording_list(&self, _recording_name: &str) -> Result> { unimplemented!() diff --git a/arirs/src/request_client/recording/mod.rs b/arirs/src/request_client/recording/mod.rs new file mode 100644 index 0000000..eeb36ef --- /dev/null +++ b/arirs/src/request_client/recording/mod.rs @@ -0,0 +1,4 @@ +mod core; + +mod responses; +pub use responses::*; diff --git a/arirs/src/request_client/recording/responses.rs b/arirs/src/request_client/recording/responses.rs new file mode 100644 index 0000000..572070f --- /dev/null +++ b/arirs/src/request_client/recording/responses.rs @@ -0,0 +1,16 @@ +use derive_getters::Getters; +use serde::Deserialize; + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct LiveRecording { + id: String, + name: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct StoredRecording { + id: String, + format: String, +} From 2c3dadcad2022d27ba5b26a89d5d4399fe038061 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 15:52:26 +0200 Subject: [PATCH 36/61] chore(ari): go over `RequestClient` method visibilities --- Cargo.lock | 21 -------------------- arirs/Cargo.toml | 1 - arirs/src/request_client/core.rs | 33 ++++++++++++++------------------ 3 files changed, 14 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d88eb74..e80e4a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,7 +55,6 @@ version = "0.1.0" dependencies = [ "chrono", "derive-getters", - "derive_more", "futures-channel", "futures-util", "rand", @@ -220,26 +219,6 @@ dependencies = [ "syn", ] -[[package]] -name = "derive_more" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "digest" version = "0.10.7" diff --git a/arirs/Cargo.toml b/arirs/Cargo.toml index f4fbac9..33da903 100644 --- a/arirs/Cargo.toml +++ b/arirs/Cargo.toml @@ -8,7 +8,6 @@ version = "0.1.0" [dependencies] chrono = { version = "0.4.38", features = ["serde"] } derive-getters = "0.5" -derive_more = { version = "1.0.0", features = ["as_ref"] } futures-channel = "0.3.30" futures-util = "0.3.30" rand = "0.8.5" diff --git a/arirs/src/request_client/core.rs b/arirs/src/request_client/core.rs index 25ffc63..328ed12 100644 --- a/arirs/src/request_client/core.rs +++ b/arirs/src/request_client/core.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use derive_getters::Getters; -use derive_more::AsRef; use serde::{de::DeserializeOwned, Serialize}; use url::Url; @@ -14,12 +13,11 @@ pub struct AuthorizedRequest { inner: T, } -#[derive(Debug, Getters, AsRef)] +#[derive(Debug, Getters)] pub struct RequestClient { - pub(crate) url: Url, - pub(crate) username: String, - pub(crate) password: String, - #[as_ref] + url: Url, + username: String, + password: String, inner: reqwest::Client, } @@ -35,13 +33,13 @@ impl RequestClient { pub(crate) async fn authorized_get(&self, path: impl AsRef<[&str]>, params: T) -> Result { let url = self.authorized_url(path, params)?; - let response = self.as_ref().get(url).send().await?.json().await?; + let response = self.inner.get(url).send().await?.json().await?; Ok(response) } pub(crate) async fn authorized_post(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { let url = self.authorized_url(path, params)?; - self.as_ref().post(url).send().await?; + self.inner.post(url).send().await?; Ok(()) } @@ -51,7 +49,7 @@ impl RequestClient { params: T, ) -> Result { let url = self.authorized_url(path, params)?; - let response = self.as_ref().post(url).send().await?.json().await?; + let response = self.inner.post(url).send().await?.json().await?; Ok(response) } @@ -63,7 +61,7 @@ impl RequestClient { ) -> Result { let url = self.authorized_url(path, params)?; let response = self - .as_ref() + .inner .post(url) .json(&serde_json::json!({ "variables": variables @@ -77,7 +75,7 @@ impl RequestClient { pub(crate) async fn authorized_delete(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { let url = self.authorized_url(path, params)?; - self.as_ref().delete(url).send().await?; + self.inner.delete(url).send().await?; Ok(()) } @@ -87,19 +85,16 @@ impl RequestClient { Ok(url) } - pub(crate) fn authorize_request(&self, inner: T) -> AuthorizedRequest { - AuthorizedRequest { - api_key: self.get_api_key(), - inner, - } - } - pub(crate) fn get_api_key(&self) -> String { format!("{}:{}", self.username, self.password) } pub(crate) fn set_authorized_query_params(&self, url: &mut Url, params: T) { - let authorized_request_params = self.authorize_request(params); + let authorized_request_params = AuthorizedRequest { + api_key: self.get_api_key(), + inner: params, + }; + let query_string = serde_qs::to_string(&authorized_request_params).expect("failed to serialize query parameters"); url.set_query(Some(&query_string)); } From cd6bf597f09db9b162e4366c693dfbcaa33e2a86 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 15:56:05 +0200 Subject: [PATCH 37/61] perf(ari): memoize API key --- arirs/src/request_client/core.rs | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/arirs/src/request_client/core.rs b/arirs/src/request_client/core.rs index 328ed12..2eb3d70 100644 --- a/arirs/src/request_client/core.rs +++ b/arirs/src/request_client/core.rs @@ -7,8 +7,8 @@ use url::Url; use crate::*; #[derive(Serialize)] -pub struct AuthorizedRequest { - api_key: String, +pub struct AuthorizedRequest<'a, T> { + api_key: &'a str, #[serde(flatten)] inner: T, } @@ -16,17 +16,15 @@ pub struct AuthorizedRequest { #[derive(Debug, Getters)] pub struct RequestClient { url: Url, - username: String, - password: String, + bearer: String, inner: reqwest::Client, } impl RequestClient { - pub(crate) fn new(url: Url, username: impl Into, password: impl Into) -> Self { + pub(crate) fn new(url: Url, username: impl AsRef, password: impl AsRef) -> Self { Self { url, - username: username.into(), - password: password.into(), + bearer: format!("{}:{}", username.as_ref(), password.as_ref()), inner: reqwest::Client::new(), } } @@ -85,13 +83,9 @@ impl RequestClient { Ok(url) } - pub(crate) fn get_api_key(&self) -> String { - format!("{}:{}", self.username, self.password) - } - pub(crate) fn set_authorized_query_params(&self, url: &mut Url, params: T) { let authorized_request_params = AuthorizedRequest { - api_key: self.get_api_key(), + api_key: &self.bearer, inner: params, }; @@ -102,11 +96,10 @@ impl RequestClient { impl Default for RequestClient { fn default() -> Self { - Self { - url: "http://localhost:8088/".parse().expect("failed to parse url"), - username: "asterisk".to_string(), - password: "asterisk".to_string(), - inner: reqwest::Client::new(), - } + Self::new( + "http://localhost:8088/".parse().expect("failed to parse url"), + "asterisk", + "asterisk", + ) } } From e4576bd29125e5be52c4c79dc7b97b3670eafabe Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 15:57:06 +0200 Subject: [PATCH 38/61] chore(ari): make `AuthorizedRequest` private --- arirs/src/request_client/core.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arirs/src/request_client/core.rs b/arirs/src/request_client/core.rs index 2eb3d70..f1b5fba 100644 --- a/arirs/src/request_client/core.rs +++ b/arirs/src/request_client/core.rs @@ -6,13 +6,6 @@ use url::Url; use crate::*; -#[derive(Serialize)] -pub struct AuthorizedRequest<'a, T> { - api_key: &'a str, - #[serde(flatten)] - inner: T, -} - #[derive(Debug, Getters)] pub struct RequestClient { url: Url, @@ -20,6 +13,13 @@ pub struct RequestClient { inner: reqwest::Client, } +#[derive(Serialize)] +struct AuthorizedRequest<'a, T> { + api_key: &'a str, + #[serde(flatten)] + inner: T, +} + impl RequestClient { pub(crate) fn new(url: Url, username: impl AsRef, password: impl AsRef) -> Self { Self { From 2a26a4f9c55e12f6558d0e8d7a385a3c02e25a68 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 16:08:29 +0200 Subject: [PATCH 39/61] refactor(ari): prefix bridge client requests with `bridge_` --- arirs/src/request_client/bridge/core.rs | 29 +++++++++++++------------ 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/arirs/src/request_client/bridge/core.rs b/arirs/src/request_client/bridge/core.rs index b3cf9ff..2673faf 100644 --- a/arirs/src/request_client/bridge/core.rs +++ b/arirs/src/request_client/bridge/core.rs @@ -1,59 +1,60 @@ use crate::*; impl RequestClient { - pub async fn destroy(&self, _bridge_id: &str) -> Result<()> { + pub async fn bridge_create(&self, _bridge_id: &str) -> Result { unimplemented!() } - pub async fn add_channel(&self, _bridge_id: &str, _channel_id: &str) -> Result<()> { + // SUGGESTION(gibbz00): combine with bidge_create by making ID optional + pub async fn bridge_create_with_id(&self, _bridge_id: &str, _bridge: &Bridge) -> Result { unimplemented!() } - pub async fn remove_channel(&self, _bridge_id: &str, _channel_id: &str) -> Result<()> { + pub async fn bridge_get(_client: &RequestClient, _bridge_id: &str) -> Result { unimplemented!() } - pub async fn set_channel_as_video_source(&self, _bridge_id: &str, _channel_id: &str, _video_source_id: &str) -> Result<()> { + pub async fn bidge_list(&self) -> Result> { unimplemented!() } - pub async fn unset_video_source(&self, _bridge_id: &str) -> Result<()> { + pub async fn bridge_destroy(&self, _bridge_id: &str) -> Result<()> { unimplemented!() } - pub async fn bridge_start_moh(&self, _bridge_id: &str, _moh_class: &str) -> Result<()> { + pub async fn bridge_add_channel(&self, _bridge_id: &str, _channel_id: &str) -> Result<()> { unimplemented!() } - pub async fn bridge_stop_moh(&self, _bridge_id: &str) -> Result<()> { + pub async fn bridge_remove_channel(&self, _bridge_id: &str, _channel_id: &str) -> Result<()> { unimplemented!() } - pub async fn bridge_play_media(&self, _bridge_id: &str, _playback: &Playback) -> Result<()> { + pub async fn bridge_set_channel_as_video_source(&self, _bridge_id: &str, _channel_id: &str, _video_source_id: &str) -> Result<()> { unimplemented!() } - pub async fn stop_media(&self, _bridge_id: &str) -> Result<()> { + pub async fn bridge_unset_video_source(&self, _bridge_id: &str) -> Result<()> { unimplemented!() } - pub async fn start_recording(&self, _bridge_id: &str, _recording: &LiveRecording) -> Result<()> { + pub async fn bridge_start_moh(&self, _bridge_id: &str, _moh_class: &str) -> Result<()> { unimplemented!() } - pub async fn list_bridges(&self) -> Result> { + pub async fn bridge_stop_moh(&self, _bridge_id: &str) -> Result<()> { unimplemented!() } - pub async fn create_bridge(&self, _bridge_id: &str) -> Result { + pub async fn bridge_play_media(&self, _bridge_id: &str, _playback: &Playback) -> Result<()> { unimplemented!() } - pub async fn create_bridge_with_id(&self, _bridge_id: &str, _bridge: &Bridge) -> Result { + pub async fn bridge_stop_media(&self, _bridge_id: &str) -> Result<()> { unimplemented!() } - pub async fn get_bridge(_client: &RequestClient, _bridge_id: &str) -> Result { + pub async fn bridge_start_recording(&self, _bridge_id: &str, _recording: &LiveRecording) -> Result<()> { unimplemented!() } } From 81b610ffcff280448f71eb6ea3f3e8ec344d5b6a Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 16:13:30 +0200 Subject: [PATCH 40/61] refactor(ari): prefix channel client requests with `channel_` --- arirs/examples/list-channels.rs | 2 +- arirs/examples/originate.rs | 2 +- arirs/examples/playback.rs | 2 +- arirs/src/request_client/channel/core.rs | 68 +++++++++++++----------- 4 files changed, 41 insertions(+), 33 deletions(-) diff --git a/arirs/examples/list-channels.rs b/arirs/examples/list-channels.rs index 178a8b7..21ee090 100644 --- a/arirs/examples/list-channels.rs +++ b/arirs/examples/list-channels.rs @@ -11,7 +11,7 @@ async fn main() -> Result<()> { let client = RequestClient::default(); - for channel in client.list().await? { + for channel in client.channel_list().await? { debug!("Channel ID: {}", channel.id()); } diff --git a/arirs/examples/originate.rs b/arirs/examples/originate.rs index fb23568..900a1ce 100644 --- a/arirs/examples/originate.rs +++ b/arirs/examples/originate.rs @@ -28,7 +28,7 @@ async fn main() -> Result<()> { formats: &["alaw,ulaw"], }; - client.originate(originate_params, &HashMap::new()).await?; + client.channel_originate(originate_params, &HashMap::new()).await?; Ok(()) } diff --git a/arirs/examples/playback.rs b/arirs/examples/playback.rs index 1185a66..8d2a848 100644 --- a/arirs/examples/playback.rs +++ b/arirs/examples/playback.rs @@ -11,7 +11,7 @@ async fn main() -> Result<(), Box> { while let Some(event) = event_listener.recv().await { if let Event::StasisStart(event) = event { request_client - .play_media( + .channel_play_media( event.channel().id(), PlayMediaParams { playback_id: None, diff --git a/arirs/src/request_client/channel/core.rs b/arirs/src/request_client/channel/core.rs index b4b0ffc..d6ce938 100644 --- a/arirs/src/request_client/channel/core.rs +++ b/arirs/src/request_client/channel/core.rs @@ -3,76 +3,83 @@ use std::collections::HashMap; use crate::*; impl RequestClient { - pub async fn answer(&self, channel_id: &str) -> Result<()> { + pub async fn channel_answer(&self, channel_id: &str) -> Result<()> { self.authorized_post(["channels", channel_id, "answer"], ()).await } - pub async fn hangup(&self, channel_id: &str, reason: Reason) -> Result<()> { + pub async fn channel_hangup(&self, channel_id: &str, reason: Reason) -> Result<()> { self.authorized_delete(["channels", channel_id], reason).await } - pub async fn start_ringing(&self, channel_id: &str) -> Result<()> { + pub async fn channel_start_ringing(&self, channel_id: &str) -> Result<()> { self.authorized_post(["channels", channel_id, "ring"], ()).await } - pub async fn stop_ringing(&self, channel_id: &str) -> Result<()> { + pub async fn channel_stop_ringing(&self, channel_id: &str) -> Result<()> { self.authorized_delete(["channels", channel_id, "ring"], ()).await } - pub async fn send_dtmf(&self, channel_id: &str, params: SendDtmfParams<'_>) -> Result<()> { + pub async fn channel_send_dtmf(&self, channel_id: &str, params: SendDtmfParams<'_>) -> Result<()> { self.authorized_post(["channels", channel_id, "dtmf"], params).await } - pub async fn mute(&self, channel_id: &str, direction: Direction) -> Result<()> { + pub async fn channel_mute(&self, channel_id: &str, direction: Direction) -> Result<()> { self.authorized_post(["channels", channel_id, "mute"], direction).await } - pub async fn unmute(&self, channel_id: &str, direction: Direction) -> Result<()> { + pub async fn channel_unmute(&self, channel_id: &str, direction: Direction) -> Result<()> { self.authorized_delete(["channels", channel_id, "mute"], direction).await } - pub async fn hold(&self, channel_id: &str) -> Result<()> { + pub async fn channel_hold(&self, channel_id: &str) -> Result<()> { self.authorized_post(["channels", channel_id, "hold"], ()).await } - pub async fn unhold(&self, channel_id: &str) -> Result<()> { + pub async fn channel_unhold(&self, channel_id: &str) -> Result<()> { self.authorized_delete(["channels", channel_id, "hold"], ()).await } - pub async fn play_media(&self, channel_id: &str, params: PlayMediaParams<'_>) -> Result { + pub async fn channel_play_media(&self, channel_id: &str, params: PlayMediaParams<'_>) -> Result { self.authorized_post_json_response(["channels", channel_id, "play"], params).await } - pub async fn play_media_with_id(&self, channel_id: &str, playback_id: &str, params: PlayMediaBaseParams<'_>) -> Result { + // SUGGESTION(gibbz00): combine with above method and mave ID optional + pub async fn channel_play_media_with_id( + &self, + channel_id: &str, + playback_id: &str, + params: PlayMediaBaseParams<'_>, + ) -> Result { self.authorized_post_json_response(["channels", channel_id, "play", playback_id, "media"], params) .await } - pub async fn record(&self, channel_id: &str, params: RecordParams<'_>) -> Result { + pub async fn channel_record(&self, channel_id: &str, params: RecordParams<'_>) -> Result { self.authorized_post_json_response(["channels", channel_id, "record"], params).await } - pub async fn dial(&self, channel_id: &str, params: DialParams<'_>) -> Result<()> { + pub async fn channel_dial(&self, channel_id: &str, params: DialParams<'_>) -> Result<()> { self.authorized_post(["channels", channel_id, "dial"], params).await } - pub async fn list(&self) -> Result> { + pub async fn channel_list(&self) -> Result> { self.authorized_get(["channels"], ()).await } - pub async fn create(&self, params: ChannelCreateParams<'_>, variables: &HashMap<&str, &str>) -> Result { + pub async fn channel_create(&self, params: ChannelCreateParams<'_>, variables: &HashMap<&str, &str>) -> Result { self.authorized_post_variables(["channels", "create"], params, variables).await } - pub async fn get(self, channel_id: &str) -> Result { + pub async fn channel_get(self, channel_id: &str) -> Result { self.authorized_get(["channels", channel_id], ()).await } - pub async fn originate<'a>(&self, params: OriginateChannelParams<'a>, variables: &HashMap<&str, &str>) -> Result { + pub async fn channel_originate<'a>(&self, params: OriginateChannelParams<'a>, variables: &HashMap<&str, &str>) -> Result { self.authorized_post_variables(["channels"], params, variables).await } - pub async fn originate_with_id<'a>( + // SUGGESTION(gibbz00): combine with above method and mave ID optional + pub async fn channel_originate_with_id<'a>( &self, channel_id: &str, params: OriginateChannelWithIdParams<'a>, @@ -81,52 +88,53 @@ impl RequestClient { self.authorized_post_variables(["channels", channel_id], params, variables).await } - pub fn start_moh(&self, _channel_id: &str) -> Result<()> { + pub async fn channel_start_moh(&self, _channel_id: &str) -> Result<()> { unimplemented!() } - pub fn stop_moh(&self, _channel_id: &str) -> Result<()> { + pub async fn channel_stop_moh(&self, _channel_id: &str) -> Result<()> { unimplemented!() } - pub fn start_silence(&self, _channel_id: &str) -> Result<()> { + pub async fn channel_silence(&self, _channel_id: &str) -> Result<()> { unimplemented!() } - pub fn stop_silence(&self, _channel_id: &str) -> Result<()> { + pub async fn channel_unsilince(&self, _channel_id: &str) -> Result<()> { unimplemented!() } - pub fn get_variable(&self, _channel_id: &str) -> Result { + pub async fn channel_get_variable(&self, _channel_id: &str) -> Result { unimplemented!() } - pub fn set_variable(&self, _channel_id: &str) -> Result<()> { + pub async fn channel_set_variable(&self, _channel_id: &str) -> Result<()> { unimplemented!() } - pub fn continue_in_dialplan(&self, _channel_id: &str) -> Result<()> { + pub async fn channel_continue_in_dialplan(&self, _channel_id: &str) -> Result<()> { unimplemented!() } /// Transfer the channel to another ARI application. /// Same as `move` in Asterisk - pub fn transfer(&self, _channel_id: &str) -> Result<()> { + pub async fn channel_transfer(&self, _channel_id: &str) -> Result<()> { unimplemented!() } - pub fn get_rtp_statistics(&self, _channel_id: &str) -> Result { + pub async fn channel_get_rtp_statistics(&self, _channel_id: &str) -> Result { unimplemented!() } - pub fn snoop(&self, _channel_id: &str) -> Result { + pub async fn channel_snoop(&self, _channel_id: &str) -> Result { unimplemented!() } - pub fn snoop_with_id(&self, _channel_id: &str) -> Result { + // SUGGESTION(gibbz00): combine with above method and mave ID optional + pub async fn channel_snoop_with_id(&self, _channel_id: &str) -> Result { unimplemented!() } - pub fn start_external_media(&self, _channel_id: &str) -> Result { + pub async fn channel_start_external_media(&self, _channel_id: &str) -> Result { unimplemented!() } } From cbd424a3da170ce5baf01190fff58447ade57a67 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 16:14:20 +0200 Subject: [PATCH 41/61] refactor(ari): prefix playback client requests with `playback_` --- arirs/src/request_client/playback/core.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arirs/src/request_client/playback/core.rs b/arirs/src/request_client/playback/core.rs index 67f3c34..7942f09 100644 --- a/arirs/src/request_client/playback/core.rs +++ b/arirs/src/request_client/playback/core.rs @@ -1,15 +1,15 @@ use crate::*; impl RequestClient { - pub async fn get_playback(&self, _playback_id: &str) -> Result { + pub async fn playback_get(&self, _playback_id: &str) -> Result { unimplemented!() } - pub async fn control(&self, _playback_id: &str, _operation: Operation) -> Result<()> { + pub async fn playback_control(&self, _playback_id: &str, _operation: Operation) -> Result<()> { unimplemented!() } - pub async fn stop(&self, _playback_id: &str) -> Result<()> { + pub async fn playback_stop(&self, _playback_id: &str) -> Result<()> { unimplemented!() } } From ddc3b3d69f09f5bccc7c4cbcfa5a7607d81c28bc Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 16:52:55 +0200 Subject: [PATCH 42/61] feat!(ari): go over WebSocket event handling Also removes `tracing` library lock-in. --- arirs/Cargo.toml | 2 +- arirs/src/client.rs | 54 +++++++++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/arirs/Cargo.toml b/arirs/Cargo.toml index 33da903..2dd839b 100644 --- a/arirs/Cargo.toml +++ b/arirs/Cargo.toml @@ -19,8 +19,8 @@ strum = { version = "0.26", features = ["derive"] } thiserror = "1.0.63" tokio = { version = "1.40.0", features = ["full"] } tokio-tungstenite = "0.23.1" -tracing = { version = "0.1.40", features = ["attributes"] } url = "2.5.2" [dev-dependencies] +tracing = { version = "0.1.40", features = ["attributes"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } diff --git a/arirs/src/client.rs b/arirs/src/client.rs index 81a21a0..a893bcf 100644 --- a/arirs/src/client.rs +++ b/arirs/src/client.rs @@ -4,7 +4,6 @@ use futures_util::{SinkExt, StreamExt}; use rand::Rng; use tokio::{sync::mpsc::UnboundedReceiver, task::JoinHandle, time::interval}; use tokio_tungstenite::{connect_async, tungstenite}; -use tracing::{event, Level}; use url::Url; use crate::*; @@ -12,9 +11,26 @@ use crate::*; pub struct Client; impl Client { - /// Create a new client + /// Create a new Asterisk ARI client /// - /// `url` should end in `/`, `ari/` will be appended to it. + /// Spawns a [`tokio::task`] that connects to the Asterisk + /// WebSocket endpoint to immideatedly listen to incoming [`Event`] + /// (`crate::Event`)s. These may be listened to by polling the returned + /// [`tokio::sync::mpsc::UnboundedReceiver`] stream. + /// + // IMPROVEMENT: return differentiable errors for the respective cases + /// Event listener task is aborted if the listener stream is dropped, + /// or when WebSocket connection toward the Asterisk is dropped, be it + /// gracefully or unintentionally. + /// + /// # Arguments + /// + /// - `url` should end in `/`, `ari/` will be appended to it. + /// + /// # Panics + /// + /// - If called outside a Tokio runtime. + /// - If if the library fails to deserialize incoming asterisk messages pub async fn connect( url: impl AsRef, app_name: impl AsRef, @@ -64,28 +80,27 @@ impl Client { let message = message?; match message { tungstenite::Message::Text(_) => { - if let Err(e) = tx.send(serde_json::from_slice(message.into_data().as_slice()).map_err(|err| AriError::Unknown(err.to_string()))?) { - event!(Level::ERROR, "Error sending event: {}", e); + let event_result = serde_json::from_slice(message.into_data().as_slice()) + .expect("failed to deserialize asterisk event"); + if tx.send(event_result).is_err() { + // Assume that tx has been dropped + break; } } - tungstenite::Message::Ping(data) => { - event!(Level::TRACE, "Received WebSocket Ping, sending Pong"); - ws_sender.send(tungstenite::Message::Pong(data)).await?; - } - tungstenite::Message::Pong(_) => { - event!(Level::TRACE, "Received WebSocket Pong"); - }, - tungstenite::Message::Close(frame) => { - event!(Level::INFO, "WebSocket closed: {:?}", frame); + tungstenite::Message::Ping(data) => { ws_sender.send(tungstenite::Message::Pong(data)).await?; } + tungstenite::Message::Pong(_) => { }, + tungstenite::Message::Close(_frame) => { break; }, - _ => { - event!(Level::INFO, "Unknown WebSocket message"); + tungstenite::Message::Frame(_) => { + unreachable!("raw frame not supposed to be received when reading incoming messages") + } + tungstenite::Message::Binary(_) => { + unreachable!("asterisk should send data marked using text payloads") } } } None => { - tracing::error!("WebSocket closed"); break; } } @@ -94,8 +109,9 @@ impl Client { // every 5 seconds we are sending ping to keep connection alive // https://rust-lang-nursery.github.io/rust-cookbook/algorithms/randomness.html let random_bytes = rand::thread_rng().gen::<[u8; 32]>().to_vec(); - let _ = ws_sender.send(tungstenite::Message::Ping(random_bytes)).await; - event!(Level::DEBUG, "ARI connection ping sent"); + if ws_sender.send(tungstenite::Message::Ping(random_bytes)).await.is_err() { + break; + } } } } From a45a38cc9f5bcbae3354f0361897e397ec96598f Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 19:51:44 +0200 Subject: [PATCH 43/61] feat!(ari): add `ClientError` Begin moving away from crate-wide error. --- Cargo.lock | 1 + arirs/Cargo.toml | 2 +- arirs/src/authorization.rs | 79 ++++++++++ arirs/src/client.rs | 141 +++++++++++------- arirs/src/error.rs | 10 +- arirs/src/lib.rs | 3 + .../request_client/channel/request_params.rs | 40 ----- arirs/src/request_client/core.rs | 32 +--- 8 files changed, 180 insertions(+), 128 deletions(-) create mode 100644 arirs/src/authorization.rs diff --git a/Cargo.lock b/Cargo.lock index e80e4a9..7bb9e2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1537,6 +1537,7 @@ dependencies = [ "rand", "sha1", "thiserror", + "url", "utf-8", ] diff --git a/arirs/Cargo.toml b/arirs/Cargo.toml index 2dd839b..4ee6ab1 100644 --- a/arirs/Cargo.toml +++ b/arirs/Cargo.toml @@ -18,7 +18,7 @@ serde_qs = "0.13" strum = { version = "0.26", features = ["derive"] } thiserror = "1.0.63" tokio = { version = "1.40.0", features = ["full"] } -tokio-tungstenite = "0.23.1" +tokio-tungstenite = { version = "0.23.1", features = ["url"] } url = "2.5.2" [dev-dependencies] diff --git a/arirs/src/authorization.rs b/arirs/src/authorization.rs new file mode 100644 index 0000000..a383efb --- /dev/null +++ b/arirs/src/authorization.rs @@ -0,0 +1,79 @@ +use serde::Serialize; +use url::Url; + +pub struct Authorization; + +#[derive(Serialize)] +struct AuthorizedRequestParams<'a, T> { + api_key: &'a str, + #[serde(flatten)] + inner: T, +} + +impl Authorization { + pub(crate) fn api_key(username: &str, password: &str) -> String { + format!("{}:{}", username, password) + } + + pub(crate) fn build_url<'a, T: Serialize>( + url: &Url, + path: impl AsRef<[&'a str]>, + api_key: &str, + params: T, + ) -> std::result::Result { + let mut url = url.join(&path.as_ref().join("/"))?; + + Self::add_query_parameters(&mut url, api_key, params); + + Ok(url) + } + + fn add_query_parameters(url: &mut Url, api_key: &str, params: T) { + let authorized_request_params = AuthorizedRequestParams { api_key, inner: params }; + + let query_string = serde_qs::to_string(&authorized_request_params).expect("failed to serialize query parameters"); + + url.set_query(Some(&query_string)); + } +} + +#[cfg(test)] +mod tests { + use crate::*; + + #[test] + fn builds_url() { + let url = "http://localhost:8080/".parse().unwrap(); + let api_key = Authorization::api_key("asterisk", "asterisk"); + + let expected = "http://localhost:8080/channel?api_key=asterisk%3Aasterisk&media=sound%3Ahello&lang=en"; + let actual = Authorization::build_url( + &url, + ["channel"], + &api_key, + PlayMediaParams { + playback_id: None, + base_params: PlayMediaBaseParams { + media: &["sound:hello"], + lang: Some("en"), + offset_ms: None, + skip_ms: None, + }, + }, + ) + .unwrap(); + + assert_eq!(expected, actual.as_str()) + } + + #[test] + fn builds_url_with_newtype() { + let url = "http://localhost:8080/".parse().unwrap(); + let api_key = Authorization::api_key("asterisk", "asterisk"); + + let expected = "http://localhost:8080/channel?api_key=asterisk%3Aasterisk"; + let actual = Authorization::build_url(&url, ["channel"], &api_key, ()).unwrap(); + + assert_eq!(expected, actual.as_str()) + } +} diff --git a/arirs/src/client.rs b/arirs/src/client.rs index a893bcf..2f3dcc4 100644 --- a/arirs/src/client.rs +++ b/arirs/src/client.rs @@ -1,13 +1,25 @@ use std::time::Duration; -use futures_util::{SinkExt, StreamExt}; +use futures_util::{SinkExt, StreamExt, TryStreamExt}; use rand::Rng; -use tokio::{sync::mpsc::UnboundedReceiver, task::JoinHandle, time::interval}; +use serde::Serialize; +use thiserror::Error; +use tokio::{sync::mpsc::UnboundedReceiver, time::interval}; use tokio_tungstenite::{connect_async, tungstenite}; use url::Url; use crate::*; +#[derive(Debug, Error)] +pub enum ClientError { + #[error(transparent)] + UrlParseError(#[from] url::ParseError), + #[error("unsupported scheme, expected 'http' or 'https', found: '{}'", .0)] + UnsupportedScheme(String), + #[error("failed to connect")] + WebSocketConnect(tokio_tungstenite::tungstenite::Error), +} + pub struct Client; impl Client { @@ -18,10 +30,9 @@ impl Client { /// (`crate::Event`)s. These may be listened to by polling the returned /// [`tokio::sync::mpsc::UnboundedReceiver`] stream. /// - // IMPROVEMENT: return differentiable errors for the respective cases - /// Event listener task is aborted if the listener stream is dropped, - /// or when WebSocket connection toward the Asterisk is dropped, be it - /// gracefully or unintentionally. + /// Event listener task is stopped if the listener stream is dropped, + /// when WebSocket connection toward the Astbrisk is dropped, be it + /// gracefully or unintentionally. But also when any /// /// # Arguments /// @@ -34,74 +45,94 @@ impl Client { pub async fn connect( url: impl AsRef, app_name: impl AsRef, - username: impl Into, - password: impl Into, - ) -> Result<(RequestClient, UnboundedReceiver)> { - let url = Url::parse(url.as_ref())?.join("ari")?; + username: impl AsRef, + password: impl AsRef, + ) -> std::result::Result<(RequestClient, UnboundedReceiver), ClientError> { + let url = url.as_ref().parse::()?.join("ari/")?; + + let api_key = Authorization::api_key(username.as_ref(), password.as_ref()); + + let ws_url = Self::build_ws_url(&url, &api_key, app_name.as_ref())?; + + let request_client = RequestClient::new(url, api_key); + + let event_listener = Self::connect_ws(&ws_url).await?; - let mut ws_url = url.join("events")?; + Ok((request_client, event_listener)) + } + + fn build_ws_url(base_url: &Url, api_key: &str, app_name: &str) -> std::result::Result { + #[derive(Serialize)] + #[serde(rename_all = "camelCase")] + struct AsteriskWebSocketParams<'a> { + #[serde(rename = "app")] + app_name: &'a str, + subscribe_all: bool, + } + + let mut ws_url = base_url.join("events")?; let scheme = match ws_url.scheme() { "http" => "ws", "https" => "wss", - _ => Err(tungstenite::error::UrlError::UnsupportedUrlScheme)?, + other => Err(ClientError::UnsupportedScheme(other.to_string()))?, }; - - let username = username.into(); - let password = password.into(); - ws_url.set_scheme(scheme).expect("invalid url scheme"); - ws_url - .query_pairs_mut() - .append_pair("app", app_name.as_ref()) - .append_pair("api_key", &format!("{}:{}", username, password)) - .append_pair("subscribeAll", "true"); - - let request_client = RequestClient::new(url, username, password); - - let event_listener = Self::connect_ws(ws_url).await?; - Ok((request_client, event_listener)) + let ws_url = Authorization::build_url( + &ws_url, + [], + api_key, + AsteriskWebSocketParams { + app_name, + subscribe_all: true, + }, + )?; + + Ok(ws_url) } - async fn connect_ws(ws_url: Url) -> Result> { + async fn connect_ws(ws_url: &Url) -> std::result::Result, ClientError> { let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + let (ws_stream, _) = connect_async(ws_url).await.map_err(ClientError::WebSocketConnect)?; - let (ws_stream, _) = connect_async(&ws_url.to_string()).await?; - - let _join_task: JoinHandle> = tokio::task::spawn(async move { + tokio::task::spawn(async move { let (mut ws_sender, mut ws_receiver) = ws_stream.split(); let mut interval = interval(Duration::from_millis(5000)); loop { tokio::select! { - message = ws_receiver.next() => { + message_result = ws_receiver.try_next() => { + let Ok(Some(message)) = message_result else { + // IMPROVEMENT: might be wiser to propagate the + // errors to the event consumer + break; + }; + match message { - Some(message) => { - let message = message?; - match message { - tungstenite::Message::Text(_) => { - let event_result = serde_json::from_slice(message.into_data().as_slice()) - .expect("failed to deserialize asterisk event"); - if tx.send(event_result).is_err() { - // Assume that tx has been dropped - break; - } - } - tungstenite::Message::Ping(data) => { ws_sender.send(tungstenite::Message::Pong(data)).await?; } - tungstenite::Message::Pong(_) => { }, - tungstenite::Message::Close(_frame) => { - break; - }, - tungstenite::Message::Frame(_) => { - unreachable!("raw frame not supposed to be received when reading incoming messages") - } - tungstenite::Message::Binary(_) => { - unreachable!("asterisk should send data marked using text payloads") - } + tungstenite::Message::Text(_) => { + let event_result = serde_json::from_slice(message.into_data().as_slice()) + .expect("failed to deserialize asterisk event"); + if tx.send(event_result).is_err() { + // Assume that tx has been dropped + break; } } - None => { + tungstenite::Message::Ping(data) => { + if ws_sender.send(tungstenite::Message::Pong(data)).await.is_err() { + // IMPROVEMENT: might be wiser to propagate the + // errors to the event consumer + break; + } + } + tungstenite::Message::Pong(_) => { }, + tungstenite::Message::Close(_frame) => { break; + }, + tungstenite::Message::Frame(_) => { + unreachable!("raw frame not supposed to be received when reading incoming messages") + } + tungstenite::Message::Binary(_) => { + unreachable!("asterisk should send data marked using text payloads") } } } @@ -115,8 +146,6 @@ impl Client { } } } - - Ok(()) }); Ok(rx) diff --git a/arirs/src/error.rs b/arirs/src/error.rs index f9e6817..4b66f49 100644 --- a/arirs/src/error.rs +++ b/arirs/src/error.rs @@ -5,18 +5,18 @@ pub type Result = std::result::Result; #[derive(Debug, Error)] pub enum AriError { - #[error("URL parsing error")] - UrlParseError(#[from] url::ParseError), + #[error(transparent)] + UrlParse(#[from] url::ParseError), #[error("WebSocket error")] - TungsteniteError(#[from] tungstenite::Error), + Tungstenite(#[from] tungstenite::Error), #[error("HTTP Request error")] - ReqwestError(#[from] reqwest::Error), + Reqwest(#[from] reqwest::Error), #[error("Unknown error occurred: {0}")] Unknown(String), } impl From for AriError { fn from(err: tungstenite::error::UrlError) -> Self { - AriError::TungsteniteError(err.into()) + AriError::Tungstenite(err.into()) } } diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index 6678395..f2a00a8 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -4,6 +4,9 @@ pub use client::Client; mod request_client; pub use request_client::*; +mod authorization; +pub(crate) use authorization::Authorization; + mod error; pub use error::{AriError, Result}; diff --git a/arirs/src/request_client/channel/request_params.rs b/arirs/src/request_client/channel/request_params.rs index 6497f80..5faf97d 100644 --- a/arirs/src/request_client/channel/request_params.rs +++ b/arirs/src/request_client/channel/request_params.rs @@ -193,43 +193,3 @@ impl Serialize for Reason { map.end() } } - -#[cfg(test)] -mod tests { - use crate::*; - - #[test] - fn serializes_parameters() { - let request_client = RequestClient::new("http://localhost:8080/".parse().unwrap(), "asterisk", "asterisk"); - - let mut url = request_client.url().join("channel").unwrap(); - - request_client.set_authorized_query_params( - &mut url, - PlayMediaParams { - playback_id: None, - base_params: PlayMediaBaseParams { - media: &["sound:hello"], - lang: Some("en"), - offset_ms: None, - skip_ms: None, - }, - }, - ); - - let expected = "http://localhost:8080/channel?api_key=asterisk%3Aasterisk&media=sound%3Ahello&lang=en"; - assert_eq!(expected, url.as_str()) - } - - #[test] - fn serializes_unit_type() { - let request_client = RequestClient::new("http://localhost:8080/".parse().unwrap(), "asterisk", "asterisk"); - - let mut url = request_client.url().join("channel").unwrap(); - - request_client.set_authorized_query_params(&mut url, ()); - - let expected = "http://localhost:8080/channel?api_key=asterisk%3Aasterisk"; - assert_eq!(expected, url.as_str()) - } -} diff --git a/arirs/src/request_client/core.rs b/arirs/src/request_client/core.rs index f1b5fba..57acdee 100644 --- a/arirs/src/request_client/core.rs +++ b/arirs/src/request_client/core.rs @@ -9,22 +9,15 @@ use crate::*; #[derive(Debug, Getters)] pub struct RequestClient { url: Url, - bearer: String, + api_key: String, inner: reqwest::Client, } -#[derive(Serialize)] -struct AuthorizedRequest<'a, T> { - api_key: &'a str, - #[serde(flatten)] - inner: T, -} - impl RequestClient { - pub(crate) fn new(url: Url, username: impl AsRef, password: impl AsRef) -> Self { + pub(crate) fn new(url: Url, api_key: String) -> Self { Self { url, - bearer: format!("{}:{}", username.as_ref(), password.as_ref()), + api_key, inner: reqwest::Client::new(), } } @@ -77,20 +70,8 @@ impl RequestClient { Ok(()) } - fn authorized_url<'a, T: Serialize>(&self, path: impl AsRef<[&'a str]>, params: T) -> Result { - let mut url = self.url().join(&path.as_ref().join("/"))?; - self.set_authorized_query_params(&mut url, params); - Ok(url) - } - - pub(crate) fn set_authorized_query_params(&self, url: &mut Url, params: T) { - let authorized_request_params = AuthorizedRequest { - api_key: &self.bearer, - inner: params, - }; - - let query_string = serde_qs::to_string(&authorized_request_params).expect("failed to serialize query parameters"); - url.set_query(Some(&query_string)); + fn authorized_url<'a, T: Serialize>(&self, path: impl AsRef<[&'a str]>, params: T) -> std::result::Result { + Authorization::build_url(&self.url, path, &self.api_key, params) } } @@ -98,8 +79,7 @@ impl Default for RequestClient { fn default() -> Self { Self::new( "http://localhost:8088/".parse().expect("failed to parse url"), - "asterisk", - "asterisk", + Authorization::api_key("asterisk", "asterisk"), ) } } From c9995c7fb458c734076ecbd7af9313d3f85265c0 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 19:56:23 +0200 Subject: [PATCH 44/61] chore!(ari): remove `AriError::Tungstenite` --- arirs/src/error.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/arirs/src/error.rs b/arirs/src/error.rs index 4b66f49..9032598 100644 --- a/arirs/src/error.rs +++ b/arirs/src/error.rs @@ -1,5 +1,4 @@ use thiserror::Error; -use tokio_tungstenite::tungstenite; pub type Result = std::result::Result; @@ -7,16 +6,8 @@ pub type Result = std::result::Result; pub enum AriError { #[error(transparent)] UrlParse(#[from] url::ParseError), - #[error("WebSocket error")] - Tungstenite(#[from] tungstenite::Error), #[error("HTTP Request error")] Reqwest(#[from] reqwest::Error), #[error("Unknown error occurred: {0}")] Unknown(String), } - -impl From for AriError { - fn from(err: tungstenite::error::UrlError) -> Self { - AriError::Tungstenite(err.into()) - } -} From 844764d16139399d8f9a093575e7d9c30d9d1e79 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 19:56:49 +0200 Subject: [PATCH 45/61] chore!(ari): remove `AriError::Unknown` --- arirs/src/error.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/arirs/src/error.rs b/arirs/src/error.rs index 9032598..2133bcd 100644 --- a/arirs/src/error.rs +++ b/arirs/src/error.rs @@ -8,6 +8,4 @@ pub enum AriError { UrlParse(#[from] url::ParseError), #[error("HTTP Request error")] Reqwest(#[from] reqwest::Error), - #[error("Unknown error occurred: {0}")] - Unknown(String), } From fddd6b1b8fac3d347164accf99761905ce6265b4 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 19:58:46 +0200 Subject: [PATCH 46/61] chore!(ari): remove `AriError::UrlParse` --- arirs/src/error.rs | 2 -- arirs/src/request_client/core.rs | 14 +++++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/arirs/src/error.rs b/arirs/src/error.rs index 2133bcd..5c307d0 100644 --- a/arirs/src/error.rs +++ b/arirs/src/error.rs @@ -4,8 +4,6 @@ pub type Result = std::result::Result; #[derive(Debug, Error)] pub enum AriError { - #[error(transparent)] - UrlParse(#[from] url::ParseError), #[error("HTTP Request error")] Reqwest(#[from] reqwest::Error), } diff --git a/arirs/src/request_client/core.rs b/arirs/src/request_client/core.rs index 57acdee..d4cb72f 100644 --- a/arirs/src/request_client/core.rs +++ b/arirs/src/request_client/core.rs @@ -23,13 +23,13 @@ impl RequestClient { } pub(crate) async fn authorized_get(&self, path: impl AsRef<[&str]>, params: T) -> Result { - let url = self.authorized_url(path, params)?; + let url = self.authorized_url(path, params); let response = self.inner.get(url).send().await?.json().await?; Ok(response) } pub(crate) async fn authorized_post(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { - let url = self.authorized_url(path, params)?; + let url = self.authorized_url(path, params); self.inner.post(url).send().await?; Ok(()) } @@ -39,7 +39,7 @@ impl RequestClient { path: impl AsRef<[&str]>, params: T, ) -> Result { - let url = self.authorized_url(path, params)?; + let url = self.authorized_url(path, params); let response = self.inner.post(url).send().await?.json().await?; Ok(response) } @@ -50,7 +50,7 @@ impl RequestClient { params: T, variables: &HashMap<&str, &str>, ) -> Result { - let url = self.authorized_url(path, params)?; + let url = self.authorized_url(path, params); let response = self .inner .post(url) @@ -65,13 +65,13 @@ impl RequestClient { } pub(crate) async fn authorized_delete(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { - let url = self.authorized_url(path, params)?; + let url = self.authorized_url(path, params); self.inner.delete(url).send().await?; Ok(()) } - fn authorized_url<'a, T: Serialize>(&self, path: impl AsRef<[&'a str]>, params: T) -> std::result::Result { - Authorization::build_url(&self.url, path, &self.api_key, params) + fn authorized_url<'a, T: Serialize>(&self, path: impl AsRef<[&'a str]>, params: T) -> Url { + Authorization::build_url(&self.url, path, &self.api_key, params).expect("failed to create internally built url") } } From 336df7c5e3b94ccb78a458f4b0c759460a94a4e1 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 20:05:00 +0200 Subject: [PATCH 47/61] refactor!(ari): replace `AriError` with `RequestClientError` --- arirs/examples/list-channels.rs | 4 +- arirs/examples/originate.rs | 4 +- arirs/src/error.rs | 9 --- arirs/src/lib.rs | 3 - arirs/src/request_client/bridge/core.rs | 33 ++++++----- arirs/src/request_client/channel/core.rs | 64 ++++++++++++---------- arirs/src/request_client/core.rs | 23 ++++++-- arirs/src/request_client/mod.rs | 3 +- arirs/src/request_client/playback/core.rs | 6 +- arirs/src/request_client/recording/core.rs | 22 ++++---- 10 files changed, 91 insertions(+), 80 deletions(-) delete mode 100644 arirs/src/error.rs diff --git a/arirs/examples/list-channels.rs b/arirs/examples/list-channels.rs index 21ee090..6a17067 100644 --- a/arirs/examples/list-channels.rs +++ b/arirs/examples/list-channels.rs @@ -1,9 +1,9 @@ -use arirs::{RequestClient, Result}; +use arirs::{RequestClient, RequestClientError}; use tracing::debug; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; #[tokio::main] -async fn main() -> Result<()> { +async fn main() -> Result<(), RequestClientError> { tracing_subscriber::registry() .with(fmt::layer()) .with(EnvFilter::from_default_env()) diff --git a/arirs/examples/originate.rs b/arirs/examples/originate.rs index 900a1ce..3b9767a 100644 --- a/arirs/examples/originate.rs +++ b/arirs/examples/originate.rs @@ -1,12 +1,12 @@ use std::collections::HashMap; -use arirs::{OriginateChannelParams, OriginateParams, RequestClient, Result}; +use arirs::{OriginateChannelParams, OriginateParams, RequestClient, RequestClientError}; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; const APP_NAME: &str = "ari"; #[tokio::main] -async fn main() -> Result<()> { +async fn main() -> Result<(), RequestClientError> { tracing_subscriber::registry() .with(fmt::layer()) .with(EnvFilter::from_default_env()) diff --git a/arirs/src/error.rs b/arirs/src/error.rs deleted file mode 100644 index 5c307d0..0000000 --- a/arirs/src/error.rs +++ /dev/null @@ -1,9 +0,0 @@ -use thiserror::Error; - -pub type Result = std::result::Result; - -#[derive(Debug, Error)] -pub enum AriError { - #[error("HTTP Request error")] - Reqwest(#[from] reqwest::Error), -} diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index f2a00a8..4e2ffb3 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -7,8 +7,5 @@ pub use request_client::*; mod authorization; pub(crate) use authorization::Authorization; -mod error; -pub use error::{AriError, Result}; - mod event; pub use event::Event; diff --git a/arirs/src/request_client/bridge/core.rs b/arirs/src/request_client/bridge/core.rs index 2673faf..44fa675 100644 --- a/arirs/src/request_client/bridge/core.rs +++ b/arirs/src/request_client/bridge/core.rs @@ -1,60 +1,65 @@ use crate::*; impl RequestClient { - pub async fn bridge_create(&self, _bridge_id: &str) -> Result { + pub async fn bridge_create(&self, _bridge_id: &str) -> RequestClientResult { unimplemented!() } // SUGGESTION(gibbz00): combine with bidge_create by making ID optional - pub async fn bridge_create_with_id(&self, _bridge_id: &str, _bridge: &Bridge) -> Result { + pub async fn bridge_create_with_id(&self, _bridge_id: &str, _bridge: &Bridge) -> RequestClientResult { unimplemented!() } - pub async fn bridge_get(_client: &RequestClient, _bridge_id: &str) -> Result { + pub async fn bridge_get(_client: &RequestClient, _bridge_id: &str) -> RequestClientResult { unimplemented!() } - pub async fn bidge_list(&self) -> Result> { + pub async fn bidge_list(&self) -> RequestClientResult> { unimplemented!() } - pub async fn bridge_destroy(&self, _bridge_id: &str) -> Result<()> { + pub async fn bridge_destroy(&self, _bridge_id: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn bridge_add_channel(&self, _bridge_id: &str, _channel_id: &str) -> Result<()> { + pub async fn bridge_add_channel(&self, _bridge_id: &str, _channel_id: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn bridge_remove_channel(&self, _bridge_id: &str, _channel_id: &str) -> Result<()> { + pub async fn bridge_remove_channel(&self, _bridge_id: &str, _channel_id: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn bridge_set_channel_as_video_source(&self, _bridge_id: &str, _channel_id: &str, _video_source_id: &str) -> Result<()> { + pub async fn bridge_set_channel_as_video_source( + &self, + _bridge_id: &str, + _channel_id: &str, + _video_source_id: &str, + ) -> RequestClientResult<()> { unimplemented!() } - pub async fn bridge_unset_video_source(&self, _bridge_id: &str) -> Result<()> { + pub async fn bridge_unset_video_source(&self, _bridge_id: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn bridge_start_moh(&self, _bridge_id: &str, _moh_class: &str) -> Result<()> { + pub async fn bridge_start_moh(&self, _bridge_id: &str, _moh_class: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn bridge_stop_moh(&self, _bridge_id: &str) -> Result<()> { + pub async fn bridge_stop_moh(&self, _bridge_id: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn bridge_play_media(&self, _bridge_id: &str, _playback: &Playback) -> Result<()> { + pub async fn bridge_play_media(&self, _bridge_id: &str, _playback: &Playback) -> RequestClientResult<()> { unimplemented!() } - pub async fn bridge_stop_media(&self, _bridge_id: &str) -> Result<()> { + pub async fn bridge_stop_media(&self, _bridge_id: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn bridge_start_recording(&self, _bridge_id: &str, _recording: &LiveRecording) -> Result<()> { + pub async fn bridge_start_recording(&self, _bridge_id: &str, _recording: &LiveRecording) -> RequestClientResult<()> { unimplemented!() } } diff --git a/arirs/src/request_client/channel/core.rs b/arirs/src/request_client/channel/core.rs index d6ce938..0775816 100644 --- a/arirs/src/request_client/channel/core.rs +++ b/arirs/src/request_client/channel/core.rs @@ -3,43 +3,43 @@ use std::collections::HashMap; use crate::*; impl RequestClient { - pub async fn channel_answer(&self, channel_id: &str) -> Result<()> { + pub async fn channel_answer(&self, channel_id: &str) -> RequestClientResult<()> { self.authorized_post(["channels", channel_id, "answer"], ()).await } - pub async fn channel_hangup(&self, channel_id: &str, reason: Reason) -> Result<()> { + pub async fn channel_hangup(&self, channel_id: &str, reason: Reason) -> RequestClientResult<()> { self.authorized_delete(["channels", channel_id], reason).await } - pub async fn channel_start_ringing(&self, channel_id: &str) -> Result<()> { + pub async fn channel_start_ringing(&self, channel_id: &str) -> RequestClientResult<()> { self.authorized_post(["channels", channel_id, "ring"], ()).await } - pub async fn channel_stop_ringing(&self, channel_id: &str) -> Result<()> { + pub async fn channel_stop_ringing(&self, channel_id: &str) -> RequestClientResult<()> { self.authorized_delete(["channels", channel_id, "ring"], ()).await } - pub async fn channel_send_dtmf(&self, channel_id: &str, params: SendDtmfParams<'_>) -> Result<()> { + pub async fn channel_send_dtmf(&self, channel_id: &str, params: SendDtmfParams<'_>) -> RequestClientResult<()> { self.authorized_post(["channels", channel_id, "dtmf"], params).await } - pub async fn channel_mute(&self, channel_id: &str, direction: Direction) -> Result<()> { + pub async fn channel_mute(&self, channel_id: &str, direction: Direction) -> RequestClientResult<()> { self.authorized_post(["channels", channel_id, "mute"], direction).await } - pub async fn channel_unmute(&self, channel_id: &str, direction: Direction) -> Result<()> { + pub async fn channel_unmute(&self, channel_id: &str, direction: Direction) -> RequestClientResult<()> { self.authorized_delete(["channels", channel_id, "mute"], direction).await } - pub async fn channel_hold(&self, channel_id: &str) -> Result<()> { + pub async fn channel_hold(&self, channel_id: &str) -> RequestClientResult<()> { self.authorized_post(["channels", channel_id, "hold"], ()).await } - pub async fn channel_unhold(&self, channel_id: &str) -> Result<()> { + pub async fn channel_unhold(&self, channel_id: &str) -> RequestClientResult<()> { self.authorized_delete(["channels", channel_id, "hold"], ()).await } - pub async fn channel_play_media(&self, channel_id: &str, params: PlayMediaParams<'_>) -> Result { + pub async fn channel_play_media(&self, channel_id: &str, params: PlayMediaParams<'_>) -> RequestClientResult { self.authorized_post_json_response(["channels", channel_id, "play"], params).await } @@ -49,32 +49,36 @@ impl RequestClient { channel_id: &str, playback_id: &str, params: PlayMediaBaseParams<'_>, - ) -> Result { + ) -> RequestClientResult { self.authorized_post_json_response(["channels", channel_id, "play", playback_id, "media"], params) .await } - pub async fn channel_record(&self, channel_id: &str, params: RecordParams<'_>) -> Result { + pub async fn channel_record(&self, channel_id: &str, params: RecordParams<'_>) -> RequestClientResult { self.authorized_post_json_response(["channels", channel_id, "record"], params).await } - pub async fn channel_dial(&self, channel_id: &str, params: DialParams<'_>) -> Result<()> { + pub async fn channel_dial(&self, channel_id: &str, params: DialParams<'_>) -> RequestClientResult<()> { self.authorized_post(["channels", channel_id, "dial"], params).await } - pub async fn channel_list(&self) -> Result> { + pub async fn channel_list(&self) -> RequestClientResult> { self.authorized_get(["channels"], ()).await } - pub async fn channel_create(&self, params: ChannelCreateParams<'_>, variables: &HashMap<&str, &str>) -> Result { + pub async fn channel_create(&self, params: ChannelCreateParams<'_>, variables: &HashMap<&str, &str>) -> RequestClientResult { self.authorized_post_variables(["channels", "create"], params, variables).await } - pub async fn channel_get(self, channel_id: &str) -> Result { + pub async fn channel_get(self, channel_id: &str) -> RequestClientResult { self.authorized_get(["channels", channel_id], ()).await } - pub async fn channel_originate<'a>(&self, params: OriginateChannelParams<'a>, variables: &HashMap<&str, &str>) -> Result { + pub async fn channel_originate<'a>( + &self, + params: OriginateChannelParams<'a>, + variables: &HashMap<&str, &str>, + ) -> RequestClientResult { self.authorized_post_variables(["channels"], params, variables).await } @@ -84,57 +88,57 @@ impl RequestClient { channel_id: &str, params: OriginateChannelWithIdParams<'a>, variables: &HashMap<&str, &str>, - ) -> Result { + ) -> RequestClientResult { self.authorized_post_variables(["channels", channel_id], params, variables).await } - pub async fn channel_start_moh(&self, _channel_id: &str) -> Result<()> { + pub async fn channel_start_moh(&self, _channel_id: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn channel_stop_moh(&self, _channel_id: &str) -> Result<()> { + pub async fn channel_stop_moh(&self, _channel_id: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn channel_silence(&self, _channel_id: &str) -> Result<()> { + pub async fn channel_silence(&self, _channel_id: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn channel_unsilince(&self, _channel_id: &str) -> Result<()> { + pub async fn channel_unsilince(&self, _channel_id: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn channel_get_variable(&self, _channel_id: &str) -> Result { + pub async fn channel_get_variable(&self, _channel_id: &str) -> RequestClientResult { unimplemented!() } - pub async fn channel_set_variable(&self, _channel_id: &str) -> Result<()> { + pub async fn channel_set_variable(&self, _channel_id: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn channel_continue_in_dialplan(&self, _channel_id: &str) -> Result<()> { + pub async fn channel_continue_in_dialplan(&self, _channel_id: &str) -> RequestClientResult<()> { unimplemented!() } /// Transfer the channel to another ARI application. /// Same as `move` in Asterisk - pub async fn channel_transfer(&self, _channel_id: &str) -> Result<()> { + pub async fn channel_transfer(&self, _channel_id: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn channel_get_rtp_statistics(&self, _channel_id: &str) -> Result { + pub async fn channel_get_rtp_statistics(&self, _channel_id: &str) -> RequestClientResult { unimplemented!() } - pub async fn channel_snoop(&self, _channel_id: &str) -> Result { + pub async fn channel_snoop(&self, _channel_id: &str) -> RequestClientResult { unimplemented!() } // SUGGESTION(gibbz00): combine with above method and mave ID optional - pub async fn channel_snoop_with_id(&self, _channel_id: &str) -> Result { + pub async fn channel_snoop_with_id(&self, _channel_id: &str) -> RequestClientResult { unimplemented!() } - pub async fn channel_start_external_media(&self, _channel_id: &str) -> Result { + pub async fn channel_start_external_media(&self, _channel_id: &str) -> RequestClientResult { unimplemented!() } } diff --git a/arirs/src/request_client/core.rs b/arirs/src/request_client/core.rs index d4cb72f..9eb3d34 100644 --- a/arirs/src/request_client/core.rs +++ b/arirs/src/request_client/core.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use derive_getters::Getters; use serde::{de::DeserializeOwned, Serialize}; +use thiserror::Error; use url::Url; use crate::*; @@ -13,6 +14,14 @@ pub struct RequestClient { inner: reqwest::Client, } +pub(crate) type RequestClientResult = std::result::Result; + +#[derive(Debug, Error)] +pub enum RequestClientError { + #[error("encountered inner HTTP client error")] + Reqwest(#[from] reqwest::Error), +} + impl RequestClient { pub(crate) fn new(url: Url, api_key: String) -> Self { Self { @@ -22,13 +31,17 @@ impl RequestClient { } } - pub(crate) async fn authorized_get(&self, path: impl AsRef<[&str]>, params: T) -> Result { + pub(crate) async fn authorized_get( + &self, + path: impl AsRef<[&str]>, + params: T, + ) -> RequestClientResult { let url = self.authorized_url(path, params); let response = self.inner.get(url).send().await?.json().await?; Ok(response) } - pub(crate) async fn authorized_post(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { + pub(crate) async fn authorized_post(&self, path: impl AsRef<[&str]>, params: T) -> RequestClientResult<()> { let url = self.authorized_url(path, params); self.inner.post(url).send().await?; Ok(()) @@ -38,7 +51,7 @@ impl RequestClient { &self, path: impl AsRef<[&str]>, params: T, - ) -> Result { + ) -> RequestClientResult { let url = self.authorized_url(path, params); let response = self.inner.post(url).send().await?.json().await?; Ok(response) @@ -49,7 +62,7 @@ impl RequestClient { path: impl AsRef<[&str]>, params: T, variables: &HashMap<&str, &str>, - ) -> Result { + ) -> RequestClientResult { let url = self.authorized_url(path, params); let response = self .inner @@ -64,7 +77,7 @@ impl RequestClient { Ok(response) } - pub(crate) async fn authorized_delete(&self, path: impl AsRef<[&str]>, params: T) -> Result<()> { + pub(crate) async fn authorized_delete(&self, path: impl AsRef<[&str]>, params: T) -> RequestClientResult<()> { let url = self.authorized_url(path, params); self.inner.delete(url).send().await?; Ok(()) diff --git a/arirs/src/request_client/mod.rs b/arirs/src/request_client/mod.rs index 9c8cc59..dd7eeb3 100644 --- a/arirs/src/request_client/mod.rs +++ b/arirs/src/request_client/mod.rs @@ -1,5 +1,6 @@ mod core; -pub use core::RequestClient; +pub(crate) use core::RequestClientResult; +pub use core::{RequestClient, RequestClientError}; mod bridge; pub use bridge::*; diff --git a/arirs/src/request_client/playback/core.rs b/arirs/src/request_client/playback/core.rs index 7942f09..713d59c 100644 --- a/arirs/src/request_client/playback/core.rs +++ b/arirs/src/request_client/playback/core.rs @@ -1,15 +1,15 @@ use crate::*; impl RequestClient { - pub async fn playback_get(&self, _playback_id: &str) -> Result { + pub async fn playback_get(&self, _playback_id: &str) -> RequestClientResult { unimplemented!() } - pub async fn playback_control(&self, _playback_id: &str, _operation: Operation) -> Result<()> { + pub async fn playback_control(&self, _playback_id: &str, _operation: Operation) -> RequestClientResult<()> { unimplemented!() } - pub async fn playback_stop(&self, _playback_id: &str) -> Result<()> { + pub async fn playback_stop(&self, _playback_id: &str) -> RequestClientResult<()> { unimplemented!() } } diff --git a/arirs/src/request_client/recording/core.rs b/arirs/src/request_client/recording/core.rs index 7243ed3..a897967 100644 --- a/arirs/src/request_client/recording/core.rs +++ b/arirs/src/request_client/recording/core.rs @@ -1,50 +1,50 @@ use crate::*; impl RequestClient { - pub async fn live_recording_get(&self, _recording_name: &str) -> Result { + pub async fn live_recording_get(&self, _recording_name: &str) -> RequestClientResult { unimplemented!() } - pub async fn live_recording_discard(&self, _recording_name: &str) -> Result<()> { + pub async fn live_recording_discard(&self, _recording_name: &str) -> RequestClientResult<()> { unimplemented!() } // TODO: explore if it's possible to return a StoredRecording - pub async fn live_recording_stop(&self, _recording_name: &str) -> Result<()> { + pub async fn live_recording_stop(&self, _recording_name: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn live_recording_pause(&self, _recording_name: &str) -> Result<()> { + pub async fn live_recording_pause(&self, _recording_name: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn live_recording_resume(&self, _recording_name: &str) -> Result<()> { + pub async fn live_recording_resume(&self, _recording_name: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn live_recording_mute(&self, _recording_name: &str) -> Result<()> { + pub async fn live_recording_mute(&self, _recording_name: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn live_recording_unmute(&self, _recording_name: &str) -> Result<()> { + pub async fn live_recording_unmute(&self, _recording_name: &str) -> RequestClientResult<()> { unimplemented!() } } impl RequestClient { - pub async fn stored_recording_list(&self, _recording_name: &str) -> Result> { + pub async fn stored_recording_list(&self, _recording_name: &str) -> RequestClientResult> { unimplemented!() } - pub async fn stored_recording_get(&self, _recording_name: &str) -> Result { + pub async fn stored_recording_get(&self, _recording_name: &str) -> RequestClientResult { unimplemented!() } - pub async fn stored_recording_delete(&self, _recording_name: &str) -> Result<()> { + pub async fn stored_recording_delete(&self, _recording_name: &str) -> RequestClientResult<()> { unimplemented!() } - pub async fn stored_recording_download(&self, _recording_name: &str) -> Result<&[u8]> { + pub async fn stored_recording_download(&self, _recording_name: &str) -> RequestClientResult<&[u8]> { unimplemented!() } } From 228921e7c6c05d6203c727c977e821386ed1ef2f Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 20:07:17 +0200 Subject: [PATCH 48/61] refactor(ari): remove fully qualified `std::result::Result` usage --- arirs/src/authorization.rs | 2 +- arirs/src/client.rs | 8 ++++---- arirs/src/request_client/channel/request_params.rs | 4 ++-- arirs/src/request_client/core.rs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/arirs/src/authorization.rs b/arirs/src/authorization.rs index a383efb..3e50155 100644 --- a/arirs/src/authorization.rs +++ b/arirs/src/authorization.rs @@ -20,7 +20,7 @@ impl Authorization { path: impl AsRef<[&'a str]>, api_key: &str, params: T, - ) -> std::result::Result { + ) -> Result { let mut url = url.join(&path.as_ref().join("/"))?; Self::add_query_parameters(&mut url, api_key, params); diff --git a/arirs/src/client.rs b/arirs/src/client.rs index 2f3dcc4..f9a920f 100644 --- a/arirs/src/client.rs +++ b/arirs/src/client.rs @@ -1,4 +1,4 @@ -use std::time::Duration; +use std::{result::Result, time::Duration}; use futures_util::{SinkExt, StreamExt, TryStreamExt}; use rand::Rng; @@ -47,7 +47,7 @@ impl Client { app_name: impl AsRef, username: impl AsRef, password: impl AsRef, - ) -> std::result::Result<(RequestClient, UnboundedReceiver), ClientError> { + ) -> Result<(RequestClient, UnboundedReceiver), ClientError> { let url = url.as_ref().parse::()?.join("ari/")?; let api_key = Authorization::api_key(username.as_ref(), password.as_ref()); @@ -61,7 +61,7 @@ impl Client { Ok((request_client, event_listener)) } - fn build_ws_url(base_url: &Url, api_key: &str, app_name: &str) -> std::result::Result { + fn build_ws_url(base_url: &Url, api_key: &str, app_name: &str) -> Result { #[derive(Serialize)] #[serde(rename_all = "camelCase")] struct AsteriskWebSocketParams<'a> { @@ -91,7 +91,7 @@ impl Client { Ok(ws_url) } - async fn connect_ws(ws_url: &Url) -> std::result::Result, ClientError> { + async fn connect_ws(ws_url: &Url) -> Result, ClientError> { let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); let (ws_stream, _) = connect_async(ws_url).await.map_err(ClientError::WebSocketConnect)?; diff --git a/arirs/src/request_client/channel/request_params.rs b/arirs/src/request_client/channel/request_params.rs index 5faf97d..d5970b0 100644 --- a/arirs/src/request_client/channel/request_params.rs +++ b/arirs/src/request_client/channel/request_params.rs @@ -141,7 +141,7 @@ pub struct ChannelCreateParams<'a> { pub formats: &'a [&'a str], } -fn join_serialize(slice: &[&str], s: S) -> std::result::Result +fn join_serialize(slice: &[&str], s: S) -> Result where S: Serializer, { @@ -181,7 +181,7 @@ pub enum Reason { } impl Serialize for Reason { - fn serialize(&self, serializer: S) -> std::result::Result + fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { diff --git a/arirs/src/request_client/core.rs b/arirs/src/request_client/core.rs index 9eb3d34..cd839ee 100644 --- a/arirs/src/request_client/core.rs +++ b/arirs/src/request_client/core.rs @@ -14,7 +14,7 @@ pub struct RequestClient { inner: reqwest::Client, } -pub(crate) type RequestClientResult = std::result::Result; +pub(crate) type RequestClientResult = Result; #[derive(Debug, Error)] pub enum RequestClientError { From 8b2d75c715f21b28a6dd9c77a43f38b2ae700ffa Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 20:12:47 +0200 Subject: [PATCH 49/61] refactor!(ari): rename `Client` to `Asterisk` --- arirs/examples/dtmf.rs | 4 ++-- arirs/examples/playback.rs | 4 ++-- arirs/src/{client.rs => asterisk.rs} | 18 +++++++++--------- arirs/src/lib.rs | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) rename arirs/src/{client.rs => asterisk.rs} (93%) diff --git a/arirs/examples/dtmf.rs b/arirs/examples/dtmf.rs index a5f8db7..9dcd68d 100644 --- a/arirs/examples/dtmf.rs +++ b/arirs/examples/dtmf.rs @@ -1,6 +1,6 @@ use std::sync::{Arc, Mutex}; -use arirs::{Client, Event}; +use arirs::{Asterisk, Event}; use tracing::debug; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; @@ -13,7 +13,7 @@ async fn main() -> Result<(), Box> { let dtmf_buffer = Arc::new(Mutex::new(String::new())); - let (_, mut event_listener) = Client::connect("http://localhost:8088/", "asterisk", "asterisk", "ari").await?; + let (_, mut event_listener) = Asterisk::connect("http://localhost:8088/", "asterisk", "asterisk", "ari").await?; while let Some(event) = event_listener.recv().await { match event { diff --git a/arirs/examples/playback.rs b/arirs/examples/playback.rs index 8d2a848..06a34aa 100644 --- a/arirs/examples/playback.rs +++ b/arirs/examples/playback.rs @@ -1,4 +1,4 @@ -use arirs::{Client, Event, PlayMediaBaseParams, PlayMediaParams}; +use arirs::{Asterisk, Event, PlayMediaBaseParams, PlayMediaParams}; use tracing::level_filters::LevelFilter; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; @@ -6,7 +6,7 @@ use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; async fn main() -> Result<(), Box> { tracing_subscriber::registry().with(fmt::layer()).with(LevelFilter::TRACE).init(); - let (request_client, mut event_listener) = Client::connect("http://localhost:8088/", "asterisk", "asterisk", "ari").await?; + let (request_client, mut event_listener) = Asterisk::connect("http://localhost:8088/", "asterisk", "asterisk", "ari").await?; while let Some(event) = event_listener.recv().await { if let Event::StasisStart(event) = event { diff --git a/arirs/src/client.rs b/arirs/src/asterisk.rs similarity index 93% rename from arirs/src/client.rs rename to arirs/src/asterisk.rs index f9a920f..545b967 100644 --- a/arirs/src/client.rs +++ b/arirs/src/asterisk.rs @@ -11,7 +11,7 @@ use url::Url; use crate::*; #[derive(Debug, Error)] -pub enum ClientError { +pub enum AsteriskError { #[error(transparent)] UrlParseError(#[from] url::ParseError), #[error("unsupported scheme, expected 'http' or 'https', found: '{}'", .0)] @@ -20,10 +20,10 @@ pub enum ClientError { WebSocketConnect(tokio_tungstenite::tungstenite::Error), } -pub struct Client; +pub struct Asterisk; -impl Client { - /// Create a new Asterisk ARI client +impl Asterisk { + /// Connect to the Asterisk, return an ARI request client and an WebSocket event stream. /// /// Spawns a [`tokio::task`] that connects to the Asterisk /// WebSocket endpoint to immideatedly listen to incoming [`Event`] @@ -47,7 +47,7 @@ impl Client { app_name: impl AsRef, username: impl AsRef, password: impl AsRef, - ) -> Result<(RequestClient, UnboundedReceiver), ClientError> { + ) -> Result<(RequestClient, UnboundedReceiver), AsteriskError> { let url = url.as_ref().parse::()?.join("ari/")?; let api_key = Authorization::api_key(username.as_ref(), password.as_ref()); @@ -61,7 +61,7 @@ impl Client { Ok((request_client, event_listener)) } - fn build_ws_url(base_url: &Url, api_key: &str, app_name: &str) -> Result { + fn build_ws_url(base_url: &Url, api_key: &str, app_name: &str) -> Result { #[derive(Serialize)] #[serde(rename_all = "camelCase")] struct AsteriskWebSocketParams<'a> { @@ -74,7 +74,7 @@ impl Client { let scheme = match ws_url.scheme() { "http" => "ws", "https" => "wss", - other => Err(ClientError::UnsupportedScheme(other.to_string()))?, + other => Err(AsteriskError::UnsupportedScheme(other.to_string()))?, }; ws_url.set_scheme(scheme).expect("invalid url scheme"); @@ -91,9 +91,9 @@ impl Client { Ok(ws_url) } - async fn connect_ws(ws_url: &Url) -> Result, ClientError> { + async fn connect_ws(ws_url: &Url) -> Result, AsteriskError> { let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); - let (ws_stream, _) = connect_async(ws_url).await.map_err(ClientError::WebSocketConnect)?; + let (ws_stream, _) = connect_async(ws_url).await.map_err(AsteriskError::WebSocketConnect)?; tokio::task::spawn(async move { let (mut ws_sender, mut ws_receiver) = ws_stream.split(); diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index 4e2ffb3..53ff73f 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -1,5 +1,5 @@ -mod client; -pub use client::Client; +mod asterisk; +pub use asterisk::Asterisk; mod request_client; pub use request_client::*; From d04e79f507d83f7f3621a3a6759f5bd9fbbd2cc6 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 20:15:22 +0200 Subject: [PATCH 50/61] refactor!(ari): rename `RequestClient` to `AriClient` --- arirs/examples/list-channels.rs | 6 +- arirs/examples/originate.rs | 6 +- .../bridge/core.rs | 30 ++++----- .../bridge/mod.rs | 0 .../bridge/responses.rs | 0 .../channel/core.rs | 62 +++++++++---------- .../channel/mod.rs | 0 .../channel/request_params.rs | 0 .../channel/responses.rs | 0 .../{request_client => ari_client}/core.rs | 20 +++--- .../src/{request_client => ari_client}/mod.rs | 5 +- arirs/src/ari_client/playback/core.rs | 15 +++++ .../playback/mod.rs | 0 .../playback/request_params.rs | 0 .../playback/responses.rs | 0 .../recording/core.rs | 26 ++++---- .../recording/mod.rs | 0 .../recording/responses.rs | 0 arirs/src/asterisk.rs | 4 +- arirs/src/lib.rs | 4 +- arirs/src/request_client/playback/core.rs | 15 ----- 21 files changed, 96 insertions(+), 97 deletions(-) rename arirs/src/{request_client => ari_client}/bridge/core.rs (55%) rename arirs/src/{request_client => ari_client}/bridge/mod.rs (100%) rename arirs/src/{request_client => ari_client}/bridge/responses.rs (100%) rename arirs/src/{request_client => ari_client}/channel/core.rs (70%) rename arirs/src/{request_client => ari_client}/channel/mod.rs (100%) rename arirs/src/{request_client => ari_client}/channel/request_params.rs (100%) rename arirs/src/{request_client => ari_client}/channel/responses.rs (100%) rename arirs/src/{request_client => ari_client}/core.rs (85%) rename arirs/src/{request_client => ari_client}/mod.rs (58%) create mode 100644 arirs/src/ari_client/playback/core.rs rename arirs/src/{request_client => ari_client}/playback/mod.rs (100%) rename arirs/src/{request_client => ari_client}/playback/request_params.rs (100%) rename arirs/src/{request_client => ari_client}/playback/responses.rs (100%) rename arirs/src/{request_client => ari_client}/recording/core.rs (71%) rename arirs/src/{request_client => ari_client}/recording/mod.rs (100%) rename arirs/src/{request_client => ari_client}/recording/responses.rs (100%) delete mode 100644 arirs/src/request_client/playback/core.rs diff --git a/arirs/examples/list-channels.rs b/arirs/examples/list-channels.rs index 6a17067..95a6b55 100644 --- a/arirs/examples/list-channels.rs +++ b/arirs/examples/list-channels.rs @@ -1,15 +1,15 @@ -use arirs::{RequestClient, RequestClientError}; +use arirs::{AriClient, AriClientError}; use tracing::debug; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; #[tokio::main] -async fn main() -> Result<(), RequestClientError> { +async fn main() -> Result<(), AriClientError> { tracing_subscriber::registry() .with(fmt::layer()) .with(EnvFilter::from_default_env()) .init(); - let client = RequestClient::default(); + let client = AriClient::default(); for channel in client.channel_list().await? { debug!("Channel ID: {}", channel.id()); diff --git a/arirs/examples/originate.rs b/arirs/examples/originate.rs index 3b9767a..d172364 100644 --- a/arirs/examples/originate.rs +++ b/arirs/examples/originate.rs @@ -1,18 +1,18 @@ use std::collections::HashMap; -use arirs::{OriginateChannelParams, OriginateParams, RequestClient, RequestClientError}; +use arirs::{AriClient, AriClientError, OriginateChannelParams, OriginateParams}; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; const APP_NAME: &str = "ari"; #[tokio::main] -async fn main() -> Result<(), RequestClientError> { +async fn main() -> Result<(), AriClientError> { tracing_subscriber::registry() .with(fmt::layer()) .with(EnvFilter::from_default_env()) .init(); - let client = RequestClient::default(); + let client = AriClient::default(); let originate_params = OriginateChannelParams { endpoint: "PJSIP/1000", diff --git a/arirs/src/request_client/bridge/core.rs b/arirs/src/ari_client/bridge/core.rs similarity index 55% rename from arirs/src/request_client/bridge/core.rs rename to arirs/src/ari_client/bridge/core.rs index 44fa675..3a5426a 100644 --- a/arirs/src/request_client/bridge/core.rs +++ b/arirs/src/ari_client/bridge/core.rs @@ -1,32 +1,32 @@ use crate::*; -impl RequestClient { - pub async fn bridge_create(&self, _bridge_id: &str) -> RequestClientResult { +impl AriClient { + pub async fn bridge_create(&self, _bridge_id: &str) -> AriClientResult { unimplemented!() } // SUGGESTION(gibbz00): combine with bidge_create by making ID optional - pub async fn bridge_create_with_id(&self, _bridge_id: &str, _bridge: &Bridge) -> RequestClientResult { + pub async fn bridge_create_with_id(&self, _bridge_id: &str, _bridge: &Bridge) -> AriClientResult { unimplemented!() } - pub async fn bridge_get(_client: &RequestClient, _bridge_id: &str) -> RequestClientResult { + pub async fn bridge_get(_client: &AriClient, _bridge_id: &str) -> AriClientResult { unimplemented!() } - pub async fn bidge_list(&self) -> RequestClientResult> { + pub async fn bidge_list(&self) -> AriClientResult> { unimplemented!() } - pub async fn bridge_destroy(&self, _bridge_id: &str) -> RequestClientResult<()> { + pub async fn bridge_destroy(&self, _bridge_id: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn bridge_add_channel(&self, _bridge_id: &str, _channel_id: &str) -> RequestClientResult<()> { + pub async fn bridge_add_channel(&self, _bridge_id: &str, _channel_id: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn bridge_remove_channel(&self, _bridge_id: &str, _channel_id: &str) -> RequestClientResult<()> { + pub async fn bridge_remove_channel(&self, _bridge_id: &str, _channel_id: &str) -> AriClientResult<()> { unimplemented!() } @@ -35,31 +35,31 @@ impl RequestClient { _bridge_id: &str, _channel_id: &str, _video_source_id: &str, - ) -> RequestClientResult<()> { + ) -> AriClientResult<()> { unimplemented!() } - pub async fn bridge_unset_video_source(&self, _bridge_id: &str) -> RequestClientResult<()> { + pub async fn bridge_unset_video_source(&self, _bridge_id: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn bridge_start_moh(&self, _bridge_id: &str, _moh_class: &str) -> RequestClientResult<()> { + pub async fn bridge_start_moh(&self, _bridge_id: &str, _moh_class: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn bridge_stop_moh(&self, _bridge_id: &str) -> RequestClientResult<()> { + pub async fn bridge_stop_moh(&self, _bridge_id: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn bridge_play_media(&self, _bridge_id: &str, _playback: &Playback) -> RequestClientResult<()> { + pub async fn bridge_play_media(&self, _bridge_id: &str, _playback: &Playback) -> AriClientResult<()> { unimplemented!() } - pub async fn bridge_stop_media(&self, _bridge_id: &str) -> RequestClientResult<()> { + pub async fn bridge_stop_media(&self, _bridge_id: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn bridge_start_recording(&self, _bridge_id: &str, _recording: &LiveRecording) -> RequestClientResult<()> { + pub async fn bridge_start_recording(&self, _bridge_id: &str, _recording: &LiveRecording) -> AriClientResult<()> { unimplemented!() } } diff --git a/arirs/src/request_client/bridge/mod.rs b/arirs/src/ari_client/bridge/mod.rs similarity index 100% rename from arirs/src/request_client/bridge/mod.rs rename to arirs/src/ari_client/bridge/mod.rs diff --git a/arirs/src/request_client/bridge/responses.rs b/arirs/src/ari_client/bridge/responses.rs similarity index 100% rename from arirs/src/request_client/bridge/responses.rs rename to arirs/src/ari_client/bridge/responses.rs diff --git a/arirs/src/request_client/channel/core.rs b/arirs/src/ari_client/channel/core.rs similarity index 70% rename from arirs/src/request_client/channel/core.rs rename to arirs/src/ari_client/channel/core.rs index 0775816..88a51f2 100644 --- a/arirs/src/request_client/channel/core.rs +++ b/arirs/src/ari_client/channel/core.rs @@ -2,44 +2,44 @@ use std::collections::HashMap; use crate::*; -impl RequestClient { - pub async fn channel_answer(&self, channel_id: &str) -> RequestClientResult<()> { +impl AriClient { + pub async fn channel_answer(&self, channel_id: &str) -> AriClientResult<()> { self.authorized_post(["channels", channel_id, "answer"], ()).await } - pub async fn channel_hangup(&self, channel_id: &str, reason: Reason) -> RequestClientResult<()> { + pub async fn channel_hangup(&self, channel_id: &str, reason: Reason) -> AriClientResult<()> { self.authorized_delete(["channels", channel_id], reason).await } - pub async fn channel_start_ringing(&self, channel_id: &str) -> RequestClientResult<()> { + pub async fn channel_start_ringing(&self, channel_id: &str) -> AriClientResult<()> { self.authorized_post(["channels", channel_id, "ring"], ()).await } - pub async fn channel_stop_ringing(&self, channel_id: &str) -> RequestClientResult<()> { + pub async fn channel_stop_ringing(&self, channel_id: &str) -> AriClientResult<()> { self.authorized_delete(["channels", channel_id, "ring"], ()).await } - pub async fn channel_send_dtmf(&self, channel_id: &str, params: SendDtmfParams<'_>) -> RequestClientResult<()> { + pub async fn channel_send_dtmf(&self, channel_id: &str, params: SendDtmfParams<'_>) -> AriClientResult<()> { self.authorized_post(["channels", channel_id, "dtmf"], params).await } - pub async fn channel_mute(&self, channel_id: &str, direction: Direction) -> RequestClientResult<()> { + pub async fn channel_mute(&self, channel_id: &str, direction: Direction) -> AriClientResult<()> { self.authorized_post(["channels", channel_id, "mute"], direction).await } - pub async fn channel_unmute(&self, channel_id: &str, direction: Direction) -> RequestClientResult<()> { + pub async fn channel_unmute(&self, channel_id: &str, direction: Direction) -> AriClientResult<()> { self.authorized_delete(["channels", channel_id, "mute"], direction).await } - pub async fn channel_hold(&self, channel_id: &str) -> RequestClientResult<()> { + pub async fn channel_hold(&self, channel_id: &str) -> AriClientResult<()> { self.authorized_post(["channels", channel_id, "hold"], ()).await } - pub async fn channel_unhold(&self, channel_id: &str) -> RequestClientResult<()> { + pub async fn channel_unhold(&self, channel_id: &str) -> AriClientResult<()> { self.authorized_delete(["channels", channel_id, "hold"], ()).await } - pub async fn channel_play_media(&self, channel_id: &str, params: PlayMediaParams<'_>) -> RequestClientResult { + pub async fn channel_play_media(&self, channel_id: &str, params: PlayMediaParams<'_>) -> AriClientResult { self.authorized_post_json_response(["channels", channel_id, "play"], params).await } @@ -49,28 +49,28 @@ impl RequestClient { channel_id: &str, playback_id: &str, params: PlayMediaBaseParams<'_>, - ) -> RequestClientResult { + ) -> AriClientResult { self.authorized_post_json_response(["channels", channel_id, "play", playback_id, "media"], params) .await } - pub async fn channel_record(&self, channel_id: &str, params: RecordParams<'_>) -> RequestClientResult { + pub async fn channel_record(&self, channel_id: &str, params: RecordParams<'_>) -> AriClientResult { self.authorized_post_json_response(["channels", channel_id, "record"], params).await } - pub async fn channel_dial(&self, channel_id: &str, params: DialParams<'_>) -> RequestClientResult<()> { + pub async fn channel_dial(&self, channel_id: &str, params: DialParams<'_>) -> AriClientResult<()> { self.authorized_post(["channels", channel_id, "dial"], params).await } - pub async fn channel_list(&self) -> RequestClientResult> { + pub async fn channel_list(&self) -> AriClientResult> { self.authorized_get(["channels"], ()).await } - pub async fn channel_create(&self, params: ChannelCreateParams<'_>, variables: &HashMap<&str, &str>) -> RequestClientResult { + pub async fn channel_create(&self, params: ChannelCreateParams<'_>, variables: &HashMap<&str, &str>) -> AriClientResult { self.authorized_post_variables(["channels", "create"], params, variables).await } - pub async fn channel_get(self, channel_id: &str) -> RequestClientResult { + pub async fn channel_get(self, channel_id: &str) -> AriClientResult { self.authorized_get(["channels", channel_id], ()).await } @@ -78,7 +78,7 @@ impl RequestClient { &self, params: OriginateChannelParams<'a>, variables: &HashMap<&str, &str>, - ) -> RequestClientResult { + ) -> AriClientResult { self.authorized_post_variables(["channels"], params, variables).await } @@ -88,57 +88,57 @@ impl RequestClient { channel_id: &str, params: OriginateChannelWithIdParams<'a>, variables: &HashMap<&str, &str>, - ) -> RequestClientResult { + ) -> AriClientResult { self.authorized_post_variables(["channels", channel_id], params, variables).await } - pub async fn channel_start_moh(&self, _channel_id: &str) -> RequestClientResult<()> { + pub async fn channel_start_moh(&self, _channel_id: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn channel_stop_moh(&self, _channel_id: &str) -> RequestClientResult<()> { + pub async fn channel_stop_moh(&self, _channel_id: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn channel_silence(&self, _channel_id: &str) -> RequestClientResult<()> { + pub async fn channel_silence(&self, _channel_id: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn channel_unsilince(&self, _channel_id: &str) -> RequestClientResult<()> { + pub async fn channel_unsilince(&self, _channel_id: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn channel_get_variable(&self, _channel_id: &str) -> RequestClientResult { + pub async fn channel_get_variable(&self, _channel_id: &str) -> AriClientResult { unimplemented!() } - pub async fn channel_set_variable(&self, _channel_id: &str) -> RequestClientResult<()> { + pub async fn channel_set_variable(&self, _channel_id: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn channel_continue_in_dialplan(&self, _channel_id: &str) -> RequestClientResult<()> { + pub async fn channel_continue_in_dialplan(&self, _channel_id: &str) -> AriClientResult<()> { unimplemented!() } /// Transfer the channel to another ARI application. /// Same as `move` in Asterisk - pub async fn channel_transfer(&self, _channel_id: &str) -> RequestClientResult<()> { + pub async fn channel_transfer(&self, _channel_id: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn channel_get_rtp_statistics(&self, _channel_id: &str) -> RequestClientResult { + pub async fn channel_get_rtp_statistics(&self, _channel_id: &str) -> AriClientResult { unimplemented!() } - pub async fn channel_snoop(&self, _channel_id: &str) -> RequestClientResult { + pub async fn channel_snoop(&self, _channel_id: &str) -> AriClientResult { unimplemented!() } // SUGGESTION(gibbz00): combine with above method and mave ID optional - pub async fn channel_snoop_with_id(&self, _channel_id: &str) -> RequestClientResult { + pub async fn channel_snoop_with_id(&self, _channel_id: &str) -> AriClientResult { unimplemented!() } - pub async fn channel_start_external_media(&self, _channel_id: &str) -> RequestClientResult { + pub async fn channel_start_external_media(&self, _channel_id: &str) -> AriClientResult { unimplemented!() } } diff --git a/arirs/src/request_client/channel/mod.rs b/arirs/src/ari_client/channel/mod.rs similarity index 100% rename from arirs/src/request_client/channel/mod.rs rename to arirs/src/ari_client/channel/mod.rs diff --git a/arirs/src/request_client/channel/request_params.rs b/arirs/src/ari_client/channel/request_params.rs similarity index 100% rename from arirs/src/request_client/channel/request_params.rs rename to arirs/src/ari_client/channel/request_params.rs diff --git a/arirs/src/request_client/channel/responses.rs b/arirs/src/ari_client/channel/responses.rs similarity index 100% rename from arirs/src/request_client/channel/responses.rs rename to arirs/src/ari_client/channel/responses.rs diff --git a/arirs/src/request_client/core.rs b/arirs/src/ari_client/core.rs similarity index 85% rename from arirs/src/request_client/core.rs rename to arirs/src/ari_client/core.rs index cd839ee..4f69d4b 100644 --- a/arirs/src/request_client/core.rs +++ b/arirs/src/ari_client/core.rs @@ -8,21 +8,19 @@ use url::Url; use crate::*; #[derive(Debug, Getters)] -pub struct RequestClient { +pub struct AriClient { url: Url, api_key: String, inner: reqwest::Client, } -pub(crate) type RequestClientResult = Result; - #[derive(Debug, Error)] -pub enum RequestClientError { +pub enum AriClientError { #[error("encountered inner HTTP client error")] Reqwest(#[from] reqwest::Error), } -impl RequestClient { +impl AriClient { pub(crate) fn new(url: Url, api_key: String) -> Self { Self { url, @@ -35,13 +33,13 @@ impl RequestClient { &self, path: impl AsRef<[&str]>, params: T, - ) -> RequestClientResult { + ) -> AriClientResult { let url = self.authorized_url(path, params); let response = self.inner.get(url).send().await?.json().await?; Ok(response) } - pub(crate) async fn authorized_post(&self, path: impl AsRef<[&str]>, params: T) -> RequestClientResult<()> { + pub(crate) async fn authorized_post(&self, path: impl AsRef<[&str]>, params: T) -> AriClientResult<()> { let url = self.authorized_url(path, params); self.inner.post(url).send().await?; Ok(()) @@ -51,7 +49,7 @@ impl RequestClient { &self, path: impl AsRef<[&str]>, params: T, - ) -> RequestClientResult { + ) -> AriClientResult { let url = self.authorized_url(path, params); let response = self.inner.post(url).send().await?.json().await?; Ok(response) @@ -62,7 +60,7 @@ impl RequestClient { path: impl AsRef<[&str]>, params: T, variables: &HashMap<&str, &str>, - ) -> RequestClientResult { + ) -> AriClientResult { let url = self.authorized_url(path, params); let response = self .inner @@ -77,7 +75,7 @@ impl RequestClient { Ok(response) } - pub(crate) async fn authorized_delete(&self, path: impl AsRef<[&str]>, params: T) -> RequestClientResult<()> { + pub(crate) async fn authorized_delete(&self, path: impl AsRef<[&str]>, params: T) -> AriClientResult<()> { let url = self.authorized_url(path, params); self.inner.delete(url).send().await?; Ok(()) @@ -88,7 +86,7 @@ impl RequestClient { } } -impl Default for RequestClient { +impl Default for AriClient { fn default() -> Self { Self::new( "http://localhost:8088/".parse().expect("failed to parse url"), diff --git a/arirs/src/request_client/mod.rs b/arirs/src/ari_client/mod.rs similarity index 58% rename from arirs/src/request_client/mod.rs rename to arirs/src/ari_client/mod.rs index dd7eeb3..1dc7661 100644 --- a/arirs/src/request_client/mod.rs +++ b/arirs/src/ari_client/mod.rs @@ -1,6 +1,7 @@ +pub(crate) type AriClientResult = Result; + mod core; -pub(crate) use core::RequestClientResult; -pub use core::{RequestClient, RequestClientError}; +pub use core::{AriClient, AriClientError}; mod bridge; pub use bridge::*; diff --git a/arirs/src/ari_client/playback/core.rs b/arirs/src/ari_client/playback/core.rs new file mode 100644 index 0000000..1c35ca2 --- /dev/null +++ b/arirs/src/ari_client/playback/core.rs @@ -0,0 +1,15 @@ +use crate::*; + +impl AriClient { + pub async fn playback_get(&self, _playback_id: &str) -> AriClientResult { + unimplemented!() + } + + pub async fn playback_control(&self, _playback_id: &str, _operation: Operation) -> AriClientResult<()> { + unimplemented!() + } + + pub async fn playback_stop(&self, _playback_id: &str) -> AriClientResult<()> { + unimplemented!() + } +} diff --git a/arirs/src/request_client/playback/mod.rs b/arirs/src/ari_client/playback/mod.rs similarity index 100% rename from arirs/src/request_client/playback/mod.rs rename to arirs/src/ari_client/playback/mod.rs diff --git a/arirs/src/request_client/playback/request_params.rs b/arirs/src/ari_client/playback/request_params.rs similarity index 100% rename from arirs/src/request_client/playback/request_params.rs rename to arirs/src/ari_client/playback/request_params.rs diff --git a/arirs/src/request_client/playback/responses.rs b/arirs/src/ari_client/playback/responses.rs similarity index 100% rename from arirs/src/request_client/playback/responses.rs rename to arirs/src/ari_client/playback/responses.rs diff --git a/arirs/src/request_client/recording/core.rs b/arirs/src/ari_client/recording/core.rs similarity index 71% rename from arirs/src/request_client/recording/core.rs rename to arirs/src/ari_client/recording/core.rs index a897967..ee3a59f 100644 --- a/arirs/src/request_client/recording/core.rs +++ b/arirs/src/ari_client/recording/core.rs @@ -1,50 +1,50 @@ use crate::*; -impl RequestClient { - pub async fn live_recording_get(&self, _recording_name: &str) -> RequestClientResult { +impl AriClient { + pub async fn live_recording_get(&self, _recording_name: &str) -> AriClientResult { unimplemented!() } - pub async fn live_recording_discard(&self, _recording_name: &str) -> RequestClientResult<()> { + pub async fn live_recording_discard(&self, _recording_name: &str) -> AriClientResult<()> { unimplemented!() } // TODO: explore if it's possible to return a StoredRecording - pub async fn live_recording_stop(&self, _recording_name: &str) -> RequestClientResult<()> { + pub async fn live_recording_stop(&self, _recording_name: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn live_recording_pause(&self, _recording_name: &str) -> RequestClientResult<()> { + pub async fn live_recording_pause(&self, _recording_name: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn live_recording_resume(&self, _recording_name: &str) -> RequestClientResult<()> { + pub async fn live_recording_resume(&self, _recording_name: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn live_recording_mute(&self, _recording_name: &str) -> RequestClientResult<()> { + pub async fn live_recording_mute(&self, _recording_name: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn live_recording_unmute(&self, _recording_name: &str) -> RequestClientResult<()> { + pub async fn live_recording_unmute(&self, _recording_name: &str) -> AriClientResult<()> { unimplemented!() } } -impl RequestClient { - pub async fn stored_recording_list(&self, _recording_name: &str) -> RequestClientResult> { +impl AriClient { + pub async fn stored_recording_list(&self, _recording_name: &str) -> AriClientResult> { unimplemented!() } - pub async fn stored_recording_get(&self, _recording_name: &str) -> RequestClientResult { + pub async fn stored_recording_get(&self, _recording_name: &str) -> AriClientResult { unimplemented!() } - pub async fn stored_recording_delete(&self, _recording_name: &str) -> RequestClientResult<()> { + pub async fn stored_recording_delete(&self, _recording_name: &str) -> AriClientResult<()> { unimplemented!() } - pub async fn stored_recording_download(&self, _recording_name: &str) -> RequestClientResult<&[u8]> { + pub async fn stored_recording_download(&self, _recording_name: &str) -> AriClientResult<&[u8]> { unimplemented!() } } diff --git a/arirs/src/request_client/recording/mod.rs b/arirs/src/ari_client/recording/mod.rs similarity index 100% rename from arirs/src/request_client/recording/mod.rs rename to arirs/src/ari_client/recording/mod.rs diff --git a/arirs/src/request_client/recording/responses.rs b/arirs/src/ari_client/recording/responses.rs similarity index 100% rename from arirs/src/request_client/recording/responses.rs rename to arirs/src/ari_client/recording/responses.rs diff --git a/arirs/src/asterisk.rs b/arirs/src/asterisk.rs index 545b967..bf36f12 100644 --- a/arirs/src/asterisk.rs +++ b/arirs/src/asterisk.rs @@ -47,14 +47,14 @@ impl Asterisk { app_name: impl AsRef, username: impl AsRef, password: impl AsRef, - ) -> Result<(RequestClient, UnboundedReceiver), AsteriskError> { + ) -> Result<(AriClient, UnboundedReceiver), AsteriskError> { let url = url.as_ref().parse::()?.join("ari/")?; let api_key = Authorization::api_key(username.as_ref(), password.as_ref()); let ws_url = Self::build_ws_url(&url, &api_key, app_name.as_ref())?; - let request_client = RequestClient::new(url, api_key); + let request_client = AriClient::new(url, api_key); let event_listener = Self::connect_ws(&ws_url).await?; diff --git a/arirs/src/lib.rs b/arirs/src/lib.rs index 53ff73f..b854c21 100644 --- a/arirs/src/lib.rs +++ b/arirs/src/lib.rs @@ -1,8 +1,8 @@ mod asterisk; pub use asterisk::Asterisk; -mod request_client; -pub use request_client::*; +mod ari_client; +pub use ari_client::*; mod authorization; pub(crate) use authorization::Authorization; diff --git a/arirs/src/request_client/playback/core.rs b/arirs/src/request_client/playback/core.rs deleted file mode 100644 index 713d59c..0000000 --- a/arirs/src/request_client/playback/core.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::*; - -impl RequestClient { - pub async fn playback_get(&self, _playback_id: &str) -> RequestClientResult { - unimplemented!() - } - - pub async fn playback_control(&self, _playback_id: &str, _operation: Operation) -> RequestClientResult<()> { - unimplemented!() - } - - pub async fn playback_stop(&self, _playback_id: &str) -> RequestClientResult<()> { - unimplemented!() - } -} From b1d2bd3be846c4762e219080e0096dcef2da3ca7 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 20:18:09 +0200 Subject: [PATCH 51/61] chore(ari): go over tokio feature flags --- Cargo.lock | 59 ------------------------------------------------ arirs/Cargo.toml | 3 ++- 2 files changed, 2 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7bb9e2d..62b862c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -602,16 +602,6 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.22" @@ -761,29 +751,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - [[package]] name = "percent-encoding" version = "2.3.1" @@ -885,15 +852,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "redox_syscall" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" -dependencies = [ - "bitflags", -] - [[package]] name = "regex" version = "1.10.6" @@ -1076,12 +1034,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "security-framework" version = "2.11.1" @@ -1186,15 +1138,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] - [[package]] name = "slab" version = "0.4.9" @@ -1363,9 +1306,7 @@ dependencies = [ "bytes", "libc", "mio", - "parking_lot", "pin-project-lite", - "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.52.0", diff --git a/arirs/Cargo.toml b/arirs/Cargo.toml index 4ee6ab1..d2e011a 100644 --- a/arirs/Cargo.toml +++ b/arirs/Cargo.toml @@ -17,10 +17,11 @@ serde_json = "1.0.128" serde_qs = "0.13" strum = { version = "0.26", features = ["derive"] } thiserror = "1.0.63" -tokio = { version = "1.40.0", features = ["full"] } +tokio = { version = "1.40.0", features = ["macros", "rt"] } tokio-tungstenite = { version = "0.23.1", features = ["url"] } url = "2.5.2" [dev-dependencies] +tokio = { version = "1.40.0", features = ["rt-multi-thread"] } tracing = { version = "0.1.40", features = ["attributes"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } From 782ee7cd8c3d7c9c63beda77e3cb488b146094ce Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 20:19:08 +0200 Subject: [PATCH 52/61] chore(ari): replace qualified `chrono::Utc` with use --- arirs/src/ari_client/channel/responses.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/arirs/src/ari_client/channel/responses.rs b/arirs/src/ari_client/channel/responses.rs index 9c02b65..170557b 100644 --- a/arirs/src/ari_client/channel/responses.rs +++ b/arirs/src/ari_client/channel/responses.rs @@ -1,4 +1,4 @@ -use chrono::DateTime; +use chrono::{DateTime, Utc}; use derive_getters::Getters; use serde::Deserialize; @@ -31,7 +31,7 @@ pub struct ChannelVariable { #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct StasisStart { - timestamp: DateTime, + timestamp: DateTime, args: Vec, channel: Channel, asterisk_id: String, @@ -41,7 +41,7 @@ pub struct StasisStart { #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct StasisEnd { - timestamp: DateTime, + timestamp: DateTime, channel: Channel, asterisk_id: String, application: String, @@ -50,7 +50,7 @@ pub struct StasisEnd { #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelCreated { - timestamp: DateTime, + timestamp: DateTime, channel: Option, asterisk_id: String, application: String, @@ -59,7 +59,7 @@ pub struct ChannelCreated { #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelDestroyed { - timestamp: DateTime, + timestamp: DateTime, cause: i32, cause_txt: String, channel: Channel, @@ -70,7 +70,7 @@ pub struct ChannelDestroyed { #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelVarset { - timestamp: DateTime, + timestamp: DateTime, variable: String, value: String, channel: Option, @@ -81,7 +81,7 @@ pub struct ChannelVarset { #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelHangupRequest { - timestamp: DateTime, + timestamp: DateTime, soft: Option, cause: i32, channel: Channel, @@ -92,7 +92,7 @@ pub struct ChannelHangupRequest { #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelDialplan { - timestamp: DateTime, + timestamp: DateTime, dialplan_app: String, dialplan_app_data: String, channel: Channel, @@ -103,7 +103,7 @@ pub struct ChannelDialplan { #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelStateChange { - timestamp: DateTime, + timestamp: DateTime, channel: Channel, asterisk_id: String, application: String, @@ -112,7 +112,7 @@ pub struct ChannelStateChange { #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelDtmfReceived { - timestamp: DateTime, + timestamp: DateTime, digit: String, duration_ms: i32, channel: Channel, From 424992f7a979ab99f1d733cf19546439a91a71b9 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 20:21:13 +0200 Subject: [PATCH 53/61] chore(ari): remove misleading patch version from `Cargo.toml` --- arirs/Cargo.toml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/arirs/Cargo.toml b/arirs/Cargo.toml index d2e011a..adda919 100644 --- a/arirs/Cargo.toml +++ b/arirs/Cargo.toml @@ -6,22 +6,22 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -chrono = { version = "0.4.38", features = ["serde"] } +chrono = { version = "0.4", features = ["serde"] } derive-getters = "0.5" -futures-channel = "0.3.30" -futures-util = "0.3.30" -rand = "0.8.5" -reqwest = { version = "0.12.7", features = ["json"] } -serde = { version = "1.0.210", features = ["derive"] } -serde_json = "1.0.128" +futures-channel = "0.3" +futures-util = "0.3" +rand = "0.8" +reqwest = { version = "0.12", features = ["json"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" serde_qs = "0.13" strum = { version = "0.26", features = ["derive"] } -thiserror = "1.0.63" -tokio = { version = "1.40.0", features = ["macros", "rt"] } -tokio-tungstenite = { version = "0.23.1", features = ["url"] } -url = "2.5.2" +thiserror = "1.0" +tokio = { version = "1.40", features = ["macros", "rt"] } +tokio-tungstenite = { version = "0.23", features = ["url"] } +url = "2.5" [dev-dependencies] -tokio = { version = "1.40.0", features = ["rt-multi-thread"] } -tracing = { version = "0.1.40", features = ["attributes"] } -tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +tokio = { version = "1.40", features = ["rt-multi-thread"] } +tracing = { version = "0.1", features = ["attributes"] } +tracing-subscriber = { version = "0.3", features = ["env-filter"] } From 84a122a2ae363ff0ab7e8e5d9859ccf8a7d6dd40 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 20:22:47 +0200 Subject: [PATCH 54/61] chore: place crates in a `crates` directory --- Cargo.toml | 2 +- {agirs => crates/agirs}/Cargo.toml | 0 {agirs => crates/agirs}/src/lib.rs | 0 {amirs => crates/amirs}/Cargo.toml | 0 {amirs => crates/amirs}/src/lib.rs | 0 {arirs => crates/arirs}/Cargo.toml | 2 -- {arirs => crates/arirs}/examples/dtmf.rs | 0 {arirs => crates/arirs}/examples/list-channels.rs | 0 {arirs => crates/arirs}/examples/originate.rs | 0 {arirs => crates/arirs}/examples/playback.rs | 0 {arirs => crates/arirs}/src/ari_client/bridge/core.rs | 0 {arirs => crates/arirs}/src/ari_client/bridge/mod.rs | 0 {arirs => crates/arirs}/src/ari_client/bridge/responses.rs | 0 {arirs => crates/arirs}/src/ari_client/channel/core.rs | 0 {arirs => crates/arirs}/src/ari_client/channel/mod.rs | 0 .../arirs}/src/ari_client/channel/request_params.rs | 0 {arirs => crates/arirs}/src/ari_client/channel/responses.rs | 0 {arirs => crates/arirs}/src/ari_client/core.rs | 0 {arirs => crates/arirs}/src/ari_client/mod.rs | 0 {arirs => crates/arirs}/src/ari_client/playback/core.rs | 0 {arirs => crates/arirs}/src/ari_client/playback/mod.rs | 0 .../arirs}/src/ari_client/playback/request_params.rs | 0 {arirs => crates/arirs}/src/ari_client/playback/responses.rs | 0 {arirs => crates/arirs}/src/ari_client/recording/core.rs | 0 {arirs => crates/arirs}/src/ari_client/recording/mod.rs | 0 {arirs => crates/arirs}/src/ari_client/recording/responses.rs | 0 {arirs => crates/arirs}/src/asterisk.rs | 0 {arirs => crates/arirs}/src/authorization.rs | 0 {arirs => crates/arirs}/src/event.rs | 0 {arirs => crates/arirs}/src/lib.rs | 0 30 files changed, 1 insertion(+), 3 deletions(-) rename {agirs => crates/agirs}/Cargo.toml (100%) rename {agirs => crates/agirs}/src/lib.rs (100%) rename {amirs => crates/amirs}/Cargo.toml (100%) rename {amirs => crates/amirs}/src/lib.rs (100%) rename {arirs => crates/arirs}/Cargo.toml (88%) rename {arirs => crates/arirs}/examples/dtmf.rs (100%) rename {arirs => crates/arirs}/examples/list-channels.rs (100%) rename {arirs => crates/arirs}/examples/originate.rs (100%) rename {arirs => crates/arirs}/examples/playback.rs (100%) rename {arirs => crates/arirs}/src/ari_client/bridge/core.rs (100%) rename {arirs => crates/arirs}/src/ari_client/bridge/mod.rs (100%) rename {arirs => crates/arirs}/src/ari_client/bridge/responses.rs (100%) rename {arirs => crates/arirs}/src/ari_client/channel/core.rs (100%) rename {arirs => crates/arirs}/src/ari_client/channel/mod.rs (100%) rename {arirs => crates/arirs}/src/ari_client/channel/request_params.rs (100%) rename {arirs => crates/arirs}/src/ari_client/channel/responses.rs (100%) rename {arirs => crates/arirs}/src/ari_client/core.rs (100%) rename {arirs => crates/arirs}/src/ari_client/mod.rs (100%) rename {arirs => crates/arirs}/src/ari_client/playback/core.rs (100%) rename {arirs => crates/arirs}/src/ari_client/playback/mod.rs (100%) rename {arirs => crates/arirs}/src/ari_client/playback/request_params.rs (100%) rename {arirs => crates/arirs}/src/ari_client/playback/responses.rs (100%) rename {arirs => crates/arirs}/src/ari_client/recording/core.rs (100%) rename {arirs => crates/arirs}/src/ari_client/recording/mod.rs (100%) rename {arirs => crates/arirs}/src/ari_client/recording/responses.rs (100%) rename {arirs => crates/arirs}/src/asterisk.rs (100%) rename {arirs => crates/arirs}/src/authorization.rs (100%) rename {arirs => crates/arirs}/src/event.rs (100%) rename {arirs => crates/arirs}/src/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 91db6cf..f973578 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["agirs", "amirs", "arirs"] +members = ["crates/*"] resolver = "2" [workspace.lints.rust] diff --git a/agirs/Cargo.toml b/crates/agirs/Cargo.toml similarity index 100% rename from agirs/Cargo.toml rename to crates/agirs/Cargo.toml diff --git a/agirs/src/lib.rs b/crates/agirs/src/lib.rs similarity index 100% rename from agirs/src/lib.rs rename to crates/agirs/src/lib.rs diff --git a/amirs/Cargo.toml b/crates/amirs/Cargo.toml similarity index 100% rename from amirs/Cargo.toml rename to crates/amirs/Cargo.toml diff --git a/amirs/src/lib.rs b/crates/amirs/src/lib.rs similarity index 100% rename from amirs/src/lib.rs rename to crates/amirs/src/lib.rs diff --git a/arirs/Cargo.toml b/crates/arirs/Cargo.toml similarity index 88% rename from arirs/Cargo.toml rename to crates/arirs/Cargo.toml index adda919..c0481cb 100644 --- a/arirs/Cargo.toml +++ b/crates/arirs/Cargo.toml @@ -3,8 +3,6 @@ edition = "2021" name = "arirs" version = "0.1.0" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] chrono = { version = "0.4", features = ["serde"] } derive-getters = "0.5" diff --git a/arirs/examples/dtmf.rs b/crates/arirs/examples/dtmf.rs similarity index 100% rename from arirs/examples/dtmf.rs rename to crates/arirs/examples/dtmf.rs diff --git a/arirs/examples/list-channels.rs b/crates/arirs/examples/list-channels.rs similarity index 100% rename from arirs/examples/list-channels.rs rename to crates/arirs/examples/list-channels.rs diff --git a/arirs/examples/originate.rs b/crates/arirs/examples/originate.rs similarity index 100% rename from arirs/examples/originate.rs rename to crates/arirs/examples/originate.rs diff --git a/arirs/examples/playback.rs b/crates/arirs/examples/playback.rs similarity index 100% rename from arirs/examples/playback.rs rename to crates/arirs/examples/playback.rs diff --git a/arirs/src/ari_client/bridge/core.rs b/crates/arirs/src/ari_client/bridge/core.rs similarity index 100% rename from arirs/src/ari_client/bridge/core.rs rename to crates/arirs/src/ari_client/bridge/core.rs diff --git a/arirs/src/ari_client/bridge/mod.rs b/crates/arirs/src/ari_client/bridge/mod.rs similarity index 100% rename from arirs/src/ari_client/bridge/mod.rs rename to crates/arirs/src/ari_client/bridge/mod.rs diff --git a/arirs/src/ari_client/bridge/responses.rs b/crates/arirs/src/ari_client/bridge/responses.rs similarity index 100% rename from arirs/src/ari_client/bridge/responses.rs rename to crates/arirs/src/ari_client/bridge/responses.rs diff --git a/arirs/src/ari_client/channel/core.rs b/crates/arirs/src/ari_client/channel/core.rs similarity index 100% rename from arirs/src/ari_client/channel/core.rs rename to crates/arirs/src/ari_client/channel/core.rs diff --git a/arirs/src/ari_client/channel/mod.rs b/crates/arirs/src/ari_client/channel/mod.rs similarity index 100% rename from arirs/src/ari_client/channel/mod.rs rename to crates/arirs/src/ari_client/channel/mod.rs diff --git a/arirs/src/ari_client/channel/request_params.rs b/crates/arirs/src/ari_client/channel/request_params.rs similarity index 100% rename from arirs/src/ari_client/channel/request_params.rs rename to crates/arirs/src/ari_client/channel/request_params.rs diff --git a/arirs/src/ari_client/channel/responses.rs b/crates/arirs/src/ari_client/channel/responses.rs similarity index 100% rename from arirs/src/ari_client/channel/responses.rs rename to crates/arirs/src/ari_client/channel/responses.rs diff --git a/arirs/src/ari_client/core.rs b/crates/arirs/src/ari_client/core.rs similarity index 100% rename from arirs/src/ari_client/core.rs rename to crates/arirs/src/ari_client/core.rs diff --git a/arirs/src/ari_client/mod.rs b/crates/arirs/src/ari_client/mod.rs similarity index 100% rename from arirs/src/ari_client/mod.rs rename to crates/arirs/src/ari_client/mod.rs diff --git a/arirs/src/ari_client/playback/core.rs b/crates/arirs/src/ari_client/playback/core.rs similarity index 100% rename from arirs/src/ari_client/playback/core.rs rename to crates/arirs/src/ari_client/playback/core.rs diff --git a/arirs/src/ari_client/playback/mod.rs b/crates/arirs/src/ari_client/playback/mod.rs similarity index 100% rename from arirs/src/ari_client/playback/mod.rs rename to crates/arirs/src/ari_client/playback/mod.rs diff --git a/arirs/src/ari_client/playback/request_params.rs b/crates/arirs/src/ari_client/playback/request_params.rs similarity index 100% rename from arirs/src/ari_client/playback/request_params.rs rename to crates/arirs/src/ari_client/playback/request_params.rs diff --git a/arirs/src/ari_client/playback/responses.rs b/crates/arirs/src/ari_client/playback/responses.rs similarity index 100% rename from arirs/src/ari_client/playback/responses.rs rename to crates/arirs/src/ari_client/playback/responses.rs diff --git a/arirs/src/ari_client/recording/core.rs b/crates/arirs/src/ari_client/recording/core.rs similarity index 100% rename from arirs/src/ari_client/recording/core.rs rename to crates/arirs/src/ari_client/recording/core.rs diff --git a/arirs/src/ari_client/recording/mod.rs b/crates/arirs/src/ari_client/recording/mod.rs similarity index 100% rename from arirs/src/ari_client/recording/mod.rs rename to crates/arirs/src/ari_client/recording/mod.rs diff --git a/arirs/src/ari_client/recording/responses.rs b/crates/arirs/src/ari_client/recording/responses.rs similarity index 100% rename from arirs/src/ari_client/recording/responses.rs rename to crates/arirs/src/ari_client/recording/responses.rs diff --git a/arirs/src/asterisk.rs b/crates/arirs/src/asterisk.rs similarity index 100% rename from arirs/src/asterisk.rs rename to crates/arirs/src/asterisk.rs diff --git a/arirs/src/authorization.rs b/crates/arirs/src/authorization.rs similarity index 100% rename from arirs/src/authorization.rs rename to crates/arirs/src/authorization.rs diff --git a/arirs/src/event.rs b/crates/arirs/src/event.rs similarity index 100% rename from arirs/src/event.rs rename to crates/arirs/src/event.rs diff --git a/arirs/src/lib.rs b/crates/arirs/src/lib.rs similarity index 100% rename from arirs/src/lib.rs rename to crates/arirs/src/lib.rs From 6e4e72afedf5889d37b170983b2f54c071942811 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 20:25:42 +0200 Subject: [PATCH 55/61] chore: consolidate packages attributes to workspace --- Cargo.toml | 14 ++++++++++++++ crates/agirs/Cargo.toml | 6 ++++-- crates/amirs/Cargo.toml | 6 ++++-- crates/arirs/Cargo.toml | 6 ++++-- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f973578..5a46e14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,22 @@ members = ["crates/*"] resolver = "2" +[workspace.package] +edition = "2021" +readme = "README.md" +version = "0.1.0" + [workspace.lints.rust] +# TEMP: +# missing_docs = "warn" unused_must_use = "deny" [workspace.lints.clippy] self_named_module_files = "deny" + +[profile.dev] +# https://davidlattimore.github.io/posts/2024/02/04/speeding-up-the-rust-edit-build-run-cycle.html#avoid-linking-debug-info +debug = 0 +strip = "debuginfo" + +[workspace.dependencies] diff --git a/crates/agirs/Cargo.toml b/crates/agirs/Cargo.toml index 9264cb7..f1be75f 100644 --- a/crates/agirs/Cargo.toml +++ b/crates/agirs/Cargo.toml @@ -1,6 +1,8 @@ [package] -edition = "2021" name = "agirs" -version = "0.1.0" + +edition.workspace = true +readme.workspace = true +version.workspace = true [dependencies] diff --git a/crates/amirs/Cargo.toml b/crates/amirs/Cargo.toml index d489c4c..d762f51 100644 --- a/crates/amirs/Cargo.toml +++ b/crates/amirs/Cargo.toml @@ -1,6 +1,8 @@ [package] -edition = "2021" name = "amirs" -version = "0.1.0" + +edition.workspace = true +readme.workspace = true +version.workspace = true [dependencies] diff --git a/crates/arirs/Cargo.toml b/crates/arirs/Cargo.toml index c0481cb..e298483 100644 --- a/crates/arirs/Cargo.toml +++ b/crates/arirs/Cargo.toml @@ -1,7 +1,9 @@ [package] -edition = "2021" name = "arirs" -version = "0.1.0" + +edition.workspace = true +readme.workspace = true +version.workspace = true [dependencies] chrono = { version = "0.4", features = ["serde"] } From e8099b1c7ab92af487c3b0292eb00a6b08694f43 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 20:30:08 +0200 Subject: [PATCH 56/61] chore: use workspace dependencies --- Cargo.toml | 16 ++++++++++++++++ crates/arirs/Cargo.toml | 34 +++++++++++++++++----------------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5a46e14..32e06c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,19 @@ debug = 0 strip = "debuginfo" [workspace.dependencies] +chrono = "0.4" +derive-getters = "0.5" +futures-channel = "0.3" +futures-util = "0.3" +rand = "0.8" +reqwest = "0.12" +serde = "1.0" +serde_json = "1.0" +serde_qs = "0.13" +strum = "0.26" +thiserror = "1.0" +tokio = "1.40" +tokio-tungstenite = "0.23" +tracing = "0.1" +tracing-subscriber = "0.3" +url = "2.5" diff --git a/crates/arirs/Cargo.toml b/crates/arirs/Cargo.toml index e298483..6132b8e 100644 --- a/crates/arirs/Cargo.toml +++ b/crates/arirs/Cargo.toml @@ -6,22 +6,22 @@ readme.workspace = true version.workspace = true [dependencies] -chrono = { version = "0.4", features = ["serde"] } -derive-getters = "0.5" -futures-channel = "0.3" -futures-util = "0.3" -rand = "0.8" -reqwest = { version = "0.12", features = ["json"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -serde_qs = "0.13" -strum = { version = "0.26", features = ["derive"] } -thiserror = "1.0" -tokio = { version = "1.40", features = ["macros", "rt"] } -tokio-tungstenite = { version = "0.23", features = ["url"] } -url = "2.5" +chrono = { workspace = true, features = ["serde"] } +derive-getters.workspace = true +futures-channel.workspace = true +futures-util.workspace = true +rand.workspace = true +reqwest = { workspace = true, features = ["json"] } +serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true +serde_qs.workspace = true +strum = { workspace = true, features = ["derive"] } +thiserror.workspace = true +tokio = { workspace = true, features = ["macros", "rt"] } +tokio-tungstenite = { workspace = true, features = ["url"] } +url.workspace = true [dev-dependencies] -tokio = { version = "1.40", features = ["rt-multi-thread"] } -tracing = { version = "0.1", features = ["attributes"] } -tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tokio = { workspace = true, features = ["rt-multi-thread"] } +tracing = { workspace = true, features = ["attributes"] } +tracing-subscriber = { workspace = true, features = ["env-filter"] } From 233bc4cb855782d28b93e5a62343e5460d68dc66 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 20:42:17 +0200 Subject: [PATCH 57/61] chore: remove common `rs` postfix from crate directory names --- crates/{agirs => agi}/Cargo.toml | 0 crates/{agirs => agi}/src/lib.rs | 0 crates/{amirs => ami}/Cargo.toml | 0 crates/{amirs => ami}/src/lib.rs | 0 crates/{arirs => ari}/Cargo.toml | 0 crates/{arirs => ari}/examples/dtmf.rs | 0 crates/{arirs => ari}/examples/list-channels.rs | 0 crates/{arirs => ari}/examples/originate.rs | 0 crates/{arirs => ari}/examples/playback.rs | 0 crates/{arirs => ari}/src/ari_client/bridge/core.rs | 0 crates/{arirs => ari}/src/ari_client/bridge/mod.rs | 0 crates/{arirs => ari}/src/ari_client/bridge/responses.rs | 0 crates/{arirs => ari}/src/ari_client/channel/core.rs | 0 crates/{arirs => ari}/src/ari_client/channel/mod.rs | 0 crates/{arirs => ari}/src/ari_client/channel/request_params.rs | 0 crates/{arirs => ari}/src/ari_client/channel/responses.rs | 0 crates/{arirs => ari}/src/ari_client/core.rs | 0 crates/{arirs => ari}/src/ari_client/mod.rs | 0 crates/{arirs => ari}/src/ari_client/playback/core.rs | 0 crates/{arirs => ari}/src/ari_client/playback/mod.rs | 0 crates/{arirs => ari}/src/ari_client/playback/request_params.rs | 0 crates/{arirs => ari}/src/ari_client/playback/responses.rs | 0 crates/{arirs => ari}/src/ari_client/recording/core.rs | 0 crates/{arirs => ari}/src/ari_client/recording/mod.rs | 0 crates/{arirs => ari}/src/ari_client/recording/responses.rs | 0 crates/{arirs => ari}/src/asterisk.rs | 0 crates/{arirs => ari}/src/authorization.rs | 0 crates/{arirs => ari}/src/event.rs | 0 crates/{arirs => ari}/src/lib.rs | 0 29 files changed, 0 insertions(+), 0 deletions(-) rename crates/{agirs => agi}/Cargo.toml (100%) rename crates/{agirs => agi}/src/lib.rs (100%) rename crates/{amirs => ami}/Cargo.toml (100%) rename crates/{amirs => ami}/src/lib.rs (100%) rename crates/{arirs => ari}/Cargo.toml (100%) rename crates/{arirs => ari}/examples/dtmf.rs (100%) rename crates/{arirs => ari}/examples/list-channels.rs (100%) rename crates/{arirs => ari}/examples/originate.rs (100%) rename crates/{arirs => ari}/examples/playback.rs (100%) rename crates/{arirs => ari}/src/ari_client/bridge/core.rs (100%) rename crates/{arirs => ari}/src/ari_client/bridge/mod.rs (100%) rename crates/{arirs => ari}/src/ari_client/bridge/responses.rs (100%) rename crates/{arirs => ari}/src/ari_client/channel/core.rs (100%) rename crates/{arirs => ari}/src/ari_client/channel/mod.rs (100%) rename crates/{arirs => ari}/src/ari_client/channel/request_params.rs (100%) rename crates/{arirs => ari}/src/ari_client/channel/responses.rs (100%) rename crates/{arirs => ari}/src/ari_client/core.rs (100%) rename crates/{arirs => ari}/src/ari_client/mod.rs (100%) rename crates/{arirs => ari}/src/ari_client/playback/core.rs (100%) rename crates/{arirs => ari}/src/ari_client/playback/mod.rs (100%) rename crates/{arirs => ari}/src/ari_client/playback/request_params.rs (100%) rename crates/{arirs => ari}/src/ari_client/playback/responses.rs (100%) rename crates/{arirs => ari}/src/ari_client/recording/core.rs (100%) rename crates/{arirs => ari}/src/ari_client/recording/mod.rs (100%) rename crates/{arirs => ari}/src/ari_client/recording/responses.rs (100%) rename crates/{arirs => ari}/src/asterisk.rs (100%) rename crates/{arirs => ari}/src/authorization.rs (100%) rename crates/{arirs => ari}/src/event.rs (100%) rename crates/{arirs => ari}/src/lib.rs (100%) diff --git a/crates/agirs/Cargo.toml b/crates/agi/Cargo.toml similarity index 100% rename from crates/agirs/Cargo.toml rename to crates/agi/Cargo.toml diff --git a/crates/agirs/src/lib.rs b/crates/agi/src/lib.rs similarity index 100% rename from crates/agirs/src/lib.rs rename to crates/agi/src/lib.rs diff --git a/crates/amirs/Cargo.toml b/crates/ami/Cargo.toml similarity index 100% rename from crates/amirs/Cargo.toml rename to crates/ami/Cargo.toml diff --git a/crates/amirs/src/lib.rs b/crates/ami/src/lib.rs similarity index 100% rename from crates/amirs/src/lib.rs rename to crates/ami/src/lib.rs diff --git a/crates/arirs/Cargo.toml b/crates/ari/Cargo.toml similarity index 100% rename from crates/arirs/Cargo.toml rename to crates/ari/Cargo.toml diff --git a/crates/arirs/examples/dtmf.rs b/crates/ari/examples/dtmf.rs similarity index 100% rename from crates/arirs/examples/dtmf.rs rename to crates/ari/examples/dtmf.rs diff --git a/crates/arirs/examples/list-channels.rs b/crates/ari/examples/list-channels.rs similarity index 100% rename from crates/arirs/examples/list-channels.rs rename to crates/ari/examples/list-channels.rs diff --git a/crates/arirs/examples/originate.rs b/crates/ari/examples/originate.rs similarity index 100% rename from crates/arirs/examples/originate.rs rename to crates/ari/examples/originate.rs diff --git a/crates/arirs/examples/playback.rs b/crates/ari/examples/playback.rs similarity index 100% rename from crates/arirs/examples/playback.rs rename to crates/ari/examples/playback.rs diff --git a/crates/arirs/src/ari_client/bridge/core.rs b/crates/ari/src/ari_client/bridge/core.rs similarity index 100% rename from crates/arirs/src/ari_client/bridge/core.rs rename to crates/ari/src/ari_client/bridge/core.rs diff --git a/crates/arirs/src/ari_client/bridge/mod.rs b/crates/ari/src/ari_client/bridge/mod.rs similarity index 100% rename from crates/arirs/src/ari_client/bridge/mod.rs rename to crates/ari/src/ari_client/bridge/mod.rs diff --git a/crates/arirs/src/ari_client/bridge/responses.rs b/crates/ari/src/ari_client/bridge/responses.rs similarity index 100% rename from crates/arirs/src/ari_client/bridge/responses.rs rename to crates/ari/src/ari_client/bridge/responses.rs diff --git a/crates/arirs/src/ari_client/channel/core.rs b/crates/ari/src/ari_client/channel/core.rs similarity index 100% rename from crates/arirs/src/ari_client/channel/core.rs rename to crates/ari/src/ari_client/channel/core.rs diff --git a/crates/arirs/src/ari_client/channel/mod.rs b/crates/ari/src/ari_client/channel/mod.rs similarity index 100% rename from crates/arirs/src/ari_client/channel/mod.rs rename to crates/ari/src/ari_client/channel/mod.rs diff --git a/crates/arirs/src/ari_client/channel/request_params.rs b/crates/ari/src/ari_client/channel/request_params.rs similarity index 100% rename from crates/arirs/src/ari_client/channel/request_params.rs rename to crates/ari/src/ari_client/channel/request_params.rs diff --git a/crates/arirs/src/ari_client/channel/responses.rs b/crates/ari/src/ari_client/channel/responses.rs similarity index 100% rename from crates/arirs/src/ari_client/channel/responses.rs rename to crates/ari/src/ari_client/channel/responses.rs diff --git a/crates/arirs/src/ari_client/core.rs b/crates/ari/src/ari_client/core.rs similarity index 100% rename from crates/arirs/src/ari_client/core.rs rename to crates/ari/src/ari_client/core.rs diff --git a/crates/arirs/src/ari_client/mod.rs b/crates/ari/src/ari_client/mod.rs similarity index 100% rename from crates/arirs/src/ari_client/mod.rs rename to crates/ari/src/ari_client/mod.rs diff --git a/crates/arirs/src/ari_client/playback/core.rs b/crates/ari/src/ari_client/playback/core.rs similarity index 100% rename from crates/arirs/src/ari_client/playback/core.rs rename to crates/ari/src/ari_client/playback/core.rs diff --git a/crates/arirs/src/ari_client/playback/mod.rs b/crates/ari/src/ari_client/playback/mod.rs similarity index 100% rename from crates/arirs/src/ari_client/playback/mod.rs rename to crates/ari/src/ari_client/playback/mod.rs diff --git a/crates/arirs/src/ari_client/playback/request_params.rs b/crates/ari/src/ari_client/playback/request_params.rs similarity index 100% rename from crates/arirs/src/ari_client/playback/request_params.rs rename to crates/ari/src/ari_client/playback/request_params.rs diff --git a/crates/arirs/src/ari_client/playback/responses.rs b/crates/ari/src/ari_client/playback/responses.rs similarity index 100% rename from crates/arirs/src/ari_client/playback/responses.rs rename to crates/ari/src/ari_client/playback/responses.rs diff --git a/crates/arirs/src/ari_client/recording/core.rs b/crates/ari/src/ari_client/recording/core.rs similarity index 100% rename from crates/arirs/src/ari_client/recording/core.rs rename to crates/ari/src/ari_client/recording/core.rs diff --git a/crates/arirs/src/ari_client/recording/mod.rs b/crates/ari/src/ari_client/recording/mod.rs similarity index 100% rename from crates/arirs/src/ari_client/recording/mod.rs rename to crates/ari/src/ari_client/recording/mod.rs diff --git a/crates/arirs/src/ari_client/recording/responses.rs b/crates/ari/src/ari_client/recording/responses.rs similarity index 100% rename from crates/arirs/src/ari_client/recording/responses.rs rename to crates/ari/src/ari_client/recording/responses.rs diff --git a/crates/arirs/src/asterisk.rs b/crates/ari/src/asterisk.rs similarity index 100% rename from crates/arirs/src/asterisk.rs rename to crates/ari/src/asterisk.rs diff --git a/crates/arirs/src/authorization.rs b/crates/ari/src/authorization.rs similarity index 100% rename from crates/arirs/src/authorization.rs rename to crates/ari/src/authorization.rs diff --git a/crates/arirs/src/event.rs b/crates/ari/src/event.rs similarity index 100% rename from crates/arirs/src/event.rs rename to crates/ari/src/event.rs diff --git a/crates/arirs/src/lib.rs b/crates/ari/src/lib.rs similarity index 100% rename from crates/arirs/src/lib.rs rename to crates/ari/src/lib.rs From 46a5075ce47ce3dc5bffd233d23841ba03dc179e Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 20:45:49 +0200 Subject: [PATCH 58/61] feat!: change crate names to improve discovery and identifiability --- Cargo.lock | 18 +++++++++--------- README.md | 6 +++--- crates/agi/Cargo.toml | 2 +- crates/ami/Cargo.toml | 2 +- crates/ari/Cargo.toml | 2 +- crates/ari/examples/dtmf.rs | 2 +- crates/ari/examples/list-channels.rs | 2 +- crates/ari/examples/originate.rs | 2 +- crates/ari/examples/playback.rs | 2 +- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62b862c..9a78c07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,10 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "agirs" -version = "0.1.0" - [[package]] name = "aho-corasick" version = "1.1.3" @@ -30,10 +26,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "amirs" -version = "0.1.0" - [[package]] name = "android-tzdata" version = "0.1.1" @@ -50,7 +42,15 @@ dependencies = [ ] [[package]] -name = "arirs" +name = "asterisk-rs-agi" +version = "0.1.0" + +[[package]] +name = "asterisk-rs-ami" +version = "0.1.0" + +[[package]] +name = "asterisk-rs-ari" version = "0.1.0" dependencies = [ "chrono", diff --git a/README.md b/README.md index 00a76e2..2525393 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@ An Asterisk Library in Rust -## arirs +## `asterisk-rs-ari` A crate for Asterisk REST Interface tooling -## amirs +## `asterisk-rs-ami` A crate for Asterisk Management Interface tooling -## agirs +## `asterisk-rs-agi` A crate for Asterisk Gateway Interface tooling diff --git a/crates/agi/Cargo.toml b/crates/agi/Cargo.toml index f1be75f..4e20314 100644 --- a/crates/agi/Cargo.toml +++ b/crates/agi/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "agirs" +name = "asterisk-rs-agi" edition.workspace = true readme.workspace = true diff --git a/crates/ami/Cargo.toml b/crates/ami/Cargo.toml index d762f51..354edab 100644 --- a/crates/ami/Cargo.toml +++ b/crates/ami/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "amirs" +name = "asterisk-rs-ami" edition.workspace = true readme.workspace = true diff --git a/crates/ari/Cargo.toml b/crates/ari/Cargo.toml index 6132b8e..3731507 100644 --- a/crates/ari/Cargo.toml +++ b/crates/ari/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "arirs" +name = "asterisk-rs-ari" edition.workspace = true readme.workspace = true diff --git a/crates/ari/examples/dtmf.rs b/crates/ari/examples/dtmf.rs index 9dcd68d..53779de 100644 --- a/crates/ari/examples/dtmf.rs +++ b/crates/ari/examples/dtmf.rs @@ -1,6 +1,6 @@ use std::sync::{Arc, Mutex}; -use arirs::{Asterisk, Event}; +use asterisk_rs_ari::{Asterisk, Event}; use tracing::debug; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; diff --git a/crates/ari/examples/list-channels.rs b/crates/ari/examples/list-channels.rs index 95a6b55..c2da906 100644 --- a/crates/ari/examples/list-channels.rs +++ b/crates/ari/examples/list-channels.rs @@ -1,4 +1,4 @@ -use arirs::{AriClient, AriClientError}; +use asterisk_rs_ari::{AriClient, AriClientError}; use tracing::debug; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; diff --git a/crates/ari/examples/originate.rs b/crates/ari/examples/originate.rs index d172364..badf7cc 100644 --- a/crates/ari/examples/originate.rs +++ b/crates/ari/examples/originate.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use arirs::{AriClient, AriClientError, OriginateChannelParams, OriginateParams}; +use asterisk_rs_ari::{AriClient, AriClientError, OriginateChannelParams, OriginateParams}; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; const APP_NAME: &str = "ari"; diff --git a/crates/ari/examples/playback.rs b/crates/ari/examples/playback.rs index 06a34aa..3a3a9a2 100644 --- a/crates/ari/examples/playback.rs +++ b/crates/ari/examples/playback.rs @@ -1,4 +1,4 @@ -use arirs::{Asterisk, Event, PlayMediaBaseParams, PlayMediaParams}; +use asterisk_rs_ari::{Asterisk, Event, PlayMediaBaseParams, PlayMediaParams}; use tracing::level_filters::LevelFilter; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; From f1ed531d4862c07dcbca3e890e5ca3a0e8c28c31 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 21:19:59 +0200 Subject: [PATCH 59/61] refactor: move event types to event module --- .../ari/src/ari_client/channel/responses.rs | 92 ------------------ crates/ari/src/event.rs | 96 ++++++++++++++++++- 2 files changed, 94 insertions(+), 94 deletions(-) diff --git a/crates/ari/src/ari_client/channel/responses.rs b/crates/ari/src/ari_client/channel/responses.rs index 170557b..7f679b4 100644 --- a/crates/ari/src/ari_client/channel/responses.rs +++ b/crates/ari/src/ari_client/channel/responses.rs @@ -1,4 +1,3 @@ -use chrono::{DateTime, Utc}; use derive_getters::Getters; use serde::Deserialize; @@ -28,97 +27,6 @@ pub struct RtpStatistics { pub struct ChannelVariable { id: String, } -#[derive(Debug, Deserialize, Getters)] -#[serde(rename_all = "snake_case")] -pub struct StasisStart { - timestamp: DateTime, - args: Vec, - channel: Channel, - asterisk_id: String, - application: String, -} - -#[derive(Debug, Deserialize, Getters)] -#[serde(rename_all = "snake_case")] -pub struct StasisEnd { - timestamp: DateTime, - channel: Channel, - asterisk_id: String, - application: String, -} - -#[derive(Debug, Deserialize, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelCreated { - timestamp: DateTime, - channel: Option, - asterisk_id: String, - application: String, -} - -#[derive(Debug, Deserialize, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelDestroyed { - timestamp: DateTime, - cause: i32, - cause_txt: String, - channel: Channel, - asterisk_id: String, - application: String, -} - -#[derive(Debug, Deserialize, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelVarset { - timestamp: DateTime, - variable: String, - value: String, - channel: Option, - asterisk_id: String, - application: String, -} - -#[derive(Debug, Deserialize, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelHangupRequest { - timestamp: DateTime, - soft: Option, - cause: i32, - channel: Channel, - asterisk_id: String, - application: String, -} - -#[derive(Debug, Deserialize, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelDialplan { - timestamp: DateTime, - dialplan_app: String, - dialplan_app_data: String, - channel: Channel, - asterisk_id: String, - application: String, -} - -#[derive(Debug, Deserialize, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelStateChange { - timestamp: DateTime, - channel: Channel, - asterisk_id: String, - application: String, -} - -#[derive(Debug, Deserialize, Getters)] -#[serde(rename_all = "snake_case")] -pub struct ChannelDtmfReceived { - timestamp: DateTime, - digit: String, - duration_ms: i32, - channel: Channel, - asterisk_id: String, - application: String, -} #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] diff --git a/crates/ari/src/event.rs b/crates/ari/src/event.rs index 04a0764..9b8b845 100644 --- a/crates/ari/src/event.rs +++ b/crates/ari/src/event.rs @@ -1,4 +1,4 @@ -use chrono::DateTime; +use chrono::{DateTime, Utc}; use derive_getters::Getters; use serde::Deserialize; @@ -21,15 +21,107 @@ pub enum Event { Unknown, } +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct StasisStart { + timestamp: DateTime, + args: Vec, + channel: Channel, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct StasisEnd { + timestamp: DateTime, + channel: Channel, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelCreated { + timestamp: DateTime, + channel: Option, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelDestroyed { + timestamp: DateTime, + cause: i32, + cause_txt: String, + channel: Channel, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelVarset { + timestamp: DateTime, + variable: String, + value: String, + channel: Option, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelHangupRequest { + timestamp: DateTime, + soft: Option, + cause: i32, + channel: Channel, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelDialplan { + timestamp: DateTime, + dialplan_app: String, + dialplan_app_data: String, + channel: Channel, + asterisk_id: String, + application: String, +} + #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct DeviceStateChanged { application: String, - timestamp: DateTime, + timestamp: DateTime, device_state: DeviceState, asterisk_id: String, } +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelStateChange { + timestamp: DateTime, + channel: Channel, + asterisk_id: String, + application: String, +} + +#[derive(Debug, Deserialize, Getters)] +#[serde(rename_all = "snake_case")] +pub struct ChannelDtmfReceived { + timestamp: DateTime, + digit: String, + duration_ms: i32, + channel: Channel, + asterisk_id: String, + application: String, +} + #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct DeviceState { From 588be1e3016d5d95771ef27e8c6239ee53e60426 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sat, 14 Sep 2024 21:29:43 +0200 Subject: [PATCH 60/61] refactor: compose common event data with an `Event` struct --- Cargo.lock | 21 +++++++++++ Cargo.toml | 1 + crates/ari/Cargo.toml | 1 + crates/ari/examples/dtmf.rs | 6 +-- crates/ari/examples/playback.rs | 4 +- crates/ari/src/asterisk.rs | 6 +-- crates/ari/src/event.rs | 65 ++++++++++++--------------------- crates/ari/src/lib.rs | 2 +- 8 files changed, 56 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9a78c07..5f963f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,6 +55,7 @@ version = "0.1.0" dependencies = [ "chrono", "derive-getters", + "derive_more", "futures-channel", "futures-util", "rand", @@ -219,6 +220,26 @@ dependencies = [ "syn", ] +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.10.7" diff --git a/Cargo.toml b/Cargo.toml index 32e06c4..d0bef1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ strip = "debuginfo" [workspace.dependencies] chrono = "0.4" derive-getters = "0.5" +derive_more = "1.0" futures-channel = "0.3" futures-util = "0.3" rand = "0.8" diff --git a/crates/ari/Cargo.toml b/crates/ari/Cargo.toml index 3731507..8251911 100644 --- a/crates/ari/Cargo.toml +++ b/crates/ari/Cargo.toml @@ -8,6 +8,7 @@ version.workspace = true [dependencies] chrono = { workspace = true, features = ["serde"] } derive-getters.workspace = true +derive_more = { workspace = true, features = ["deref"] } futures-channel.workspace = true futures-util.workspace = true rand.workspace = true diff --git a/crates/ari/examples/dtmf.rs b/crates/ari/examples/dtmf.rs index 53779de..1fbe61f 100644 --- a/crates/ari/examples/dtmf.rs +++ b/crates/ari/examples/dtmf.rs @@ -1,6 +1,6 @@ use std::sync::{Arc, Mutex}; -use asterisk_rs_ari::{Asterisk, Event}; +use asterisk_rs_ari::{Asterisk, AsteriskEvent}; use tracing::debug; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; @@ -17,11 +17,11 @@ async fn main() -> Result<(), Box> { while let Some(event) = event_listener.recv().await { match event { - Event::ChannelDtmfReceived(event) => { + AsteriskEvent::ChannelDtmfReceived(event) => { debug!("Received DTMF: {}", event.digit()); dtmf_buffer.lock().unwrap().push_str(event.digit()); } - Event::StasisEnd(_) => { + AsteriskEvent::StasisEnd(_) => { debug!("Stasis ended, DTMF buffer: {}", dtmf_buffer.lock().unwrap()); dtmf_buffer.lock().unwrap().clear(); } diff --git a/crates/ari/examples/playback.rs b/crates/ari/examples/playback.rs index 3a3a9a2..c5eefea 100644 --- a/crates/ari/examples/playback.rs +++ b/crates/ari/examples/playback.rs @@ -1,4 +1,4 @@ -use asterisk_rs_ari::{Asterisk, Event, PlayMediaBaseParams, PlayMediaParams}; +use asterisk_rs_ari::{Asterisk, AsteriskEvent, PlayMediaBaseParams, PlayMediaParams}; use tracing::level_filters::LevelFilter; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; @@ -9,7 +9,7 @@ async fn main() -> Result<(), Box> { let (request_client, mut event_listener) = Asterisk::connect("http://localhost:8088/", "asterisk", "asterisk", "ari").await?; while let Some(event) = event_listener.recv().await { - if let Event::StasisStart(event) = event { + if let AsteriskEvent::StasisStart(event) = event { request_client .channel_play_media( event.channel().id(), diff --git a/crates/ari/src/asterisk.rs b/crates/ari/src/asterisk.rs index bf36f12..2a2b0a0 100644 --- a/crates/ari/src/asterisk.rs +++ b/crates/ari/src/asterisk.rs @@ -26,7 +26,7 @@ impl Asterisk { /// Connect to the Asterisk, return an ARI request client and an WebSocket event stream. /// /// Spawns a [`tokio::task`] that connects to the Asterisk - /// WebSocket endpoint to immideatedly listen to incoming [`Event`] + /// WebSocket endpoint to immideatedly listen to incoming [`AsteriskEvent`] /// (`crate::Event`)s. These may be listened to by polling the returned /// [`tokio::sync::mpsc::UnboundedReceiver`] stream. /// @@ -47,7 +47,7 @@ impl Asterisk { app_name: impl AsRef, username: impl AsRef, password: impl AsRef, - ) -> Result<(AriClient, UnboundedReceiver), AsteriskError> { + ) -> Result<(AriClient, UnboundedReceiver), AsteriskError> { let url = url.as_ref().parse::()?.join("ari/")?; let api_key = Authorization::api_key(username.as_ref(), password.as_ref()); @@ -91,7 +91,7 @@ impl Asterisk { Ok(ws_url) } - async fn connect_ws(ws_url: &Url) -> Result, AsteriskError> { + async fn connect_ws(ws_url: &Url) -> Result, AsteriskError> { let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); let (ws_stream, _) = connect_async(ws_url).await.map_err(AsteriskError::WebSocketConnect)?; diff --git a/crates/ari/src/event.rs b/crates/ari/src/event.rs index 9b8b845..29f94a8 100644 --- a/crates/ari/src/event.rs +++ b/crates/ari/src/event.rs @@ -1,125 +1,108 @@ use chrono::{DateTime, Utc}; use derive_getters::Getters; +use derive_more::derive::Deref; use serde::Deserialize; use crate::*; #[derive(Debug, Deserialize)] #[serde(tag = "type")] -pub enum Event { - StasisStart(StasisStart), - StasisEnd(StasisEnd), - ChannelCreated(ChannelCreated), - ChannelDestroyed(ChannelDestroyed), - ChannelVarset(ChannelVarset), - ChannelHangupRequest(ChannelHangupRequest), - ChannelDialplan(ChannelDialplan), - ChannelStateChange(ChannelStateChange), - ChannelDtmfReceived(ChannelDtmfReceived), - DeviceStateChanged(DeviceStateChanged), +pub enum AsteriskEvent { + StasisStart(Event), + StasisEnd(Event), + ChannelCreated(Event), + ChannelDestroyed(Event), + ChannelVarset(Event), + ChannelHangupRequest(Event), + ChannelDialplan(Event), + ChannelStateChange(Event), + ChannelDtmfReceived(Event), + DeviceStateChanged(Event), #[serde(other)] Unknown, } +#[derive(Debug, Deserialize, Getters, Deref)] +#[serde(rename_all = "snake_case")] +pub struct Event { + asterisk_id: String, + application: String, + timestamp: DateTime, + #[deref] + #[getter(skip)] + #[serde(flatten)] + data: D, +} + #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct StasisStart { - timestamp: DateTime, args: Vec, channel: Channel, - asterisk_id: String, - application: String, } #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct StasisEnd { - timestamp: DateTime, channel: Channel, - asterisk_id: String, - application: String, } #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelCreated { - timestamp: DateTime, channel: Option, - asterisk_id: String, - application: String, } #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelDestroyed { - timestamp: DateTime, cause: i32, cause_txt: String, channel: Channel, - asterisk_id: String, - application: String, } #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelVarset { - timestamp: DateTime, variable: String, value: String, channel: Option, - asterisk_id: String, - application: String, } #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelHangupRequest { - timestamp: DateTime, soft: Option, cause: i32, channel: Channel, - asterisk_id: String, - application: String, } #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelDialplan { - timestamp: DateTime, dialplan_app: String, dialplan_app_data: String, channel: Channel, - asterisk_id: String, - application: String, } #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct DeviceStateChanged { - application: String, - timestamp: DateTime, device_state: DeviceState, - asterisk_id: String, } #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelStateChange { - timestamp: DateTime, channel: Channel, - asterisk_id: String, - application: String, } #[derive(Debug, Deserialize, Getters)] #[serde(rename_all = "snake_case")] pub struct ChannelDtmfReceived { - timestamp: DateTime, digit: String, duration_ms: i32, channel: Channel, - asterisk_id: String, - application: String, } #[derive(Debug, Deserialize, Getters)] diff --git a/crates/ari/src/lib.rs b/crates/ari/src/lib.rs index b854c21..1fcfacf 100644 --- a/crates/ari/src/lib.rs +++ b/crates/ari/src/lib.rs @@ -8,4 +8,4 @@ mod authorization; pub(crate) use authorization::Authorization; mod event; -pub use event::Event; +pub use event::AsteriskEvent; From 8562bc0ffea067ddd42ecf23b1b17fd556ba936f Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Sun, 15 Sep 2024 07:44:32 +0200 Subject: [PATCH 61/61] feat(ari): add missing `StasisStart.replace_channel` field --- crates/ari/src/event.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/ari/src/event.rs b/crates/ari/src/event.rs index 29f94a8..5152670 100644 --- a/crates/ari/src/event.rs +++ b/crates/ari/src/event.rs @@ -8,7 +8,7 @@ use crate::*; #[derive(Debug, Deserialize)] #[serde(tag = "type")] pub enum AsteriskEvent { - StasisStart(Event), + StasisStart(Box>), StasisEnd(Event), ChannelCreated(Event), ChannelDestroyed(Event), @@ -39,6 +39,7 @@ pub struct Event { pub struct StasisStart { args: Vec, channel: Channel, + replace_channel: Option, } #[derive(Debug, Deserialize, Getters)]