diff --git a/src/model.rs b/src/model.rs index d6f0ea1..f9e9106 100644 --- a/src/model.rs +++ b/src/model.rs @@ -2,6 +2,7 @@ mod song { use std::{ collections::{BTreeMap, VecDeque}, str::FromStr, + sync::OnceLock, }; use regex::Regex; @@ -19,7 +20,7 @@ mod song { } /// Sequence of verse names. - pub type Plan = Vec; + pub type Plan = VecDeque; pub struct Song { pub name: String, @@ -29,7 +30,7 @@ mod song { } impl Song { - pub fn plan(&self, plan_name: Option) -> &Vec { + pub fn plan(&self, plan_name: Option) -> &VecDeque { plan_name .map(|plan_name| { self.other_plans @@ -41,11 +42,15 @@ mod song { } #[derive(Debug)] - pub enum SongParseError { - EmptyString, + pub struct SourceRef { + line_number: usize, } - const REGEX_TWO_NEWLINES_WITH_ARBITRARY_SURROUNDING_WHITESPACE: &str = r"\s*[\n\r]\s*[\n\r]\s*"; + #[derive(Debug)] + pub enum SongParseError { + EmptyString, + InvalidMetadata(SourceRef), + } impl FromStr for Song { type Err = SongParseError; @@ -55,7 +60,8 @@ mod song { return Err(SongParseError::EmptyString); } - let re = Regex::new(REGEX_TWO_NEWLINES_WITH_ARBITRARY_SURROUNDING_WHITESPACE).unwrap(); + static HUNK_REGEX: OnceLock = OnceLock::new(); + let re = HUNK_REGEX.get_or_init(|| Regex::new(r"\s*[\n\r]\s*[\n\r]\s*").unwrap()); let mut hunks = VecDeque::new(); let mut last_end: usize = 0; @@ -68,14 +74,25 @@ mod song { // process header let mut header_lines = hunks.pop_front().unwrap().lines(); let name = header_lines.next().unwrap().trim().to_owned(); + let mut other_plans = BTreeMap::new(); - for line in header_lines { + for (line_number, line) in header_lines.enumerate() { + if line.starts_with("map(") { + match line.find(":") { + Some(i) => {} + None => { + return Err(SongParseError::InvalidMetadata(SourceRef { line_number })) + } + } + } // map(band2): slide1, slide2 // band2: slide1, slide2 } let mut verses = BTreeMap::new(); + let mut default_plan = Plan::new(); + // process verses for hunk in hunks { let mut verse_contents = hunk; @@ -86,14 +103,15 @@ mod song { } else { format!("Generated Verse {}", verses.len() + 1).to_owned() }; - verses.insert(verse_name, Verse::new(verse_contents.to_owned())); + verses.insert(verse_name.clone(), Verse::new(verse_contents.to_owned())); + default_plan.push_back(verse_name.clone()); } Ok(Self { name, verses, - other_plans: BTreeMap::new(), - default_plan: vec![], + other_plans, + default_plan, }) } } @@ -117,8 +135,8 @@ mod song { }) ); assert_eq!(song.verses.len(), 1); - assert_eq!(song.default_plan.len(), 1); assert_eq!(song.default_plan[0], "Generated Verse 1"); + assert_eq!(song.default_plan.len(), 1); } } }