some bare minimum trait work for songs
This commit is contained in:
parent
c8bb484a53
commit
de34c6818a
6 changed files with 236 additions and 26 deletions
|
@ -61,6 +61,7 @@ mkShell rec {
|
|||
rustfmt
|
||||
rust-analyzer
|
||||
sqlx-cli
|
||||
cargo-watch
|
||||
corrosion
|
||||
];
|
||||
|
||||
|
|
|
@ -5,3 +5,4 @@ pub mod songs;
|
|||
pub mod videos;
|
||||
pub mod presentations;
|
||||
pub mod images;
|
||||
pub mod model;
|
||||
|
|
37
src/rust/core/model.rs
Normal file
37
src/rust/core/model.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use color_eyre::eyre::Result;
|
||||
use sqlx::{Connection, SqliteConnection};
|
||||
|
||||
use crate::songs::Song;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Model<T> {
|
||||
pub items: Vec<T>,
|
||||
pub db: SqliteConnection,
|
||||
}
|
||||
|
||||
pub trait Models {
|
||||
type Item;
|
||||
|
||||
fn setup_db() -> SqliteConnection {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
let mut data = dirs::data_local_dir().unwrap();
|
||||
data.push("lumina");
|
||||
data.push("library-db.sqlite3");
|
||||
let mut db_url = String::from("sqlite://");
|
||||
db_url.push_str(data.to_str().unwrap());
|
||||
rt.block_on(async {
|
||||
SqliteConnection::connect(&db_url).await.expect("problems")
|
||||
})
|
||||
}
|
||||
|
||||
fn add_item(&mut self, item: Self::Item) -> Result<()>;
|
||||
fn add_to_db(&mut self, item: Self::Item) -> Result<()>;
|
||||
fn update_item(&mut self, item: Self::Item, index: i32) -> Result<()>;
|
||||
fn remove_item(&mut self, index: i32) -> Result<()>;
|
||||
fn get_item(&self, index: i32) -> Option<&Self::Item>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
}
|
|
@ -18,6 +18,23 @@ pub enum TextAlignment {
|
|||
BottomRight,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum Background {
|
||||
#[default]
|
||||
Image,
|
||||
Video
|
||||
}
|
||||
|
||||
impl From<String> for Background {
|
||||
fn from(value: String) -> Self {
|
||||
if value == "image" {
|
||||
Background::Image
|
||||
} else {
|
||||
Background::Video
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
struct Slide {
|
||||
id: i32,
|
||||
|
@ -82,12 +99,12 @@ impl From<Presentation> for Slide {
|
|||
}
|
||||
}
|
||||
|
||||
impl Slide {
|
||||
pub fn slides_from_song(song: Song) -> Result<Vec<Self>> {
|
||||
impl SlideModel {
|
||||
pub fn add_song_to_end(&mut self,song: Song) -> Result<()> {
|
||||
let lyrics = song.get_lyrics()?;
|
||||
Ok(lyrics.iter().map(|lyric| {
|
||||
Self {
|
||||
background: song.background.clone().into(),
|
||||
let mut slides: Vec<Slide> = lyrics.iter().map(|lyric| {
|
||||
Slide {
|
||||
background: song.background.clone(),
|
||||
text: lyric.to_owned(),
|
||||
font: song.font.clone(),
|
||||
font_size: song.font_size,
|
||||
|
@ -95,7 +112,14 @@ impl Slide {
|
|||
text_alignment: song.text_alignment,
|
||||
..Default::default()
|
||||
}
|
||||
}).collect())
|
||||
}).collect();
|
||||
self.slides.append(&mut slides);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_video_to_end(&mut self, video: Video) -> Result<()> {
|
||||
self.slides.push(Slide::from(video));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, mem::replace, path::PathBuf};
|
||||
|
||||
use color_eyre::eyre::Result;
|
||||
use color_eyre::eyre::{eyre, Report, Result};
|
||||
use sqlx::{query, Connection, SqliteConnection};
|
||||
use tracing::{debug, error};
|
||||
|
||||
use crate::slides::TextAlignment;
|
||||
|
||||
use crate::{model::{Model, Models}, slides::{Background, TextAlignment}};
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct Song {
|
||||
|
@ -12,18 +12,16 @@ pub struct Song {
|
|||
pub lyrics: String,
|
||||
pub author: String,
|
||||
pub ccli: String,
|
||||
pub audio: String,
|
||||
pub verse_order: String,
|
||||
pub background: String,
|
||||
pub background_type: String,
|
||||
pub audio: PathBuf,
|
||||
pub verse_order: Vec<String>,
|
||||
pub background: PathBuf,
|
||||
pub background_type: Background,
|
||||
pub text_alignment: TextAlignment,
|
||||
pub horizontal_text_alignment: String,
|
||||
pub vertical_text_alignment: String,
|
||||
pub font: String,
|
||||
pub font_size: i32,
|
||||
}
|
||||
|
||||
const KEYWORDS: [&'static str; 24] = [
|
||||
const VERSE_KEYWORDS: [&'static str; 24] = [
|
||||
"Verse 1", "Verse 2", "Verse 3", "Verse 4",
|
||||
"Verse 5", "Verse 6", "Verse 7", "Verse 8",
|
||||
"Chorus 1", "Chorus 2", "Chorus 3", "Chorus 4",
|
||||
|
@ -32,19 +30,118 @@ const KEYWORDS: [&'static str; 24] = [
|
|||
"Other 1", "Other 2", "Other 3", "Other 4",
|
||||
];
|
||||
|
||||
impl Models for Model<Song> {
|
||||
type Item = Song;
|
||||
fn add_item(&mut self, item: Self::Item) -> Result<()> {
|
||||
self.items.push(item);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_to_db(&mut self, item: Self::Item) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn update_item(&mut self, item: Self::Item, index: i32) -> Result<()> {
|
||||
if let Some(current_song) = self.items.get_mut(index as usize) {
|
||||
let _old_song = replace(current_song, item);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(eyre!("Song doesn't exist in model. Id was {}", index))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_item(&self, index: i32) -> Option<&Self::Item> {
|
||||
self.items.get(index as usize)
|
||||
}
|
||||
|
||||
fn remove_item(&mut self, index: i32) -> Result<()> {
|
||||
self.items.remove(index as usize);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Model<Song> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
items: vec![],
|
||||
db: {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
let mut data = dirs::data_local_dir().unwrap();
|
||||
data.push("lumina");
|
||||
data.push("library-db.sqlite3");
|
||||
let mut db_url = String::from("sqlite://");
|
||||
db_url.push_str(data.to_str().unwrap());
|
||||
rt.block_on(async {
|
||||
SqliteConnection::connect(&db_url).await.expect("problems")
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Model<Song> {
|
||||
pub fn get_song(&self, id: i32) -> Option<&Song> {
|
||||
self.items.get(id as usize)
|
||||
}
|
||||
|
||||
pub fn remove_song(&mut self, id: i32) -> Result<(), Report> {
|
||||
if let Some(_song) = self.items.get(id as usize) {
|
||||
let _song = self.items.remove(id as usize);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(eyre!("Song doesn't exist in model. Id was {}", id))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_from_db(&mut self) {
|
||||
// static DATABASE_URL: &str = "sqlite:///home/chris/.local/share/lumina/library-db.sqlite3";
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
let result = query!(r#"SELECT vorder as "verse_order!", fontSize as "font_size!: i32", backgroundType as "background_type!", horizontalTextAlignment as "horizontal_text_alignment!", verticalTextAlignment as "vertical_text_alignment!", title as "title!", font as "font!", background as "background!", lyrics as "lyrics!", ccli as "ccli!", author as "author!", audio as "audio!", id as "id: i32" from songs"#).fetch_all(&mut self.db).await;
|
||||
match result {
|
||||
Ok(s) => {
|
||||
for song in s.into_iter() {
|
||||
self.add_song(Song {
|
||||
title: song.title,
|
||||
lyrics: song.lyrics,
|
||||
author: song.author,
|
||||
ccli: song.ccli,
|
||||
audio: song.audio.into(),
|
||||
verse_order: song.verse_order.split(" ").map(|s| s.to_string()).collect(),
|
||||
background: song.background.into(),
|
||||
background_type: song.background_type.into(),
|
||||
text_alignment: {
|
||||
if song.horizontal_text_alignment == "center" && song.vertical_text_alignment == "center" {
|
||||
TextAlignment::MiddleCenter
|
||||
} else {
|
||||
TextAlignment::TopCenter
|
||||
}
|
||||
},
|
||||
font: song.font,
|
||||
font_size: song.font_size,
|
||||
});
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("There was an error in converting songs: {e}");
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Song {
|
||||
pub fn get_lyrics(&self) -> Result<Vec<String>> {
|
||||
let mut lyric_list = Vec::new();
|
||||
let raw_lyrics = self.lyrics.as_str();
|
||||
let vorder: Vec<&str> =
|
||||
self.verse_order.split(' ').collect();
|
||||
let _first_item = true;
|
||||
let verse_order =
|
||||
self.verse_order.clone();
|
||||
|
||||
let mut lyric_map = HashMap::new();
|
||||
let mut verse_title = String::from("");
|
||||
let mut lyric = String::from("");
|
||||
for (i, line) in raw_lyrics.split('\n').enumerate() {
|
||||
if KEYWORDS.contains(&line) {
|
||||
if VERSE_KEYWORDS.contains(&line) {
|
||||
if i != 0 {
|
||||
lyric_map.insert(verse_title, lyric);
|
||||
lyric = String::from("");
|
||||
|
@ -59,10 +156,10 @@ impl Song {
|
|||
}
|
||||
lyric_map.insert(verse_title, lyric);
|
||||
|
||||
for verse in vorder {
|
||||
for verse in verse_order {
|
||||
let mut verse_name = "";
|
||||
debug!(verse = verse);
|
||||
for word in KEYWORDS.clone() {
|
||||
for word in VERSE_KEYWORDS.clone() {
|
||||
let end_verse =
|
||||
verse.get(1..2).unwrap_or_default();
|
||||
let beg_verse =
|
||||
|
@ -98,6 +195,10 @@ impl Song {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
@ -162,11 +263,45 @@ Ending 1
|
|||
Oh Oh Oh
|
||||
From the day
|
||||
You saved my soul".to_string();
|
||||
song.verse_order = "O1 V1 C1 C2 O2 V2 C3 C2 O2 B1 C2 C2 E1 O2".to_string();
|
||||
song.verse_order = "O1 V1 C1 C2 O2 V2 C3 C2 O2 B1 C2 C2 E1 O2".to_string().split(' ').map(|s| s.to_string()).collect();
|
||||
let lyrics = song.get_lyrics();
|
||||
match lyrics {
|
||||
Ok(lyrics) => { assert_eq!(vec!["From the Day\nI Am They", "When You found me,\nI was so blind\nMy sin was before me,\nI was swallowed by pride", "But out of the darkness,\nYou brought me to Your light\nYou showed me new mercy\nAnd opened up my eyes", "From the day\nYou saved my soul\n'Til the very moment\nWhen I come home", "I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul", "Where brilliant light\nIs all around\nAnd endless joy\nIs the only sound", "Oh, rest my heart\nForever now\nOh, in Your arms\nI'll always be found", "From the day\nYou saved my soul\n'Til the very moment\nWhen I come home", "I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul", "My love is Yours\nMy heart is Yours\nMy life is Yours\nForever", "My love is Yours\nMy heart is Yours\nMy life is Yours\nForever", "From the day\nYou saved my soul\n'Til the very moment\nWhen I come home", "I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul", "From the day\nYou saved my soul\n'Til the very moment\nWhen I come home", "I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul", "Oh Oh Oh\nFrom the day\nYou saved my soul\n"], lyrics); },
|
||||
Err(e) => { assert!(false) },
|
||||
Err(e) => { assert!(false, "{:?}", e) },
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_db_and_model() {
|
||||
let mut song_model = Model::default();
|
||||
song_model.load_from_db();
|
||||
if let Some(song) = song_model.get_song(3) {
|
||||
let test_song = test_song();
|
||||
assert_eq!(test_song.title, song.title);
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_update() {
|
||||
let song = test_song();
|
||||
let cloned_song = song.clone();
|
||||
let mut song_model = Model::default();
|
||||
song_model.load_from_db();
|
||||
|
||||
match song_model.update_item(song, 2) {
|
||||
Ok(()) => assert_eq!(&cloned_song, song_model.get_song(2).unwrap()),
|
||||
Err(e) => assert!(false, "{:?}", e),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn test_song() -> Song {
|
||||
Song {
|
||||
title: "Death Was Arrested".to_string(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct Video {
|
||||
title: String
|
||||
title: String,
|
||||
path: PathBuf,
|
||||
start_time: f32,
|
||||
end_time: f32,
|
||||
looping: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct VideoModel {
|
||||
videos: Vec<Video>
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue