use super::*;
use crate::events::{Event, EventData, EventHandler};
use flume::{Receiver, Sender};
use nonmax::NonMaxU32;
use std::{any::Any, sync::Arc, time::Duration};
use uuid::Uuid;
#[derive(Clone, Debug)]
pub struct TrackHandle {
inner: Arc<InnerHandle>,
}
#[derive(Debug)]
struct InnerHandle {
command_channel: Sender<TrackCommand>,
uuid: Uuid,
data: Arc<dyn Any + Send + Sync + 'static>,
}
impl TrackHandle {
#[must_use]
pub(crate) fn new(
command_channel: Sender<TrackCommand>,
uuid: Uuid,
data: Arc<dyn Any + Send + Sync + 'static>,
) -> Self {
let inner = Arc::new(InnerHandle {
command_channel,
uuid,
data,
});
Self { inner }
}
pub fn play(&self) -> TrackResult<()> {
self.send(TrackCommand::Play)
}
pub fn pause(&self) -> TrackResult<()> {
self.send(TrackCommand::Pause)
}
pub fn stop(&self) -> TrackResult<()> {
self.send(TrackCommand::Stop)
}
pub fn set_volume(&self, volume: f32) -> TrackResult<()> {
self.send(TrackCommand::Volume(volume))
}
#[must_use]
pub fn make_playable(&self) -> TrackCallback<()> {
let (tx, rx) = flume::bounded(1);
let fail = self.send(TrackCommand::MakePlayable(tx)).is_err();
TrackCallback { fail, rx }
}
pub async fn make_playable_async(&self) -> TrackResult<()> {
self.make_playable().result_async().await
}
#[must_use]
pub fn seek(&self, position: Duration) -> TrackCallback<Duration> {
let (tx, rx) = flume::bounded(1);
let fail = self
.send(TrackCommand::Seek(SeekRequest {
time: position,
callback: tx,
}))
.is_err();
TrackCallback { fail, rx }
}
pub async fn seek_async(&self, position: Duration) -> TrackResult<Duration> {
self.seek(position).result_async().await
}
pub fn add_event<F: EventHandler + 'static>(&self, event: Event, action: F) -> TrackResult<()> {
let cmd = TrackCommand::AddEvent(EventData::new(event, action));
if event.is_global_only() {
Err(ControlError::InvalidTrackEvent)
} else {
self.send(cmd)
}
}
pub fn action<F>(&self, action: F) -> TrackResult<()>
where
F: FnOnce(View<'_>) -> Option<Action> + Send + Sync + 'static,
{
self.send(TrackCommand::Do(Box::new(action)))
}
pub async fn get_info(&self) -> TrackResult<TrackState> {
let (tx, rx) = flume::bounded(1);
self.send(TrackCommand::Request(tx))?;
rx.recv_async().await.map_err(|_| ControlError::Finished)
}
pub fn enable_loop(&self) -> TrackResult<()> {
self.send(TrackCommand::Loop(LoopState::Infinite))
}
pub fn disable_loop(&self) -> TrackResult<()> {
self.send(TrackCommand::Loop(LoopState::Finite(NonMaxU32::ZERO)))
}
pub fn loop_for(&self, count: NonMaxU32) -> TrackResult<()> {
self.send(TrackCommand::Loop(LoopState::Finite(count)))
}
#[must_use]
pub fn uuid(&self) -> Uuid {
self.inner.uuid
}
#[must_use]
pub fn data<Data>(&self) -> Arc<Data>
where
Data: Send + Sync + 'static,
{
Arc::clone(&self.inner.data)
.downcast()
.expect("TrackHandle::data generic does not match type set in TrackHandle::set_data")
}
#[inline]
pub(crate) fn send(&self, cmd: TrackCommand) -> TrackResult<()> {
self.inner
.command_channel
.send(cmd)
.map_err(|_e| ControlError::Finished)
}
}
pub struct TrackCallback<T> {
fail: bool,
rx: Receiver<Result<T, PlayError>>,
}
impl<T> TrackCallback<T> {
pub fn result(self) -> TrackResult<T> {
if self.fail {
Err(ControlError::Finished)
} else {
self.rx.recv()?.map_err(ControlError::Play)
}
}
pub async fn result_async(self) -> TrackResult<T> {
if self.fail {
Err(ControlError::Finished)
} else {
self.rx.recv_async().await?.map_err(ControlError::Play)
}
}
#[must_use]
pub fn is_hung_up(&self) -> bool {
self.fail
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{constants::test_data::FILE_WAV_TARGET, driver::Driver, input::File, Config};
#[tokio::test]
#[ntest::timeout(10_000)]
async fn make_playable_callback_fires() {
let (t_handle, config) = Config::test_cfg(true);
let mut driver = Driver::new(config.clone());
let file = File::new(FILE_WAV_TARGET);
let handle = driver.play(Track::from(file).pause());
let callback = handle.make_playable();
t_handle.spawn_ticker();
assert!(callback.result_async().await.is_ok());
}
#[tokio::test]
#[ntest::timeout(10_000)]
async fn seek_callback_fires() {
let (t_handle, config) = Config::test_cfg(true);
let mut driver = Driver::new(config.clone());
let file = File::new(FILE_WAV_TARGET);
let handle = driver.play(Track::from(file).pause());
let target = Duration::from_millis(500);
let callback = handle.seek(target);
t_handle.spawn_ticker();
let answer = callback.result_async().await;
assert!(answer.is_ok());
let answer = answer.unwrap();
let delta = Duration::from_millis(100);
assert!(answer > target - delta && answer < target + delta);
}
}