use serde::{Deserialize, Serialize}; use crate::Chord; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ChordPosition { pub offset: usize, pub chord: Chord, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LyricLine { pub text: String, pub chords: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum SectionKind { Verse, Chorus, Bridge, PreChorus, Intro, Outro, Break, Tab, Other(String), } impl SectionKind { pub fn from_label(s: &str) -> Self { match s.to_lowercase().replace(['-', '_', ' '], "").as_str() { "verse" => Self::Verse, "chorus" => Self::Chorus, "bridge" => Self::Bridge, "prechorus" => Self::PreChorus, "intro" => Self::Intro, "outro" => Self::Outro, "break" => Self::Break, "tab" => Self::Tab, _ => Self::Other(s.to_string()), } } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Section { pub kind: SectionKind, pub label: Option, pub lines: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SongMeta { pub title: String, pub artist: String, pub capo: Option, pub original_key: Option, pub tuning: Option, pub tempo: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Song { pub meta: SongMeta, pub sections: Vec
, } #[cfg(test)] mod tests { use super::*; use crate::{Chord, Note}; #[test] fn lyric_line_chord_positions() { let line = LyricLine { text: "A drop in the ocean".into(), chords: vec![ ChordPosition { offset: 0, chord: Chord { root: Note::E, descriptor: Some("m".into()) } }, ChordPosition { offset: 8, chord: Chord { root: Note::C, descriptor: None } }, ], }; assert_eq!(line.chords[0].offset, 0); assert_eq!(line.chords[1].offset, 8); } #[test] fn section_kind_from_label() { assert_eq!(SectionKind::from_label("Chorus"), SectionKind::Chorus); assert_eq!(SectionKind::from_label("Pre-Chorus"), SectionKind::PreChorus); assert_eq!(SectionKind::from_label("Tab"), SectionKind::Tab); assert_eq!(SectionKind::from_label("Riff"), SectionKind::Other("Riff".into())); } }