updating the core for actual builds and working tests

This commit is contained in:
Chris Cochrun 2024-11-12 13:10:12 -06:00
parent e82a9c161b
commit a94ad65914
10 changed files with 219 additions and 86 deletions

View file

@ -1,11 +1,13 @@
use super::model::Model;
use miette::{Result, miette, IntoDiagnostic};
use miette::{miette, IntoDiagnostic, Result};
use serde::{Deserialize, Serialize};
use sqlx::{query_as, SqliteConnection};
use std::path::PathBuf;
use tracing::error;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[derive(
Clone, Debug, Default, PartialEq, Serialize, Deserialize,
)]
pub struct Image {
pub id: i32,
pub title: String,
@ -26,12 +28,17 @@ impl Model<Image> {
let _ = self.add_item(image);
}
}
Err(e) => error!("There was an error in converting images: {e}"),
Err(e) => {
error!("There was an error in converting images: {e}")
}
};
}
}
pub async fn get_image_from_db(database_id: i32, db: &mut SqliteConnection) -> Result<Image> {
pub async fn get_image_from_db(
database_id: i32,
db: &mut SqliteConnection,
) -> Result<Image> {
Ok(query_as!(Image, r#"SELECT title as "title!", file_path as "path!", id as "id: i32" from images where id = ?"#, database_id).fetch_one(db).await.into_diagnostic()?)
}
@ -52,7 +59,7 @@ mod test {
pub async fn test_db_and_model() {
let mut image_model: Model<Image> = Model {
items: vec![],
db: crate::core::model::get_db().await
db: crate::core::model::get_db().await,
};
image_model.load_from_db().await;
dbg!(&image_model.items);
@ -70,16 +77,26 @@ mod test {
let image = test_image("A new image".into());
let mut image_model: Model<Image> = Model {
items: vec![],
db: crate::core::model::get_db().await
db: crate::core::model::get_db().await,
};
let result = image_model.add_item(image.clone());
let new_image = test_image("A newer image".into());
match result {
Ok(_) => {
assert_eq!(&image, image_model.find(|i| i.id == 0).unwrap());
assert_ne!(&new_image, image_model.find(|i| i.id == 0).unwrap());
assert_eq!(
&image,
image_model.find(|i| i.id == 0).unwrap()
);
assert_ne!(
&new_image,
image_model.find(|i| i.id == 0).unwrap()
);
}
Err(e) => assert!(false, "There was an error adding the image: {:?}", e),
Err(e) => assert!(
false,
"There was an error adding the image: {:?}",
e
),
}
}
}

View file

@ -4,7 +4,9 @@ use serde::{Deserialize, Serialize};
use super::presentations::PresKind;
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub enum ServiceItemKind {
#[default]
Song,
@ -22,7 +24,9 @@ impl std::fmt::Display for ServiceItemKind {
Self::Video => "video".to_owned(),
Self::Presentation(PresKind::Html) => "html".to_owned(),
Self::Presentation(PresKind::Pdf) => "pdf".to_owned(),
Self::Presentation(PresKind::Generic) => "presentation".to_owned(),
Self::Presentation(PresKind::Generic) => {
"presentation".to_owned()
}
Self::Content => "content".to_owned(),
};
write!(f, "{s}")
@ -36,7 +40,9 @@ impl TryFrom<String> for ServiceItemKind {
"song" => Ok(Self::Song),
"image" => Ok(Self::Image),
"video" => Ok(Self::Video),
"presentation" => Ok(Self::Presentation(PresKind::Generic)),
"presentation" => {
Ok(Self::Presentation(PresKind::Generic))
}
"html" => Ok(Self::Presentation(PresKind::Html)),
"pdf" => Ok(Self::Presentation(PresKind::Pdf)),
"content" => Ok(Self::Content),
@ -51,7 +57,9 @@ impl From<ServiceItemKind> for String {
ServiceItemKind::Song => "song".to_owned(),
ServiceItemKind::Video => "video".to_owned(),
ServiceItemKind::Image => "image".to_owned(),
ServiceItemKind::Presentation(_) => "presentation".to_owned(),
ServiceItemKind::Presentation(_) => {
"presentation".to_owned()
}
ServiceItemKind::Content => "content".to_owned(),
}
}
@ -65,7 +73,10 @@ pub enum ParseError {
impl Error for ParseError {}
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
let message = match self {
Self::UnknownType => "The type does not exist. It needs to be one of 'song', 'video', 'image', 'presentation', or 'content'",
};

View file

@ -142,9 +142,11 @@ mod test {
#[test]
fn test_list() {
let lisp = read_to_string("./test_presentation.lisp").expect("oops");
let lisp =
read_to_string("./test_presentation.lisp").expect("oops");
println!("{lisp}");
let mut parser = Parser::from_str_custom(&lisp, Options::elisp());
let mut parser =
Parser::from_str_custom(&lisp, Options::elisp());
for atom in parser.value_iter() {
match atom {
Ok(atom) => {

View file

@ -1,9 +1,9 @@
pub mod images;
pub mod kinds;
pub mod lisp;
pub mod model;
pub mod presentations;
pub mod service_items;
pub mod slide;
pub mod songs;
pub mod videos;
pub mod model;

View file

@ -71,9 +71,7 @@ pub async fn get_db() -> SqliteConnection {
data.push("library-db.sqlite3");
let mut db_url = String::from("sqlite://");
db_url.push_str(data.to_str().unwrap());
SqliteConnection::connect(&db_url)
.await
.expect("problems")
SqliteConnection::connect(&db_url).await.expect("problems")
}
pub trait Modeling {

View file

@ -1,12 +1,16 @@
use miette::{miette, Result, IntoDiagnostic};
use miette::{miette, IntoDiagnostic, Result};
use serde::{Deserialize, Serialize};
use sqlx::{prelude::FromRow, query, sqlite::SqliteRow, Row, SqliteConnection};
use sqlx::{
prelude::FromRow, query, sqlite::SqliteRow, Row, SqliteConnection,
};
use std::path::PathBuf;
use tracing::error;
use super::model::Model;
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub enum PresKind {
Html,
#[default]
@ -14,7 +18,9 @@ pub enum PresKind {
Generic,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub struct Presentation {
pub id: i32,
pub title: String,
@ -75,7 +81,9 @@ impl Model<Presentation> {
});
}
}
Err(e) => error!("There was an error in converting presentations: {e}"),
Err(e) => error!(
"There was an error in converting presentations: {e}"
),
}
}
}
@ -114,10 +122,12 @@ mod test {
async fn test_db_and_model() {
let mut presentation_model: Model<Presentation> = Model {
items: vec![],
db: crate::core::model::get_db().await
db: crate::core::model::get_db().await,
};
presentation_model.load_from_db().await;
if let Some(presentation) = presentation_model.find(|p| p.id == 54) {
if let Some(presentation) =
presentation_model.find(|p| p.id == 54)
{
let test_presentation = test_presentation();
assert_eq!(&test_presentation, presentation);
} else {

View file

@ -52,7 +52,9 @@ impl From<&Image> for ServiceItem {
impl From<&Presentation> for ServiceItem {
fn from(presentation: &Presentation) -> Self {
Self {
kind: ServiceItemKind::Presentation(presentation.kind.clone()),
kind: ServiceItemKind::Presentation(
presentation.kind.clone(),
),
database_id: presentation.id,
..Default::default()
}
@ -60,7 +62,10 @@ impl From<&Presentation> for ServiceItem {
}
impl ServiceItemModel {
fn add_item(&mut self, item: impl Into<ServiceItem>) -> Result<()> {
fn add_item(
&mut self,
item: impl Into<ServiceItem>,
) -> Result<()> {
let service_item: ServiceItem = item.into();
self.items.push(service_item);
Ok(())
@ -103,7 +108,10 @@ mod test {
let mut service_model = ServiceItemModel::default();
match service_model.add_item(&song) {
Ok(_) => {
assert_eq!(ServiceItemKind::Song, service_model.items[0].kind);
assert_eq!(
ServiceItemKind::Song,
service_model.items[0].kind
);
assert_eq!(
ServiceItemKind::Presentation(PresKind::Html),
pres_item.kind

View file

@ -15,7 +15,9 @@ use crate::core::lisp::Symbol;
use super::lisp::get_lists;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub enum TextAlignment {
TopLeft,
TopCenter,
@ -39,7 +41,9 @@ impl From<Value> for TextAlignment {
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub struct Background {
pub path: PathBuf,
pub kind: BackgroundKind,
@ -113,16 +117,23 @@ pub enum ParseError {
impl std::error::Error for ParseError {}
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
let message = match self {
Self::NonBackgroundFile => "The file is not a recognized image or video type",
Self::NonBackgroundFile => {
"The file is not a recognized image or video type"
}
Self::DoesNotExist => "This file doesn't exist",
};
write!(f, "Error: {message}")
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub enum BackgroundKind {
#[default]
Image,
@ -139,7 +150,9 @@ impl From<String> for BackgroundKind {
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[derive(
Clone, Debug, Default, PartialEq, Serialize, Deserialize,
)]
pub struct Slide {
id: i32,
background: Background,
@ -170,7 +183,9 @@ impl Slide {
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[derive(
Clone, Debug, Default, PartialEq, Serialize, Deserialize,
)]
pub struct SlideBuilder {
background: Option<Background>,
text: Option<String>,
@ -187,7 +202,10 @@ impl SlideBuilder {
Self::default()
}
pub(crate) fn background(mut self, background: PathBuf) -> Result<Self, ParseError> {
pub(crate) fn background(
mut self,
background: PathBuf,
) -> Result<Self, ParseError> {
let background = Background::try_from(background)?;
let _ = self.background.insert(background);
Ok(self)
@ -208,7 +226,10 @@ impl SlideBuilder {
self
}
pub(crate) fn text_alignment(mut self, text_alignment: TextAlignment) -> Self {
pub(crate) fn text_alignment(
mut self,
text_alignment: TextAlignment,
) -> Self {
let _ = self.text_alignment.insert(text_alignment);
self
}
@ -218,12 +239,18 @@ impl SlideBuilder {
self
}
pub(crate) fn video_start_time(mut self, video_start_time: f32) -> Self {
pub(crate) fn video_start_time(
mut self,
video_start_time: f32,
) -> Self {
let _ = self.video_start_time.insert(video_start_time);
self
}
pub(crate) fn video_end_time(mut self, video_end_time: f32) -> Self {
pub(crate) fn video_end_time(
mut self,
video_end_time: f32,
) -> Self {
let _ = self.video_end_time.insert(video_end_time);
self
}
@ -281,7 +308,11 @@ impl Image {
}
}
fn build_image_bg(atom: &Value, image_map: &mut HashMap<String, String>, map_index: usize) {
fn build_image_bg(
atom: &Value,
image_map: &mut HashMap<String, String>,
map_index: usize,
) {
// This needs to be the cons that contains (image . ...)
// the image is a symbol and the rest are keywords and other maps
if atom.is_symbol() {
@ -289,7 +320,9 @@ fn build_image_bg(atom: &Value, image_map: &mut HashMap<String, String>, map_ind
return;
}
for atom in atom.list_iter().unwrap().map(|a| a.as_cons().unwrap()) {
for atom in
atom.list_iter().unwrap().map(|a| a.as_cons().unwrap())
{
if atom.car() == &Value::Symbol("image".into()) {
build_image_bg(atom.cdr(), image_map, map_index);
} else {
@ -345,7 +378,11 @@ fn build_slides(
dbg!(&current_symbol);
match value {
Value::Cons(v) => {
slide_builder = build_slides(&v, current_symbol.clone(), slide_builder);
slide_builder = build_slides(
&v,
current_symbol.clone(),
slide_builder,
);
}
Value::Nil => {
dbg!(Value::Nil);
@ -361,7 +398,8 @@ fn build_slides(
}
Value::Symbol(symbol) => {
dbg!(symbol);
current_symbol = Symbol::from_str(symbol).unwrap_or_default();
current_symbol =
Symbol::from_str(symbol).unwrap_or_default();
}
Value::Keyword(keyword) => {
dbg!(keyword);
@ -399,16 +437,19 @@ mod test {
#[test]
fn test_lexp_serialize() {
let lisp = read_to_string("./test_presentation.lisp").expect("oops");
let lisp =
read_to_string("./test_presentation.lisp").expect("oops");
println!("{lisp}");
let mut parser = Parser::from_str_custom(&lisp, Options::elisp());
let mut parser =
Parser::from_str_custom(&lisp, Options::elisp());
for atom in parser.value_iter() {
match atom {
Ok(atom) => {
let symbol = Symbol::None;
let slide_builder = SlideBuilder::new();
atom.as_cons()
.map(|c| build_slides(c, symbol, slide_builder));
atom.as_cons().map(|c| {
build_slides(c, symbol, slide_builder)
});
}
Err(e) => {
dbg!(e);
@ -432,7 +473,8 @@ mod test {
#[test]
fn test_ron_deserialize() {
let slide = read_to_string("./test_presentation.ron").expect("Problem getting file read");
let slide = read_to_string("./test_presentation.ron")
.expect("Problem getting file read");
match ron::from_str::<Vec<Slide>>(&slide) {
Ok(s) => {
dbg!(s);

View file

@ -3,15 +3,20 @@ use std::{collections::HashMap, path::PathBuf};
use cosmic::{executor, iced::Executor};
use miette::{miette, IntoDiagnostic, Result};
use serde::{Deserialize, Serialize};
use sqlx::{query, query_as, sqlite::SqliteRow, FromRow, Row, SqliteConnection};
use sqlx::{
query, query_as, sqlite::SqliteRow, FromRow, Row,
SqliteConnection,
};
use tracing::{debug, error};
use super::{
model::{Model},
model::Model,
slide::{Background, TextAlignment},
};
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub struct Song {
pub id: i32,
pub title: String,
@ -27,9 +32,11 @@ pub struct Song {
}
const VERSE_KEYWORDS: [&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", "Bridge 1", "Bridge 2", "Bridge 3", "Bridge 4",
"Intro 1", "Intro 2", "Ending 1", "Ending 2", "Other 1", "Other 2", "Other 3", "Other 4",
"Verse 1", "Verse 2", "Verse 3", "Verse 4", "Verse 5", "Verse 6",
"Verse 7", "Verse 8", "Chorus 1", "Chorus 2", "Chorus 3",
"Chorus 4", "Bridge 1", "Bridge 2", "Bridge 3", "Bridge 4",
"Intro 1", "Intro 2", "Ending 1", "Ending 2", "Other 1",
"Other 2", "Other 3", "Other 4",
];
impl FromRow<'_, SqliteRow> for Song {
@ -54,7 +61,6 @@ impl FromRow<'_, SqliteRow> for Song {
Ok(background) => Some(background),
Err(_) => None,
}
},
text_alignment: Some({
let horizontal_alignment: String = row.try_get(3)?;
@ -67,8 +73,12 @@ impl FromRow<'_, SqliteRow> for Song {
("left", "center") => TextAlignment::MiddleLeft,
("left", "bottom") => TextAlignment::BottomLeft,
("center", "top") => TextAlignment::TopCenter,
("center", "center") => TextAlignment::MiddleCenter,
("center", "bottom") => TextAlignment::BottomCenter,
("center", "center") => {
TextAlignment::MiddleCenter
}
("center", "bottom") => {
TextAlignment::BottomCenter
}
("right", "top") => TextAlignment::TopRight,
("right", "center") => TextAlignment::MiddleRight,
("right", "bottom") => TextAlignment::BottomRight,
@ -81,7 +91,10 @@ impl FromRow<'_, SqliteRow> for Song {
}
}
pub async fn get_song_from_db(index: i32, db: &mut SqliteConnection) -> Result<Song> {
pub async fn get_song_from_db(
index: i32,
db: &mut SqliteConnection,
) -> Result<Song> {
let row = query(r#"SELECT verse_order as "verse_order!", font_size as "font_size!: i32", background_type as "background_type!", horizontal_text_alignment as "horizontal_text_alignment!", vertical_text_alignment 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 where id = $1"#).bind(index).fetch_one(db).await.into_diagnostic()?;
Ok(Song::from_row(&row).into_diagnostic()?)
}
@ -96,14 +109,16 @@ impl Model<Song> {
match Song::from_row(&song) {
Ok(song) => {
let _ = self.add_item(song);
},
Err(e) => error!("Could not convert song: {e}"),
}
Err(e) => {
error!("Could not convert song: {e}")
}
};
};
},
}
}
Err(e) => {
error!("There was an error in converting songs: {e}");
},
}
}
}
}
@ -115,7 +130,11 @@ impl Song {
return Err(miette!("There is no lyrics here"));
} else if self.verse_order.is_none() {
return Err(miette!("There is no verse_order here"));
} else if self.verse_order.clone().is_some_and(|v| v.is_empty()) {
} else if self
.verse_order
.clone()
.is_some_and(|v| v.is_empty())
{
return Err(miette!("There is no verse_order here"));
}
if let Some(raw_lyrics) = self.lyrics.clone() {
@ -145,16 +164,21 @@ impl Song {
let mut verse_name = "";
debug!(verse = verse);
for word in VERSE_KEYWORDS {
let end_verse = verse.get(1..2).unwrap_or_default();
let beg_verse = verse.get(0..1).unwrap_or_default();
if word.starts_with(beg_verse) && word.ends_with(end_verse) {
let end_verse =
verse.get(1..2).unwrap_or_default();
let beg_verse =
verse.get(0..1).unwrap_or_default();
if word.starts_with(beg_verse)
&& word.ends_with(end_verse)
{
verse_name = word;
continue;
}
}
if let Some(lyric) = lyric_map.get(verse_name) {
if lyric.contains("\n\n") {
let split_lyrics: Vec<&str> = lyric.split("\n\n").collect();
let split_lyrics: Vec<&str> =
lyric.split("\n\n").collect();
for lyric in split_lyrics {
if lyric.is_empty() {
continue;
@ -246,7 +270,8 @@ 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"
song.verse_order =
"O1 V1 C1 C2 O2 V2 C3 C2 O2 B1 C2 C2 E1 O2"
.to_string()
.split(' ')
.map(|s| Some(s.to_string()))
@ -265,7 +290,7 @@ You saved my soul"
async fn model() -> Model<Song> {
let song_model: Model<Song> = Model {
items: vec![],
db: crate::core::model::get_db().await
db: crate::core::model::get_db().await,
};
song_model
}
@ -302,7 +327,10 @@ You saved my soul"
song_model.load_from_db().await;
match song_model.update_item(song, 2) {
Ok(()) => assert_eq!(&cloned_song, song_model.find(|s| s.id == 7).unwrap()),
Ok(()) => assert_eq!(
&cloned_song,
song_model.find(|s| s.id == 7).unwrap()
),
Err(e) => assert!(false, "{e}"),
}
}

View file

@ -1,12 +1,14 @@
use super::model::Model;
use cosmic::{executor, iced::Executor};
use miette::{Result, miette, IntoDiagnostic};
use miette::{miette, IntoDiagnostic, Result};
use serde::{Deserialize, Serialize};
use sqlx::{query_as, SqliteConnection};
use std::path::PathBuf;
use tracing::error;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[derive(
Clone, Debug, Default, PartialEq, Serialize, Deserialize,
)]
pub struct Video {
pub id: i32,
pub title: String,
@ -25,12 +27,17 @@ impl Model<Video> {
let _ = self.add_item(video);
}
}
Err(e) => error!("There was an error in converting videos: {e}"),
Err(e) => {
error!("There was an error in converting videos: {e}")
}
};
}
}
pub async fn get_video_from_db(database_id: i32, db: &mut SqliteConnection) -> Result<Video> {
pub async fn get_video_from_db(
database_id: i32,
db: &mut SqliteConnection,
) -> Result<Video> {
Ok(query_as!(Video, r#"SELECT title as "title!", file_path as "path!", start_time as "start_time!: f32", end_time as "end_time!: f32", loop as "looping!", id as "id: i32" from videos where id = ?"#, database_id).fetch_one(db).await.into_diagnostic()?)
}
@ -51,7 +58,7 @@ mod test {
async fn test_db_and_model() {
let mut video_model: Model<Video> = Model {
items: vec![],
db: crate::core::model::get_db().await
db: crate::core::model::get_db().await,
};
video_model.load_from_db().await;
if let Some(video) = video_model.find(|v| v.id == 73) {
@ -70,16 +77,26 @@ mod test {
let video = test_video("A new video".into());
let mut video_model: Model<Video> = Model {
items: vec![],
db: crate::core::model::get_db().await
db: crate::core::model::get_db().await,
};
let result = video_model.add_item(video.clone());
let new_video = test_video("A newer video".into());
match result {
Ok(_) => {
assert_eq!(&video, video_model.find(|v| v.id == 0).unwrap());
assert_ne!(&new_video, video_model.find(|v| v.id == 0).unwrap());
assert_eq!(
&video,
video_model.find(|v| v.id == 0).unwrap()
);
assert_ne!(
&new_video,
video_model.find(|v| v.id == 0).unwrap()
);
}
Err(e) => assert!(false, "There was an error adding the video: {:?}", e),
Err(e) => assert!(
false,
"There was an error adding the video: {:?}",
e
),
}
}
}