kithara_queue/queue/selection/
navigation.rs1use kithara_bufpool::HasPool;
2use kithara_events::TrackId;
3use smallvec::SmallVec;
4use tracing::debug;
5
6use crate::{
7 error::QueueError,
8 event::{AdvanceReason, QueueEvent, TrackStatus},
9 navigation::RepeatMode,
10 queue::{QueueControl, types::Transition},
11 track::TrackEntry,
12};
13
14impl<S> QueueControl<S>
15where
16 S: HasPool<u8> + HasPool<f32> + Send + Sync + 'static,
17{
18 pub(in crate::queue) fn advance_to_next_inner(
19 &self,
20 transition: Transition,
21 reason: AdvanceReason,
22 ) -> Result<Option<TrackId>, QueueError> {
23 let Some(next) = self.next_selectable_entry(reason) else {
24 if matches!(
25 reason,
26 AdvanceReason::NaturalEof
27 | AdvanceReason::TrackFailed
28 | AdvanceReason::CrossfadePreArm
29 ) {
30 self.lock_navigation_mut().finish();
31 self.bus.publish(QueueEvent::QueueEnded);
32 }
33 return Ok(None);
34 };
35 let id = next.id;
36 self.select_with_reason(id, transition, reason)?;
37 Ok(Some(id))
38 }
39
40 pub fn next(&self, transition: Transition) -> Result<Option<TrackId>, QueueError> {
48 self.with_open_result(|queue| {
49 queue.advance_to_next_inner(transition, AdvanceReason::UserNext)
50 })
51 }
52
53 pub(in crate::queue) fn next_selectable_entry(
56 &self,
57 reason: AdvanceReason,
58 ) -> Option<TrackEntry> {
59 let tracks = self.lock_tracks();
60 let selectable = tracks
61 .iter()
62 .filter(|record| {
63 let available = !matches!(
64 record.status,
65 TrackStatus::Cancelled | TrackStatus::Failed(_)
66 );
67 if !available {
68 debug!(
69 id = record.id.as_u64(),
70 "navigation skipped unavailable track"
71 );
72 }
73 available
74 })
75 .map(crate::track::TrackRecord::entry)
76 .collect::<Vec<TrackEntry>>();
77 drop(tracks);
78 let ids = selectable
79 .iter()
80 .map(|entry| entry.id)
81 .collect::<SmallVec<[_; 16]>>();
82 let mut navigation = self.lock_navigation_mut();
83 let automatic = matches!(
84 reason,
85 AdvanceReason::NaturalEof | AdvanceReason::TrackFailed | AdvanceReason::CrossfadePreArm
86 );
87 let allow_repeat_one = matches!(
88 reason,
89 AdvanceReason::NaturalEof | AdvanceReason::CrossfadePreArm
90 );
91 let allow_wrap = automatic && navigation.repeat_mode() == RepeatMode::All;
92 let id = navigation.next(&ids, allow_repeat_one, allow_wrap)?;
93 drop(navigation);
94 selectable.into_iter().find(|entry| entry.id == id)
95 }
96
97 pub(in crate::queue) fn peek_selectable_entry(&self) -> Option<TrackEntry> {
98 let selectable = self
99 .lock_tracks()
100 .iter()
101 .filter(|record| {
102 !matches!(
103 record.status,
104 TrackStatus::Cancelled | TrackStatus::Failed(_)
105 )
106 })
107 .map(crate::track::TrackRecord::entry)
108 .collect::<Vec<TrackEntry>>();
109 let ids = selectable
110 .iter()
111 .map(|entry| entry.id)
112 .collect::<SmallVec<[_; 16]>>();
113 let id = self.lock_navigation().peek_next(&ids)?;
114 selectable.into_iter().find(|entry| entry.id == id)
115 }
116
117 pub fn previous(&self, transition: Transition) -> Result<Option<TrackId>, QueueError> {
124 self.with_open_result(|queue| queue.return_to_previous_inner(transition))
125 }
126
127 fn return_to_previous_inner(
128 &self,
129 transition: Transition,
130 ) -> Result<Option<TrackId>, QueueError> {
131 let tracks = self.tracks();
132 let ids = tracks
133 .iter()
134 .map(|entry| entry.id)
135 .collect::<SmallVec<[_; 16]>>();
136 let Some(id) = self.lock_navigation_mut().prev(&ids) else {
137 return Ok(None);
138 };
139 self.select_with_reason(id, transition, AdvanceReason::UserPrev)?;
140 Ok(Some(id))
141 }
142}