use crate::utils::FromStaticStr; #[derive(Debug)] pub enum Stanza { Entry(&'static str), Defn(&'static str), Note(&'static str), Synonym(&'static str), Bullet(&'static str), SubBullet(&'static str), } impl Stanza { fn is_entry(s: &str) -> bool { s.chars() .all(|c| c.is_uppercase() || c.is_ascii_whitespace() || "-;'.".contains(c)) } fn is_defn(s: &str) -> bool { s.starts_with("Defn") } fn is_note(s: &str) -> bool { s.starts_with("Note") } fn is_synonym(s: &str) -> bool { s.starts_with("Syn") } fn is_bullet(s: &str) -> bool { s.find('.') .map(|idx| s[..idx].chars().all(char::is_numeric)) .unwrap_or_default() } fn is_sub_bullet(s: &str) -> bool { let mut chars = s.chars(); chars.next().map(|c| c == '(').unwrap_or_default() && chars.next().map(char::is_alphabetic).unwrap_or_default() && chars.next().map(|c| c == ')').unwrap_or_default() } } pub struct StanzaLexError { pub data: String, } impl FromStaticStr for Stanza { type Err = StanzaLexError; fn from_str(s: &'static str) -> Result { let mut lines = s.split("\n"); if let Some(first_line) = lines.next() { if !first_line.is_empty() { if Stanza::is_entry(first_line) { Ok(Self::Entry(first_line.trim())) } else if Stanza::is_defn(first_line) { Ok(Self::Defn(s.strip_prefix("Defn: ").unwrap_or(s).trim())) } else if Stanza::is_note(first_line) { Ok(Self::Note(s.strip_prefix("Note: ").unwrap_or(s).trim())) } else if Stanza::is_synonym(first_line) { Ok(Self::Synonym(s.strip_prefix("Syn.").unwrap_or(s))) } else if Stanza::is_bullet(first_line) { Ok(Self::Defn( s.trim_start_matches(|c| "0123456789. ".contains(c)), )) // } else if Stanza::is_sub_bullet(first_line) { // Ok(Self::SubBullet(s)) } else { Err(Self::Err { data: format!("weird stanza: {}", s), }) } } else { Err(Self::Err { data: format!("empty first line: {}", s), }) } } else { Err(Self::Err { data: format!("empty stanza: {}", s), }) } } } pub fn lex(src: &'static str) -> impl Iterator> { src.split("\n\n").map(Stanza::from_str) }