use super::app_context::ApplicationContext;
use super::controller::ControllerEvent;
use super::{AppAction, AppEvent, CallApp, CallController, ExitReason};
use tokio::sync::mpsc;
use tokio::time::sleep;
use tokio_util::sync::CancellationToken;
pub struct AppEventLoop {
app: Box<dyn CallApp>,
controller: CallController,
context: ApplicationContext,
cancel_token: CancellationToken,
fired_timer_rx: mpsc::UnboundedReceiver<String>,
}
impl AppEventLoop {
pub fn new(
app: Box<dyn CallApp>,
controller: CallController,
context: ApplicationContext,
cancel_token: CancellationToken,
fired_timer_rx: mpsc::UnboundedReceiver<String>,
) -> Self {
Self {
app,
controller,
context,
cancel_token,
fired_timer_rx,
}
}
pub async fn run(mut self) -> anyhow::Result<()> {
let mut action = self
.app
.on_enter(&mut self.controller, &self.context)
.await?;
loop {
if self.cancel_token.is_cancelled() {
self.app.on_exit(ExitReason::Cancelled).await?;
return Ok(());
}
match action {
AppAction::Continue => {
action = self.handle_next_event().await?;
}
AppAction::Exit => {
self.app.on_exit(ExitReason::Normal).await?;
break;
}
AppAction::Hangup { reason, code } => {
self.controller.hangup(reason.clone(), code).await?;
self.app.on_exit(ExitReason::Hangup).await?;
break;
}
AppAction::Transfer(target) => {
self.controller.transfer(target).await?;
self.app.on_exit(ExitReason::Transferred).await?;
break;
}
AppAction::Chain(next_app) => {
self.app.on_exit(ExitReason::Chained).await?;
self.app = next_app;
action = self
.app
.on_enter(&mut self.controller, &self.context)
.await?;
}
AppAction::Sleep(duration) => {
sleep(duration).await;
action = AppAction::Continue;
}
}
}
Ok(())
}
async fn handle_next_event(&mut self) -> anyhow::Result<AppAction> {
tokio::select! {
event = self.controller.wait_event() => {
match event {
Some(ControllerEvent::DtmfReceived(digit)) => {
self.app.on_dtmf(digit, &mut self.controller, &self.context).await
}
Some(ControllerEvent::AudioComplete { track_id, .. }) => {
self.app.on_audio_complete(track_id, &mut self.controller, &self.context).await
}
Some(ControllerEvent::RecordingComplete(info)) => {
self.app.on_record_complete(info, &mut self.controller, &self.context).await
}
Some(ControllerEvent::Hangup(reason)) => {
self.app.on_exit(ExitReason::RemoteHangup(reason)).await?;
Ok(AppAction::Exit)
}
Some(ControllerEvent::Timeout(id)) => {
self.app.on_timeout(id, &mut self.controller, &self.context).await
}
Some(ControllerEvent::Custom(name, data)) => {
self.app.on_external_event(
AppEvent::Custom { name, data },
&mut self.controller,
&self.context
).await
}
None => Ok(AppAction::Exit),
}
}
Some(timer_id) = self.fired_timer_rx.recv() => {
self.app.on_timeout(timer_id, &mut self.controller, &self.context).await
}
_ = self.cancel_token.cancelled() => {
self.app.on_exit(ExitReason::Cancelled).await?;
Ok(AppAction::Exit)
}
}
}
}