use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SyllableInfo {
pub text: String,
pub start_time: i32,
pub end_time: i32,
}
impl SyllableInfo {
pub fn new(text: String, start_time: i32, end_time: i32) -> Self {
Self {
text,
start_time,
end_time,
}
}
pub fn duration(&self) -> i32 {
self.end_time - self.start_time
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FullSyllableInfo {
pub sub_items: Vec<SyllableInfo>,
}
impl FullSyllableInfo {
pub fn new(sub_items: Vec<SyllableInfo>) -> Self {
Self { sub_items }
}
pub fn sub_items(&self) -> &[SyllableInfo] {
&self.sub_items
}
pub fn into_sub_items(self) -> Vec<SyllableInfo> {
self.sub_items
}
pub fn sub_items_mut(&mut self) -> &mut Vec<SyllableInfo> {
&mut self.sub_items
}
pub fn set_sub_items(&mut self, sub_items: Vec<SyllableInfo>) {
self.sub_items = sub_items;
}
pub fn extend_sub_items(&mut self, items: impl IntoIterator<Item = SyllableInfo>) {
self.sub_items.extend(items);
}
pub fn text(&self) -> String {
get_text_from_syllable_list(&self.sub_items)
}
pub fn start_time(&self) -> i32 {
self.sub_items.first().map_or(0, |s| s.start_time)
}
pub fn end_time(&self) -> i32 {
self.sub_items.last().map_or(0, |s| s.end_time)
}
pub fn duration(&self) -> i32 {
self.end_time() - self.start_time()
}
}
pub fn get_text_from_syllable_list(syllables: &[SyllableInfo]) -> String {
syllables.iter().map(|s| s.text.as_str()).collect()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SyllableItem {
Syllable(SyllableInfo),
Full(FullSyllableInfo),
}
impl SyllableItem {
pub fn text(&self) -> String {
match self {
Self::Syllable(s) => s.text.clone(),
Self::Full(f) => f.text(),
}
}
pub fn start_time(&self) -> i32 {
match self {
Self::Syllable(s) => s.start_time,
Self::Full(f) => f.start_time(),
}
}
pub fn end_time(&self) -> i32 {
match self {
Self::Syllable(s) => s.end_time,
Self::Full(f) => f.end_time(),
}
}
pub fn duration(&self) -> i32 {
self.end_time() - self.start_time()
}
pub fn is_full(&self) -> bool {
matches!(self, Self::Full(_))
}
pub fn parts(&self) -> &[SyllableInfo] {
match self {
Self::Syllable(syllable) => std::slice::from_ref(syllable),
Self::Full(full) => full.sub_items(),
}
}
pub fn as_syllable(&self) -> Option<&SyllableInfo> {
match self {
Self::Syllable(s) => Some(s),
Self::Full(_) => None,
}
}
pub fn as_syllable_mut(&mut self) -> Option<&mut SyllableInfo> {
match self {
Self::Syllable(s) => Some(s),
Self::Full(_) => None,
}
}
pub fn as_full(&self) -> Option<&FullSyllableInfo> {
match self {
Self::Syllable(_) => None,
Self::Full(f) => Some(f),
}
}
pub fn as_full_mut(&mut self) -> Option<&mut FullSyllableInfo> {
match self {
Self::Syllable(_) => None,
Self::Full(f) => Some(f),
}
}
}
impl From<SyllableInfo> for SyllableItem {
fn from(value: SyllableInfo) -> Self {
Self::Syllable(value)
}
}
impl From<FullSyllableInfo> for SyllableItem {
fn from(value: FullSyllableInfo) -> Self {
Self::Full(value)
}
}
pub fn get_text_from_syllable_items(syllables: &[SyllableItem]) -> String {
let mut text = String::new();
for item in syllables {
for part in item.parts() {
text.push_str(&part.text);
}
}
text
}
pub fn to_syllable_items(syllables: impl IntoIterator<Item = SyllableInfo>) -> Vec<SyllableItem> {
syllables.into_iter().map(SyllableItem::from).collect()
}
pub fn flatten_syllable_items(syllables: &[SyllableItem]) -> Vec<SyllableInfo> {
let mut items = Vec::with_capacity(syllables.len());
for syllable in syllables {
items.extend_from_slice(syllable.parts());
}
items
}
pub fn add_offset_to_syllable_items(syllables: &mut [SyllableItem], offset: i32) {
for syllable in syllables.iter_mut() {
match syllable {
SyllableItem::Syllable(s) => {
s.start_time -= offset;
s.end_time -= offset;
}
SyllableItem::Full(f) => {
for sub in f.sub_items_mut() {
sub.start_time -= offset;
sub.end_time -= offset;
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::{LineInfo, LyricsData};
#[test]
fn model_is_send_and_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<SyllableInfo>();
assert_send_sync::<FullSyllableInfo>();
assert_send_sync::<SyllableItem>();
assert_send_sync::<LineInfo>();
assert_send_sync::<LyricsData>();
}
#[test]
fn aggregates_follow_sub_items() {
let mut full = FullSyllableInfo::new(vec![
SyllableInfo::new("He".to_string(), 0, 100),
SyllableInfo::new("llo".to_string(), 100, 300),
]);
assert_eq!(full.text(), "Hello");
assert_eq!(
(full.start_time(), full.end_time(), full.duration()),
(0, 300, 300)
);
full.sub_items_mut()
.push(SyllableInfo::new("!".to_string(), 300, 350));
assert_eq!(full.text(), "Hello!");
assert_eq!(
(full.start_time(), full.end_time(), full.duration()),
(0, 350, 350)
);
let empty = FullSyllableInfo::new(Vec::new());
assert_eq!(empty.text(), "");
assert_eq!((empty.start_time(), empty.end_time()), (0, 0));
}
}