Work on the display functionality
This commit is contained in:
parent
41a66fd32f
commit
b19f5dd31e
2 changed files with 100 additions and 11 deletions
|
@ -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<String>,
|
||||
}
|
||||
|
||||
pub struct PlaylistVerseRef {
|
||||
pub song_index: usize,
|
||||
pub song_map: String,
|
||||
pub map_verse_index: usize,
|
||||
}
|
||||
|
||||
pub struct Display {
|
||||
pub playlist: Vec<PlaylistEntry>,
|
||||
pub playlist: VecDeque<PlaylistEntry>,
|
||||
current: PlaylistVerseRef,
|
||||
frozen_at: Option<PlaylistVerseRef>,
|
||||
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<PlaylistEntry> {
|
||||
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<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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,11 +29,11 @@ pub struct 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
|
||||
.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
|
||||
|
|
Loading…
Reference in a new issue