From b19f5dd31ee893d4eaef852557cf084c8b8b882d Mon Sep 17 00:00:00 2001 From: Daniel Flanagan Date: Mon, 8 Jul 2024 22:58:15 -0500 Subject: [PATCH] Work on the display functionality --- src/model/display.rs | 104 ++++++++++++++++++++++++++++++++++++++++--- src/model/song.rs | 7 ++- 2 files changed, 100 insertions(+), 11 deletions(-) diff --git a/src/model/display.rs b/src/model/display.rs index 7fa7e6e..a5366db 100644 --- a/src/model/display.rs +++ b/src/model/display.rs @@ -1,18 +1,19 @@ +use std::collections::VecDeque; + use super::song::{Plan, Song, Verse}; pub struct PlaylistEntry { pub song: Song, - pub plan_name: String, + pub plan_name: Option, } pub struct PlaylistVerseRef { pub song_index: usize, - pub song_map: String, pub map_verse_index: usize, } pub struct Display { - pub playlist: Vec, + pub playlist: VecDeque, current: PlaylistVerseRef, frozen_at: Option, pub blanked: bool, @@ -27,13 +28,102 @@ impl Display { if self.blanked { None } 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 { - let vref = self.verse_ref(); - self.playlist.get(vref.song_index) + pub fn entry(&self, verse_ref: &PlaylistVerseRef) -> Option<&PlaylistEntry> { + self.playlist.get(verse_ref.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) -> Display { + let current = PlaylistVerseRef { + song_index: 0, + map_verse_index: 0, + }; + + Display { + playlist, + current, + frozen_at: None, + blanked: false, + } + } + + fn two_song_playlist() -> VecDeque { + 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 = 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") + } } diff --git a/src/model/song.rs b/src/model/song.rs index 58c26f1..7de430d 100644 --- a/src/model/song.rs +++ b/src/model/song.rs @@ -29,11 +29,11 @@ pub struct Song { } impl Song { - pub fn plan(&self, plan_name: Option) -> &VecDeque { + pub fn plan(&self, plan_name: Option<&str>) -> &VecDeque { plan_name .map(|plan_name| { self.other_plans - .get(&plan_name) + .get(plan_name) .unwrap_or(&self.default_plan) }) .unwrap_or(&self.default_plan) @@ -144,10 +144,9 @@ impl FromStr for Song { } mod test { + use super::*; use std::collections::VecDeque; - use super::{Song, Verse}; - #[test] fn parses_simple_song() { let song: Song = r#"Song Title