use std::collections::VecDeque;
pub struct Queue {
tracks: VecDeque<String>,
}
impl Queue {
#[must_use]
pub fn new() -> Self {
Self {
tracks: VecDeque::new(),
}
}
pub fn push(&mut self, track: String) {
self.tracks.push_back(track);
}
pub fn push_front(&mut self, track: String) {
self.tracks.push_front(track);
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<String> {
self.tracks.pop_front()
}
#[must_use]
pub fn peek(&self) -> Option<&str> {
self.tracks.front().map(String::as_str)
}
pub fn remove(&mut self, index: usize) -> Option<String> {
self.tracks.remove(index)
}
pub fn clear(&mut self) {
self.tracks.clear();
}
pub fn shuffle(&mut self) {
let len = self.tracks.len();
if len <= 1 {
return;
}
let tracks = self.tracks.make_contiguous();
for i in (1..len).rev() {
let j = (i * 2_654_435_761) % (i + 1);
tracks.swap(i, j);
}
}
#[must_use]
pub fn len(&self) -> usize {
self.tracks.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.tracks.is_empty()
}
#[must_use]
pub fn items(&self) -> Vec<&str> {
self.tracks.iter().map(String::as_str).collect()
}
pub fn move_track(&mut self, from: usize, to: usize) {
let len = self.tracks.len();
if from >= len || to >= len || from == to {
return;
}
let track = self.tracks.remove(from).expect("from index was bounds-checked");
let insert_at = if to > len - 1 { len - 1 } else { to };
self.tracks.insert(insert_at, track);
}
}
impl Default for Queue {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RepeatMode {
Off,
One,
All,
}
pub struct QueueManager {
queue: Queue,
repeat: RepeatMode,
history: Vec<String>,
current: Option<String>,
}
impl QueueManager {
#[must_use]
pub fn new() -> Self {
Self {
queue: Queue::new(),
repeat: RepeatMode::Off,
history: Vec::new(),
current: None,
}
}
pub fn queue_mut(&mut self) -> &mut Queue {
&mut self.queue
}
#[must_use]
pub fn queue(&self) -> &Queue {
&self.queue
}
pub fn advance(&mut self) -> Option<String> {
match self.repeat {
RepeatMode::One => {
self.current.clone()
}
RepeatMode::Off | RepeatMode::All => {
if let Some(ref cur) = self.current {
self.history.push(cur.clone());
}
let next = self.queue.next();
if next.is_none() && self.repeat == RepeatMode::All {
self.current = None;
return None;
}
self.current.clone_from(&next);
next
}
}
}
pub fn previous(&mut self) -> Option<String> {
let prev = self.history.pop()?;
if let Some(cur) = self.current.take() {
self.queue.push_front(cur);
}
self.current = Some(prev.clone());
Some(prev)
}
pub fn set_repeat(&mut self, mode: RepeatMode) {
self.repeat = mode;
}
#[must_use]
pub fn repeat_mode(&self) -> RepeatMode {
self.repeat
}
#[must_use]
pub fn current(&self) -> Option<&str> {
self.current.as_deref()
}
}
impl Default for QueueManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn queue_new_is_empty() {
let q = Queue::new();
assert!(q.is_empty());
assert_eq!(q.len(), 0);
}
#[test]
fn queue_push_and_next() {
let mut q = Queue::new();
q.push("a.flac".into());
q.push("b.flac".into());
assert_eq!(q.len(), 2);
assert_eq!(q.next(), Some("a.flac".into()));
assert_eq!(q.next(), Some("b.flac".into()));
assert_eq!(q.next(), None);
}
#[test]
fn queue_push_front() {
let mut q = Queue::new();
q.push("a.flac".into());
q.push_front("urgent.flac".into());
assert_eq!(q.next(), Some("urgent.flac".into()));
assert_eq!(q.next(), Some("a.flac".into()));
}
#[test]
fn queue_peek() {
let mut q = Queue::new();
assert!(q.peek().is_none());
q.push("track.mp3".into());
assert_eq!(q.peek(), Some("track.mp3"));
assert_eq!(q.len(), 1);
}
#[test]
fn queue_remove() {
let mut q = Queue::new();
q.push("a".into());
q.push("b".into());
q.push("c".into());
let removed = q.remove(1);
assert_eq!(removed, Some("b".into()));
assert_eq!(q.items(), vec!["a", "c"]);
}
#[test]
fn queue_remove_out_of_bounds() {
let mut q = Queue::new();
q.push("a".into());
assert!(q.remove(5).is_none());
}
#[test]
fn queue_clear() {
let mut q = Queue::new();
q.push("a".into());
q.push("b".into());
q.clear();
assert!(q.is_empty());
assert_eq!(q.len(), 0);
}
#[test]
fn queue_items() {
let mut q = Queue::new();
q.push("x".into());
q.push("y".into());
q.push("z".into());
assert_eq!(q.items(), vec!["x", "y", "z"]);
}
#[test]
fn queue_move_track_forward() {
let mut q = Queue::new();
q.push("a".into());
q.push("b".into());
q.push("c".into());
q.push("d".into());
q.move_track(0, 2);
assert_eq!(q.items(), vec!["b", "c", "a", "d"]);
}
#[test]
fn queue_move_track_backward() {
let mut q = Queue::new();
q.push("a".into());
q.push("b".into());
q.push("c".into());
q.push("d".into());
q.move_track(3, 1);
assert_eq!(q.items(), vec!["a", "d", "b", "c"]);
}
#[test]
fn queue_move_track_same_index() {
let mut q = Queue::new();
q.push("a".into());
q.push("b".into());
q.move_track(1, 1);
assert_eq!(q.items(), vec!["a", "b"]);
}
#[test]
fn queue_move_track_out_of_bounds() {
let mut q = Queue::new();
q.push("a".into());
q.move_track(0, 10);
assert_eq!(q.items(), vec!["a"]);
}
#[test]
fn queue_shuffle_changes_order() {
let mut q = Queue::new();
for i in 0..10 {
q.push(format!("track_{i}"));
}
let before: Vec<String> = q.items().iter().map(|s| (*s).to_owned()).collect();
q.shuffle();
let after: Vec<String> = q.items().iter().map(|s| (*s).to_owned()).collect();
assert_eq!(before.len(), after.len());
assert_ne!(before, after, "shuffle should change order for 10 items");
}
#[test]
fn queue_shuffle_single_item() {
let mut q = Queue::new();
q.push("only.mp3".into());
q.shuffle();
assert_eq!(q.items(), vec!["only.mp3"]);
}
#[test]
fn queue_len_and_is_empty() {
let mut q = Queue::new();
assert!(q.is_empty());
assert_eq!(q.len(), 0);
q.push("a".into());
assert!(!q.is_empty());
assert_eq!(q.len(), 1);
}
#[test]
fn manager_advance_off_drains() {
let mut mgr = QueueManager::new();
mgr.queue_mut().push("a".into());
mgr.queue_mut().push("b".into());
assert_eq!(mgr.advance(), Some("a".into()));
assert_eq!(mgr.current(), Some("a"));
assert_eq!(mgr.advance(), Some("b".into()));
assert_eq!(mgr.current(), Some("b"));
assert_eq!(mgr.advance(), None);
}
#[test]
fn manager_advance_repeat_one() {
let mut mgr = QueueManager::new();
mgr.queue_mut().push("loop.flac".into());
mgr.set_repeat(RepeatMode::One);
let first = mgr.advance(); let mut mgr = QueueManager::new();
mgr.queue_mut().push("loop.flac".into());
mgr.queue_mut().push("next.flac".into());
mgr.set_repeat(RepeatMode::Off);
assert_eq!(mgr.advance(), Some("loop.flac".into()));
mgr.set_repeat(RepeatMode::One);
assert_eq!(mgr.advance(), Some("loop.flac".into()));
assert_eq!(mgr.advance(), Some("loop.flac".into()));
assert_eq!(mgr.advance(), Some("loop.flac".into()));
assert_eq!(mgr.queue().len(), 1);
let _ = first;
}
#[test]
fn manager_advance_repeat_all_exhausts() {
let mut mgr = QueueManager::new();
mgr.queue_mut().push("a".into());
mgr.queue_mut().push("b".into());
mgr.set_repeat(RepeatMode::All);
assert_eq!(mgr.advance(), Some("a".into()));
assert_eq!(mgr.advance(), Some("b".into()));
assert_eq!(mgr.advance(), None);
}
#[test]
fn manager_previous_returns_history() {
let mut mgr = QueueManager::new();
mgr.queue_mut().push("first.mp3".into());
mgr.queue_mut().push("second.mp3".into());
mgr.queue_mut().push("third.mp3".into());
mgr.advance(); mgr.advance(); mgr.advance();
let prev = mgr.previous();
assert_eq!(prev, Some("second.mp3".into()));
assert_eq!(mgr.queue().peek(), Some("third.mp3"));
}
#[test]
fn manager_previous_empty_history() {
let mut mgr = QueueManager::new();
assert!(mgr.previous().is_none());
}
#[test]
fn manager_set_repeat() {
let mut mgr = QueueManager::new();
assert_eq!(mgr.repeat_mode(), RepeatMode::Off);
mgr.set_repeat(RepeatMode::All);
assert_eq!(mgr.repeat_mode(), RepeatMode::All);
mgr.set_repeat(RepeatMode::One);
assert_eq!(mgr.repeat_mode(), RepeatMode::One);
}
}