From 4c0123e94e1abfe5b651e006a9e68ac7fcec046d Mon Sep 17 00:00:00 2001 From: Chris Cochrun Date: Tue, 7 Apr 2026 11:28:20 -0500 Subject: [PATCH] working using the passing of model items around Moving the model into and out of the db functions creates more messages, but allows for less complexity in the overall library module. This means that the library can just use those functions to change the model and db, but need to remember to readd the items to the model once the function is finished in the async function. --- src/core/images.rs | 94 ++++---- src/core/presentations.rs | 185 +++++++-------- src/core/songs.rs | 62 +++-- src/core/videos.rs | 115 ++++++---- src/ui/library.rs | 467 +++++++++++++++++++------------------- 5 files changed, 476 insertions(+), 447 deletions(-) diff --git a/src/core/images.rs b/src/core/images.rs index e0d78bf..4aa6440 100644 --- a/src/core/images.rs +++ b/src/core/images.rs @@ -7,13 +7,17 @@ use super::{ service_items::ServiceTrait, }; use crisp::types::{Keyword, Symbol, Value}; -use miette::{IntoDiagnostic, Result}; +use miette::{IntoDiagnostic, Result, miette}; use serde::{Deserialize, Serialize}; use sqlx::{ Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection, query, query_as, }; -use std::path::{Path, PathBuf}; +use std::{ + mem::replace, + path::{Path, PathBuf}, + sync::Arc, +}; use tracing::{debug, error}; #[derive( @@ -254,70 +258,84 @@ impl Model { } pub async fn remove_from_db( - db: PoolConnection, + db: Arc, + mut images: Vec, id: i32, -) -> Result<()> { +) -> Result> { query!("DELETE FROM images WHERE id = $1", id) - .execute(&mut db.detach()) + .execute(&*db) .await .into_diagnostic() - .map(|_| ()) + .map(|_| ())?; + let index = images + .iter() + .position(|current_image| current_image.id == id) + .ok_or_else(|| miette!("Could not find image in model"))?; + images.remove(index); + Ok(images) } -pub async fn add_image_to_db( - image: Image, - db: PoolConnection, -) -> Result<()> { - let path = image - .path - .to_str() - .map(std::string::ToString::to_string) - .unwrap_or_default(); - let mut db = db.detach(); - query!( +pub async fn add_to_db( + new_images: Vec, + mut current_images: Vec, + db: Arc, +) -> Result> { + for image in new_images { + let path = image + .path + .to_str() + .map(std::string::ToString::to_string) + .unwrap_or_default(); + + query!( r#"INSERT INTO images (title, file_path) VALUES ($1, $2)"#, image.title, path, ) - .execute(&mut db) + .execute(&*db) .await .into_diagnostic()?; - Ok(()) + + current_images.push(image); + } + Ok(current_images) } -pub async fn update_image_in_db( +pub async fn update_in_db( image: Image, - db: PoolConnection, -) -> Result<()> { + mut images: Vec, + db: Arc, +) -> Result> { let path = image .path .to_str() .map(std::string::ToString::to_string) .unwrap_or_default(); - let mut db = db.detach(); - debug!(?image, "should be been updated"); - let result = query!( + + query!( r#"UPDATE images SET title = $2, file_path = $3 WHERE id = $1"#, image.id, image.title, path, ) - .execute(&mut db) - .await.into_diagnostic(); + .execute(&*db) + .await.into_diagnostic()?; - match result { - Ok(_) => { - debug!("should have been updated"); - Ok(()) - } - Err(e) => { - error! {?e}; - Err(e) - } - } + let current_image = images + .iter() + .position(|current_image| current_image.id == image.id) + .ok_or_else(|| miette!("Could not find image in model")) + .map(|index| { + images + .get_mut(index) + .expect("We should have this image already") + })?; + + replace(current_image, image); + Ok(images) } -pub async fn get_image_from_db( +pub async fn get_from_db( database_id: i32, db: &mut SqliteConnection, ) -> Result { diff --git a/src/core/presentations.rs b/src/core/presentations.rs index f02b53b..eaefd08 100644 --- a/src/core/presentations.rs +++ b/src/core/presentations.rs @@ -1,13 +1,17 @@ use cosmic::widget::image::Handle; use crisp::types::{Keyword, Symbol, Value}; -use miette::{IntoDiagnostic, Result}; +use miette::{IntoDiagnostic, Result, miette}; use mupdf::{Colorspace, Document, Matrix}; use serde::{Deserialize, Serialize}; use sqlx::{ Row, Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection, prelude::FromRow, query, sqlite::SqliteRow, }; -use std::path::{Path, PathBuf}; +use std::{ + mem::replace, + path::{Path, PathBuf}, + sync::Arc, +}; use tracing::{debug, error}; use crate::{Background, Slide, SlideBuilder, TextAlignment}; @@ -406,61 +410,77 @@ impl Model { } pub async fn remove_from_db( - db: PoolConnection, + db: Arc, + mut presentations: Vec, id: i32, -) -> Result<()> { +) -> Result> { query!("DELETE FROM presentations WHERE id = $1", id) - .execute(&mut db.detach()) + .execute(&*db) .await .into_diagnostic() - .map(|_| ()) + .map(|_| ())?; + + let index = presentations + .iter() + .position(|current_presentation| { + current_presentation.id == id + }) + .ok_or_else(|| { + miette!("Could not find presentation in model") + })?; + presentations.remove(index); + Ok(presentations) } -pub async fn add_presentation_to_db( +pub async fn add_to_db( + new_presentations: Vec, + mut current_presentations: Vec, + db: Arc, +) -> Result> { + for presentation in new_presentations { + let path = presentation + .path + .to_str() + .map(std::string::ToString::to_string) + .unwrap_or_default(); + let html = presentation.kind == PresKind::Html; + let (starting_index, ending_index) = if let PresKind::Pdf { + starting_index, + ending_index, + } = presentation.kind + { + (starting_index, ending_index) + } else { + (0, 0) + }; + query!( + r#"INSERT INTO presentations (title, file_path, html, starting_index, ending_index) VALUES ($1, $2, $3, $4, $5)"#, + presentation.title, + path, + html, + starting_index, + ending_index + ) + .execute(&*db) + .await + .into_diagnostic()?; + + current_presentations.push(presentation); + } + Ok(current_presentations) +} + +pub async fn update_in_db( presentation: Presentation, - db: PoolConnection, -) -> Result<()> { + mut presentations: Vec, + db: Arc, +) -> Result> { let path = presentation .path .to_str() .map(std::string::ToString::to_string) .unwrap_or_default(); let html = presentation.kind == PresKind::Html; - let mut db = db.detach(); - let (starting_index, ending_index) = if let PresKind::Pdf { - starting_index, - ending_index, - } = presentation.kind - { - (starting_index, ending_index) - } else { - (0, 0) - }; - query!( - r#"INSERT INTO presentations (title, file_path, html, starting_index, ending_index) VALUES ($1, $2, $3, $4, $5)"#, - presentation.title, - path, - html, - starting_index, - ending_index - ) - .execute(&mut db) - .await - .into_diagnostic()?; - Ok(()) -} - -pub async fn update_presentation_in_db( - presentation: Presentation, - db: PoolConnection, -) -> Result<()> { - let path = presentation - .path - .to_str() - .map(std::string::ToString::to_string) - .unwrap_or_default(); - let html = presentation.kind == PresKind::Html; - let mut db = db.detach(); let (starting_index, ending_index) = if let PresKind::Pdf { starting_index: s_index, ending_index: e_index, @@ -472,53 +492,8 @@ pub async fn update_presentation_in_db( (0, 0) }; debug!(starting_index, ending_index); - let id = presentation.id; - if let Err(e) = - query!("SELECT id FROM presentations where id = $1", id) - .fetch_one(&mut db) - .await - { - if let Ok(ids) = query!("SELECT id FROM presentations") - .fetch_all(&mut db) - .await - { - let Some(mut max) = ids.iter().map(|r| r.id).max() else { - return Err(miette::miette!("cannot find max id")); - }; - debug!( - ?e, - "Presentation not found adding a new presentation" - ); - max += 1; - let result = query!( - r#"INSERT into presentations VALUES($1, $2, $3, $4, $5, $6)"#, - max, - presentation.title, - path, - html, - starting_index, - ending_index, - ) - .execute(&mut db) - .await - .into_diagnostic(); - return match result { - Ok(_) => { - debug!("presentation should have been added"); - Ok(()) - } - Err(e) => { - error! {?e}; - Err(e) - } - }; - } - return Err(miette::miette!("cannot find ids")); - } - - debug!(?presentation, "should be been updated"); - let result = query!( + query!( r#"UPDATE presentations SET title = $2, file_path = $3, html = $4, starting_index = $5, ending_index = $6 WHERE id = $1"#, presentation.id, presentation.title, @@ -527,19 +502,25 @@ pub async fn update_presentation_in_db( starting_index, ending_index ) - .execute(&mut db) - .await.into_diagnostic(); + .execute(&*db) + .await.into_diagnostic()?; - match result { - Ok(_) => { - debug!("should have been updated"); - Ok(()) - } - Err(e) => { - error! {?e}; - Err(e) - } - } + let current_presentation = presentations + .iter() + .position(|current_presentation| { + current_presentation.id == presentation.id + }) + .ok_or_else(|| { + miette!("Could not find presentation in model") + }) + .map(|index| { + presentations + .get_mut(index) + .expect("We should have this presentation already") + })?; + + replace(current_presentation, presentation); + Ok(presentations) } pub async fn get_presentation_from_db( diff --git a/src/core/songs.rs b/src/core/songs.rs index 8237708..85e4bc5 100644 --- a/src/core/songs.rs +++ b/src/core/songs.rs @@ -1,6 +1,6 @@ use std::{ - borrow::Cow, collections::HashMap, option::Option, path::PathBuf, - sync::Arc, + borrow::Cow, collections::HashMap, mem::replace, option::Option, + path::PathBuf, sync::Arc, }; use cosmic::{ @@ -1027,26 +1027,28 @@ pub async fn add_to_db( Ok(songs) } -pub async fn update_song_in_db( - item: Song, - songs: Vec, +pub async fn update_in_db( + song: Song, + mut songs: Vec, db: Arc, -) -> Result<()> { +) -> Result> { // self.update_item(item.clone(), index)?; // debug!(?item); let verse_order = - ron::ser::to_string(&item.verses).into_diagnostic()?; + ron::ser::to_string(&song.verses).into_diagnostic()?; - let audio = item + let audio = song .audio + .clone() .map(|a| a.to_str().unwrap_or_default().to_string()); - let background = item + let background = song .background + .clone() .map(|b| b.path.to_str().unwrap_or_default().to_string()); - let lyrics = item.verse_map.map(|map| { + let lyrics = song.verse_map.clone().map(|map| { map.iter() .map(|(name, lyric)| { let lyric = lyric.trim_end_matches('\n').to_string(); @@ -1057,7 +1059,7 @@ pub async fn update_song_in_db( let lyrics = ron::ser::to_string(&lyrics).into_diagnostic()?; let (vertical_alignment, horizontal_alignment) = - item.text_alignment.map_or_else( + song.text_alignment.map_or_else( || ("center", "center"), |ta| match ta { TextAlignment::TopLeft => ("top", "left"), @@ -1072,20 +1074,20 @@ pub async fn update_song_in_db( }, ); - let stroke_size = item.stroke_size.unwrap_or_default(); - let shadow_size = item.shadow_size.unwrap_or_default(); + let stroke_size = song.stroke_size.unwrap_or_default(); + let shadow_size = song.shadow_size.unwrap_or_default(); let (shadow_offset_x, shadow_offset_y) = - item.shadow_offset.unwrap_or_default(); + song.shadow_offset.unwrap_or_default(); let stroke_color = - ron::ser::to_string(&item.stroke_color).into_diagnostic()?; + ron::ser::to_string(&song.stroke_color).into_diagnostic()?; let shadow_color = - ron::ser::to_string(&item.shadow_color).into_diagnostic()?; + ron::ser::to_string(&song.shadow_color).into_diagnostic()?; let style = - ron::ser::to_string(&item.font_style).into_diagnostic()?; + ron::ser::to_string(&song.font_style).into_diagnostic()?; let weight = - ron::ser::to_string(&item.font_weight).into_diagnostic()?; + ron::ser::to_string(&song.font_weight).into_diagnostic()?; // debug!( // ?stroke_size, @@ -1098,15 +1100,15 @@ pub async fn update_song_in_db( let result = query!( r#"UPDATE songs SET title = $2, lyrics = $3, author = $4, ccli = $5, verse_order = $6, audio = $7, font = $8, font_size = $9, background = $10, horizontal_text_alignment = $11, vertical_text_alignment = $12, stroke_color = $13, shadow_color = $14, stroke_size = $15, shadow_size = $16, shadow_offset_x = $17, shadow_offset_y = $18, style = $19, weight = $20 WHERE id = $1"#, - item.id, - item.title, + song.id, + song.title, lyrics, - item.author, - item.ccli, + song.author, + song.ccli, verse_order, audio, - item.font, - item.font_size, + song.font, + song.font_size, background, horizontal_alignment, vertical_alignment, @@ -1124,8 +1126,18 @@ pub async fn update_song_in_db( .into_diagnostic()?; debug!(rows_affected = ?result.rows_affected()); + let index = songs + .iter() + .position(|current_song| current_song.id == song.id) + .ok_or_else(|| miette!("Could not find song in model"))?; - Ok(()) + replace( + songs + .get_mut(index) + .expect("We have found the song so this shouldn't fail"), + song, + ); + Ok(songs) } impl Song { diff --git a/src/core/videos.rs b/src/core/videos.rs index e91b93b..76c8acd 100644 --- a/src/core/videos.rs +++ b/src/core/videos.rs @@ -8,13 +8,17 @@ use super::{ slide::Slide, }; use crisp::types::{Keyword, Symbol, Value}; -use miette::{IntoDiagnostic, Result}; +use miette::{IntoDiagnostic, Result, miette}; use serde::{Deserialize, Serialize}; use sqlx::{ Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection, query, query_as, }; -use std::path::{Path, PathBuf}; +use std::{ + mem::replace, + path::{Path, PathBuf}, + sync::Arc, +}; use tracing::{debug, error}; #[derive( @@ -254,52 +258,65 @@ impl Model