Work on the display functionality

This commit is contained in:
Daniel Flanagan 2024-07-08 22:58:15 -05:00
parent 41a66fd32f
commit b19f5dd31e
2 changed files with 100 additions and 11 deletions

View file

@ -1,18 +1,19 @@
use std::collections::VecDeque;
use super::song::{Plan, Song, Verse}; use super::song::{Plan, Song, Verse};
pub struct PlaylistEntry { pub struct PlaylistEntry {
pub song: Song, pub song: Song,
pub plan_name: String, pub plan_name: Option<String>,
} }
pub struct PlaylistVerseRef { pub struct PlaylistVerseRef {
pub song_index: usize, pub song_index: usize,
pub song_map: String,
pub map_verse_index: usize, pub map_verse_index: usize,
} }
pub struct Display { pub struct Display {
pub playlist: Vec<PlaylistEntry>, pub playlist: VecDeque<PlaylistEntry>,
current: PlaylistVerseRef, current: PlaylistVerseRef,
frozen_at: Option<PlaylistVerseRef>, frozen_at: Option<PlaylistVerseRef>,
pub blanked: bool, pub blanked: bool,
@ -27,13 +28,102 @@ impl Display {
if self.blanked { if self.blanked {
None None
} else { } else {
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) -> Option<PlaylistEntry> { pub fn entry(&self, verse_ref: &PlaylistVerseRef) -> Option<&PlaylistEntry> {
let vref = self.verse_ref(); self.playlist.get(verse_ref.song_index)
self.playlist.get(vref.song_index)
} }
pub fn plan() -> Plan {} 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,
}
}
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,
},
])
}
#[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")
}
} }

View file

@ -29,11 +29,11 @@ pub struct Song {
} }
impl Song { impl Song {
pub fn plan(&self, plan_name: Option<String>) -> &VecDeque<String> { pub fn plan(&self, plan_name: Option<&str>) -> &VecDeque<String> {
plan_name plan_name
.map(|plan_name| { .map(|plan_name| {
self.other_plans self.other_plans
.get(&plan_name) .get(plan_name)
.unwrap_or(&self.default_plan) .unwrap_or(&self.default_plan)
}) })
.unwrap_or(&self.default_plan) .unwrap_or(&self.default_plan)
@ -144,10 +144,9 @@ impl FromStr for Song {
} }
mod test { mod test {
use super::*;
use std::collections::VecDeque; use std::collections::VecDeque;
use super::{Song, Verse};
#[test] #[test]
fn parses_simple_song() { fn parses_simple_song() {
let song: Song = r#"Song Title let song: Song = r#"Song Title