2024-07-08 22:58:15 -05:00
|
|
|
use std::collections::VecDeque;
|
|
|
|
|
2024-07-07 22:44:39 -05:00
|
|
|
use super::song::{Plan, Song, Verse};
|
2024-07-06 18:18:34 -05:00
|
|
|
|
|
|
|
pub struct PlaylistEntry {
|
|
|
|
pub song: Song,
|
2024-07-08 22:58:15 -05:00
|
|
|
pub plan_name: Option<String>,
|
2024-07-06 18:18:34 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct PlaylistVerseRef {
|
|
|
|
pub song_index: usize,
|
|
|
|
pub map_verse_index: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Display {
|
2024-07-08 22:58:15 -05:00
|
|
|
pub playlist: VecDeque<PlaylistEntry>,
|
2024-07-07 22:44:39 -05:00
|
|
|
current: PlaylistVerseRef,
|
|
|
|
frozen_at: Option<PlaylistVerseRef>,
|
2024-07-06 18:18:34 -05:00
|
|
|
pub blanked: bool,
|
|
|
|
}
|
2024-07-07 22:44:39 -05:00
|
|
|
|
|
|
|
impl Display {
|
|
|
|
pub fn verse_ref(&self) -> &PlaylistVerseRef {
|
|
|
|
self.frozen_at.as_ref().unwrap_or(&self.current)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn verse(&self) -> Option<&Verse> {
|
|
|
|
if self.blanked {
|
|
|
|
None
|
|
|
|
} else {
|
2024-07-08 22:58:15 -05:00
|
|
|
let verse_ref = self.verse_ref();
|
|
|
|
let entry = self.entry(verse_ref)?;
|
|
|
|
entry.song.verses.get(self.verse_name(verse_ref)?)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn entry(&self, verse_ref: &PlaylistVerseRef) -> Option<&PlaylistEntry> {
|
|
|
|
self.playlist.get(verse_ref.song_index)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn plan(&self, verse_ref: &PlaylistVerseRef) -> Option<&Plan> {
|
|
|
|
let PlaylistEntry { song, plan_name } = self.entry(verse_ref)?;
|
|
|
|
// TODO: this could "fail silently" and use the default plan
|
|
|
|
Some(song.plan(plan_name.as_deref()))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn verse_name(&self, verse_ref: &PlaylistVerseRef) -> Option<&String> {
|
|
|
|
self.plan(verse_ref)?.get(verse_ref.map_verse_index)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn clamp(&mut self) {
|
|
|
|
// ensure all plan names exist or fallback to defaults
|
|
|
|
for p in self.playlist.iter_mut() {
|
|
|
|
if let Some(plan_name) = p.plan_name.as_mut() {
|
|
|
|
if !p.song.other_plans.contains_key(plan_name) {
|
|
|
|
p.plan_name = None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ensure song index is within bounds or be 0
|
|
|
|
self.current.song_index = self.current.song_index.clamp(0, self.playlist.len());
|
|
|
|
if let Some(frozen_at) = self.frozen_at.as_mut() {
|
|
|
|
frozen_at.song_index = frozen_at.song_index.clamp(0, self.playlist.len());
|
|
|
|
}
|
|
|
|
|
|
|
|
// ensure map verse index is within bounds or be 0
|
|
|
|
if let Some(plan) = self.plan(&self.current) {
|
|
|
|
self.current.map_verse_index = self.current.map_verse_index.clamp(0, plan.len());
|
|
|
|
}
|
|
|
|
let new_frozen_map_verse_index = match &self.frozen_at {
|
|
|
|
Some(frozen_at) => {
|
|
|
|
if let Some(plan) = self.plan(&frozen_at) {
|
|
|
|
frozen_at.map_verse_index.clamp(0, plan.len())
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => 0,
|
|
|
|
};
|
|
|
|
if let Some(frozen_at) = self.frozen_at.as_mut() {
|
|
|
|
frozen_at.map_verse_index = new_frozen_map_verse_index
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
fn default(playlist: VecDeque<PlaylistEntry>) -> Display {
|
|
|
|
let current = PlaylistVerseRef {
|
|
|
|
song_index: 0,
|
|
|
|
map_verse_index: 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
Display {
|
|
|
|
playlist,
|
|
|
|
current,
|
|
|
|
frozen_at: None,
|
|
|
|
blanked: false,
|
2024-07-07 22:44:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-08 22:58:15 -05:00
|
|
|
fn two_song_playlist() -> VecDeque<PlaylistEntry> {
|
|
|
|
VecDeque::from([
|
|
|
|
PlaylistEntry {
|
|
|
|
song: "Song1\n\ns1verse1\n\ns1verse2".parse().unwrap(),
|
|
|
|
plan_name: None,
|
|
|
|
},
|
|
|
|
PlaylistEntry {
|
|
|
|
song: "Song2\n\ns2verse1\n\ns2verse2".parse().unwrap(),
|
|
|
|
plan_name: None,
|
|
|
|
},
|
|
|
|
])
|
2024-07-07 22:44:39 -05:00
|
|
|
}
|
|
|
|
|
2024-07-08 22:58:15 -05:00
|
|
|
#[test]
|
|
|
|
fn displays_no_verse_when_empty() {
|
|
|
|
let playlist: VecDeque<PlaylistEntry> = VecDeque::from(vec![]);
|
|
|
|
let display = default(playlist);
|
|
|
|
assert_eq!(display.verse(), None)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn displays_the_first_verse_when_a_song_is_there() {
|
|
|
|
let display = default(two_song_playlist());
|
|
|
|
assert_eq!(display.verse().unwrap().content, "s1verse1")
|
|
|
|
}
|
2024-07-07 22:44:39 -05:00
|
|
|
}
|