use std::{
convert::Infallible,
ops::{Deref, DerefMut},
str::FromStr,
sync::LazyLock,
};
use regex::Regex;
use serde::{Deserialize, Serialize};
use crate::lyrics::{
LyricFormat,
synced_lyrics::{LyricParseError, SyncedLyrics},
};
static SYNCED_CHECK_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"\[\d+:\d+\.\d+\]").unwrap());
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum LyricData {
Instrumental,
Plain(PlainLyrics),
Synced(SyncedLyrics),
}
impl LyricData {
pub fn infer_from_string(string: impl AsRef<str>) -> Result<Self, LyricParseError> {
let string = string.as_ref();
if string.is_empty() || string == "[au:instrumental]" {
return Ok(Self::Instrumental);
}
let lyrics = if SYNCED_CHECK_REGEX.is_match(string) {
LyricData::Synced(SyncedLyrics::from_synced_lyrics(string)?)
} else {
LyricData::Plain(PlainLyrics::from_str(string).unwrap())
};
Ok(lyrics)
}
#[must_use]
pub fn has_lyrics(&self) -> bool {
match self {
LyricData::Instrumental => false,
LyricData::Plain(_) => true,
LyricData::Synced(_) => true,
}
}
#[must_use]
pub fn instrumental(&self) -> bool {
match self {
LyricData::Instrumental => true,
LyricData::Plain(_) => false,
LyricData::Synced(_) => false,
}
}
#[must_use]
pub fn to_lyrics(&self) -> Option<String> {
match self {
LyricData::Instrumental => None,
LyricData::Plain(plain_lyrics) => Some(plain_lyrics.to_string()),
LyricData::Synced(synced_lyrics) => {
Some(synced_lyrics.to_lyrics(LyricFormat::Lrc { a2: false }))
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct PlainLyrics {
lyrics: String,
}
impl Deref for PlainLyrics {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.lyrics
}
}
impl DerefMut for PlainLyrics {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.lyrics
}
}
impl FromStr for PlainLyrics {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self {
lyrics: s.to_owned(),
})
}
}