🚀 refactor(pmocontrol): eliminate QueueBackend boilerplate with HasQueue blanket impl
- Add `Hasqueue` trait and implement it for all renderers (Upnp, OpenHome, LinkPlay, ArylicTcp, Chromecast) - Replace manual `QueueBackend` implementations with blanket impl for types implementing Hasqueue (removes ~30+ duplicated methods across renderers) - Fix BUG: `sync_queue` in UpnpRenderer now correctly propagates cancel_token instead of ignoring it - Update version to 0.3.48 in Cargo.toml, lockfile and root file - Add refactoring plan document (`refactoring_pmocontrol.md`) detailing remaining P1-P3 tasks
This commit is contained in:
@@ -28,8 +28,89 @@
|
||||
//! - This identity is used by the sync helpers to preserve the current
|
||||
//! track across queue rebuilds when the MediaServer content changes.
|
||||
|
||||
use crate::queue::MusicQueue;
|
||||
use crate::{errors::ControlPointError, PlaybackItem, QueueSnapshot};
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
use std::sync::{atomic::AtomicBool, Arc, Mutex};
|
||||
|
||||
/// Trait for types that have aMusicQueue.
|
||||
pub trait HasQueue {
|
||||
fn queue(&self) -> &Arc<Mutex<MusicQueue>>;
|
||||
}
|
||||
|
||||
/// Blanket implementation of QueueBackend for types that have a queue.
|
||||
/// All methods simply delegate to the underlying MusicQueue.
|
||||
impl<T: HasQueue> QueueBackend for T {
|
||||
fn len(&self) -> Result<usize, ControlPointError> {
|
||||
self.queue().lock().unwrap().len()
|
||||
}
|
||||
|
||||
fn track_ids(&self) -> Result<Vec<u32>, ControlPointError> {
|
||||
self.queue().lock().unwrap().track_ids()
|
||||
}
|
||||
|
||||
fn id_to_position(&self, id: u32) -> Result<usize, ControlPointError> {
|
||||
self.queue().lock().unwrap().id_to_position(id)
|
||||
}
|
||||
|
||||
fn position_to_id(&self, id: usize) -> Result<u32, ControlPointError> {
|
||||
self.queue().lock().unwrap().position_to_id(id)
|
||||
}
|
||||
|
||||
fn current_track(&self) -> Result<Option<u32>, ControlPointError> {
|
||||
self.queue().lock().unwrap().current_track()
|
||||
}
|
||||
|
||||
fn current_index(&self) -> Result<Option<usize>, ControlPointError> {
|
||||
self.queue().lock().unwrap().current_index()
|
||||
}
|
||||
|
||||
fn queue_snapshot(&self) -> Result<QueueSnapshot, ControlPointError> {
|
||||
self.queue().lock().unwrap().queue_snapshot()
|
||||
}
|
||||
|
||||
fn set_index(&mut self, index: Option<usize>) -> Result<(), ControlPointError> {
|
||||
self.queue().lock().unwrap().set_index(index)
|
||||
}
|
||||
|
||||
fn replace_queue(
|
||||
&mut self,
|
||||
items: Vec<PlaybackItem>,
|
||||
current_index: Option<usize>,
|
||||
) -> Result<(), ControlPointError> {
|
||||
self.queue()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.replace_queue(items, current_index)
|
||||
}
|
||||
|
||||
fn sync_queue(
|
||||
&mut self,
|
||||
items: Vec<PlaybackItem>,
|
||||
cancel_token: &Arc<AtomicBool>,
|
||||
on_ready: Option<Box<dyn FnOnce() + Send>>,
|
||||
) -> Result<(), ControlPointError> {
|
||||
self.queue()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.sync_queue(items, cancel_token, on_ready)
|
||||
}
|
||||
|
||||
fn get_item(&self, index: usize) -> Result<Option<PlaybackItem>, ControlPointError> {
|
||||
self.queue().lock().unwrap().get_item(index)
|
||||
}
|
||||
|
||||
fn replace_item(&mut self, index: usize, item: PlaybackItem) -> Result<(), ControlPointError> {
|
||||
self.queue().lock().unwrap().replace_item(index, item)
|
||||
}
|
||||
|
||||
fn enqueue_items(
|
||||
&mut self,
|
||||
items: Vec<PlaybackItem>,
|
||||
mode: EnqueueMode,
|
||||
) -> Result<(), ControlPointError> {
|
||||
self.queue().lock().unwrap().enqueue_items(items, mode)
|
||||
}
|
||||
}
|
||||
|
||||
/// High-level enqueue mode.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user