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};
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue