This commit is contained in:
Daniel Flanagan 2024-07-06 09:43:07 -05:00
parent 3dcfcb88a0
commit 3803df7221
4 changed files with 131 additions and 2 deletions

5
Cargo.lock generated
View file

@ -1036,6 +1036,7 @@ dependencies = [
"notify",
"pathdiff",
"redact",
"regex",
"serde",
"sled",
"thiserror",
@ -1512,9 +1513,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.4"
version = "1.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
dependencies = [
"aho-corasick",
"memchr",

View file

@ -31,6 +31,7 @@ maud = "0.26.0"
notify = "6.1.1"
pathdiff = "0.2.1"
redact = { version = "0.1.10", features = ["serde"] }
regex = { version = "1.10.5" }
serde = "1.0.201"
sled = { version = "0.34.7", features = [] }
thiserror = "1.0.60"

View file

@ -2,6 +2,7 @@ mod auth;
mod cli;
mod db;
mod file_watcher;
mod model;
mod observe;
mod partials;
mod prelude;

126
src/model.rs Normal file
View file

@ -0,0 +1,126 @@
mod song {
use std::{
collections::{BTreeMap, VecDeque},
str::FromStr,
};
use regex::Regex;
pub struct Verse {
// pub background: String, // url
pub content: String,
}
/// Sequence of verse names.
pub type SongMap = Vec<String>;
pub struct Song {
pub name: String,
pub verses: BTreeMap<String, Verse>,
pub other_maps: BTreeMap<String, SongMap>,
pub default_map: SongMap,
}
impl Song {
pub fn song_map(&self, map_name: Option<String>) -> &Vec<String> {
map_name
.map(|map_name| self.other_maps.get(&map_name).unwrap_or(&self.default_map))
.unwrap_or(&self.default_map)
}
}
#[derive(Debug)]
pub enum SongParseError {
EmptyString,
}
const REGEX_TWO_NEWLINES_WITH_ARBITRARY_SURROUNDING_WHITESPACE: &str = r"\s*[\n\r]\s*[\n\r]\s*";
impl FromStr for Song {
type Err = SongParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "" {
return Err(SongParseError::EmptyString);
}
let re = Regex::new(REGEX_TWO_NEWLINES_WITH_ARBITRARY_SURROUNDING_WHITESPACE).unwrap();
let mut hunks = VecDeque::new();
let mut last_end: usize = 0;
for m in re.find_iter(s) {
hunks.push_back(&s[last_end..m.start()]);
last_end = m.end();
}
hunks.push_back(&s[last_end..s.len() - 1]);
// process header
let mut header_lines = hunks.pop_front().unwrap().lines();
let name = header_lines.next().unwrap().trim().to_owned();
for line in header_lines {
// map(band2): slide1, slide2
// band2: slide1, slide2
}
let mut verses = BTreeMap::new();
// process verses
for hunk in hunks {
let mut verse_contents = hunk;
let first_line = &hunk[0..hunk.find('\n').unwrap_or(hunk.len())];
let verse_name: String = if let Some(i) = first_line.find(':') {
String::from(&first_line[0..i])
} else {
format!("Generated Verse {}", verses.len()).to_owned()
};
verses.insert(verse_name, verse_content);
}
Ok(Self {
name,
verses,
other_maps: BTreeMap::new(),
default_map: vec![],
})
}
}
mod test {
use super::Song;
#[test]
fn parses_simple_song() {
let song: Song = r#"Song Title
A verse"#
.parse()
.unwrap();
assert_eq!(song.name, "Song Title");
assert_eq!(song.verses.len(), 1);
}
}
}
mod display {
use super::song::{Song, SongMap};
pub struct PlaylistEntry {
pub song: Song,
pub map: SongMap,
}
pub struct PlaylistVerseRef {
pub song_index: usize,
pub song_map: String,
pub map_verse_index: usize,
}
pub struct Display {
pub playlist: Vec<(Song, Option<String>)>,
pub current: PlaylistVerseRef,
pub frozen_at: Option<PlaylistVerseRef>,
pub blanked: bool,
}
}