1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//! Process-local identity and cancellation for current and prefetched playback attempts.
use mineral_model::{Envelope, SongId};
use tokio_util::sync::CancellationToken;
use uuid::Uuid;
/// Unique identity of one real current-play or prefetch attempt.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct PlaybackInstanceId(Uuid);
impl std::fmt::Display for PlaybackInstanceId {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(formatter)
}
}
impl PlaybackInstanceId {
/// Creates a fresh process-local playback identity.
pub(crate) fn new() -> Self {
Self(Uuid::new_v4())
}
}
/// Active ownership slot for one playback attempt.
#[derive(Clone)]
pub(crate) struct PlaybackSlot {
/// Unique attempt identity.
pub(crate) instance_id: PlaybackInstanceId,
/// Defensive domain identity paired with the attempt.
pub(crate) song_id: SongId,
/// Root cancellation token for resolve, hook, open, reader, and instance-only envelope work.
pub(crate) cancellation: CancellationToken,
/// Envelope retained only for this attempt and moved with gapless promotion.
pub(crate) envelope: Option<Envelope>,
}
impl PlaybackSlot {
/// Creates a fresh slot for a song.
///
/// # Params:
/// - `song_id`: Song targeted by this attempt.
pub(crate) fn new(song_id: SongId) -> Self {
Self {
instance_id: PlaybackInstanceId::new(),
song_id,
cancellation: CancellationToken::new(),
envelope: None,
}
}
/// Returns whether both attempt and song identities match.
///
/// # Params:
/// - `instance_id`: Attempt identity to validate.
/// - `song_id`: Song identity to validate.
pub(crate) fn matches(&self, instance_id: PlaybackInstanceId, song_id: &SongId) -> bool {
self.instance_id == instance_id && self.song_id == *song_id
}
/// Cancels all work owned by this slot.
pub(crate) fn cancel(&self) {
self.cancellation.cancel();
}
}
#[cfg(test)]
mod tests {
use mineral_model::{SongId, SourceKind};
use super::PlaybackSlot;
/// Two attempts for the same song still have different identities.
#[test]
fn same_song_attempts_are_distinct() {
let song = SongId::new(SourceKind::NETEASE, "1");
let first = PlaybackSlot::new(song.clone());
let second = PlaybackSlot::new(song);
assert_ne!(first.instance_id, second.instance_id);
}
}