this may be the way.

By making the db functions take the vector of items in the model, we
can drain the model, pass an owned version of those items to the async
db function(adding, updating, deleting, etc) and then return an
updated list of the items back in the Result.

We should probably return a tuple with the original vector of items in
case the db function fails somehow.
This commit is contained in:
Chris Cochrun 2026-04-06 16:45:36 -05:00
parent ae1f2830e4
commit fbc4606471
2 changed files with 101 additions and 148 deletions

View file

@ -1,5 +1,6 @@
use std::{
borrow::Cow, collections::HashMap, option::Option, path::PathBuf,
sync::Arc,
};
use cosmic::{
@ -745,7 +746,7 @@ impl Model<Song> {
pub async fn append_song(
&mut self,
song: Song,
db: PoolConnection<Sqlite>,
db: &SqlitePool,
) -> Result<()> {
self.add_item(song)?;
todo!()
@ -753,7 +754,7 @@ impl Model<Song> {
pub async fn new_song(
&mut self,
db: PoolConnection<Sqlite>,
db: Arc<SqlitePool>,
) -> Result<Song> {
let mut song = Song::default();
@ -790,7 +791,7 @@ impl Model<Song> {
song.font_size,
background
)
.execute(&mut db.detach())
.execute(&*db)
.await
.into_diagnostic()?;
song.id = i32::try_from(res.last_insert_rowid()).expect(
@ -803,7 +804,7 @@ impl Model<Song> {
pub async fn update_song(
&mut self,
song: Song,
db: PoolConnection<Sqlite>,
db: &SqlitePool,
) -> Result<()> {
let id = song.id;
self.update_item(song.clone(), |song| song.id == id)?;
@ -898,7 +899,7 @@ impl Model<Song> {
style,
weight
)
.execute(&mut db.detach())
.execute(db)
.await
.into_diagnostic()?;
@ -910,11 +911,11 @@ impl Model<Song> {
pub async fn remove_song(
&mut self,
id: i32,
db: PoolConnection<Sqlite>,
db: &SqlitePool,
) -> Result<()> {
self.remove_item(|current_song| id == current_song.id)?;
query!("DELETE FROM songs WHERE id = $1", id)
.execute(&mut db.detach())
.execute(db)
.await
.into_diagnostic()
.map(|_| ())
@ -970,8 +971,9 @@ pub async fn remove_from_db(
}
pub async fn add_song_to_db(
db: PoolConnection<Sqlite>,
) -> Result<Song> {
mut songs: Vec<Song>,
db: Arc<SqlitePool>,
) -> Result<Vec<Song>> {
let mut song = Song::default();
let verse_order = {
@ -1007,13 +1009,14 @@ pub async fn add_song_to_db(
song.font_size,
background
)
.execute(&mut db.detach())
.execute(&*db)
.await
.into_diagnostic()?;
song.id = i32::try_from(res.last_insert_rowid()).expect(
"Fairly confident that this number won't get that high",
);
Ok(song)
songs.push(song);
Ok(songs)
}
pub async fn update_song_in_db(

View file

@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::{collections::HashMap, sync::Arc};
use cosmic::{
Element, Task,
@ -50,7 +50,7 @@ pub struct Library {
selected_items: Option<Vec<(LibraryKind, i32)>>,
hovered_item: Option<(LibraryKind, i32)>,
editing_item: Option<(LibraryKind, i32)>,
db: SqlitePool,
db: Arc<SqlitePool>,
menu_keys: std::collections::HashMap<menu::KeyBind, MenuMessage>,
context_menu: Option<i32>,
modifiers_pressed: Option<Modifiers>,
@ -104,7 +104,8 @@ pub enum Message {
OpenContext(i32),
None,
AddFiles(Vec<ServiceItemKind>),
AddSong(PoolConnection<Sqlite>),
ReaddSongs(Vec<Song>),
AddSong,
AddImages(Option<Vec<Image>>),
AddVideos(Option<Vec<Video>>),
AddPresentations(Option<Vec<Presentation>>),
@ -130,7 +131,7 @@ impl<'a> Library {
selected_items: None,
hovered_item: None,
editing_item: None,
db,
db: db.into(),
menu_keys: HashMap::new(),
context_menu: None,
modifiers_pressed: None,
@ -142,29 +143,30 @@ impl<'a> Library {
self.song_library.get_item(index)
}
async fn test(&mut self) -> Result<Song> {
self.song_library.new_song(Arc::clone(&self.db)).await
}
#[allow(clippy::cast_possible_wrap)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::too_many_lines)]
#[allow(clippy::match_same_arms)]
pub fn update(&'a mut self, message: Message) -> Action {
pub fn update(&mut self, message: Message) -> Action {
match message {
Message::None => (),
Message::DeleteItem => {
return self.delete_items();
}
Message::AddSong(db) => {
Message::ReaddSongs(songs) => {
self.song_library.items = songs;
}
Message::AddSong => {
let songs =
self.song_library.items.drain(..).collect();
return Action::Task(Task::perform(
self.song_library.new_song(db),
add_song_to_db(songs, Arc::clone(&self.db)),
move |res| match res {
Ok(song) => {
let index =
(self.song_library.items.len() - 1)
as i32;
Message::OpenItem(Some((
LibraryKind::Song,
index,
)))
}
Ok(songs) => Message::ReaddSongs(songs),
Err(e) => {
error!("adding error: {e}");
Message::None
@ -177,12 +179,7 @@ impl<'a> Library {
self.library_open.unwrap_or(LibraryKind::Song);
match kind {
LibraryKind::Song => {
let task = Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!("Database error: {e}")
})
.map(|db| Message::AddSong(db));
return Action::Task(task);
return self.update(Message::AddSong);
}
LibraryKind::Video => {
return Action::Task(Task::perform(
@ -479,20 +476,13 @@ impl<'a> Library {
return Action::None;
}
return Action::Task(
Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!("Database error: {e}")
})
.and_then(move |db| {
Task::perform(
self.song_library
.update_song(song, db),
|r| r.map(|_| Message::SongChanged),
)
})
.map(|r| r.unwrap_or(Message::None)),
);
return Action::Task(Task::perform(
self.song_library.update_song(song, &self.db),
|r| {
r.map(|_| Message::SongChanged)
.unwrap_or(Message::None)
},
));
}
Message::SongChanged => {
// self.song_library.update_item(song, index);
@ -509,20 +499,13 @@ impl<'a> Library {
return Action::None;
}
return Action::Task(
Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!("Database error: {e}")
})
.and_then(move |conn| {
Task::perform(
self.image_library
.update_image(image, conn),
|r| r.map(|_| Message::ImageChanged),
)
})
.map(|r| r.unwrap_or(Message::None)),
);
return Action::Task(Task::perform(
self.image_library.update_image(image, &self.db),
|r| {
r.map(|_| Message::ImageChanged)
.unwrap_or(Message::None)
},
));
}
Message::ImageChanged => (),
Message::UpdateVideo(video) => {
@ -536,20 +519,13 @@ impl<'a> Library {
return Action::None;
}
return Action::Task(
Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!("Database error: {e}")
})
.and_then(move |conn| {
Task::perform(
self.video_library
.update_video(video, conn),
|r| r.map(|_| Message::VideoChanged),
)
})
.map(|r| r.unwrap_or(Message::None)),
);
return Action::Task(Task::perform(
self.video_library.update_video(video, &self.db),
|r| {
r.map(|_| Message::VideoChanged)
.unwrap_or(Message::None)
},
));
}
Message::VideoChanged => debug!("vid shoulda changed"),
Message::UpdatePresentation(presentation) => {
@ -562,25 +538,15 @@ impl<'a> Library {
error!("Not editing a presentation item");
return Action::None;
}
let mut update_fn = move |conn| {
self.presentation_library
.update_presentation(presentation, conn)
};
return Action::Task(
Task::future(self.db.acquire())
.map_err(|e| {
miette::miette!("Database error: {e}")
})
.and_then(move |conn| {
Task::perform(update_fn(conn), |r| {
r.map(|_| {
Message::PresentationChanged
})
})
})
.map(|r| r.unwrap_or(Message::None)),
);
return Action::Task(Task::perform(
self.presentation_library
.update_presentation(presentation, &self.db),
|r| {
r.map(|_| Message::PresentationChanged)
.unwrap_or(Message::None)
},
));
}
Message::PresentationChanged => (),
Message::Error(_) => (),
@ -1264,21 +1230,17 @@ impl<'a> Library {
if let Some(song) =
self.song_library.get_item(*index)
{
Task::future(self.db.acquire()).and_then(
move |db| {
Task::perform(
self.song_library
.remove_song(song.id, db),
|r| {
if let Err(e) = r {
error!(?e);
}
Message::None
},
)
.map(|m| Ok(m))
Task::perform(
self.song_library
.remove_song(song.id, &self.db),
|r| {
if let Err(e) = r {
error!(?e);
}
Message::None
},
)
.map(|m| Ok(m))
} else {
Task::none()
}
@ -1287,21 +1249,17 @@ impl<'a> Library {
if let Some(video) =
self.video_library.get_item(*index)
{
Task::future(self.db.acquire()).and_then(
move |db| {
Task::perform(
self.video_library
.remove_video(video.id, db),
|r| {
if let Err(e) = r {
error!(?e);
}
Message::None
},
)
.map(|m| Ok(m))
Task::perform(
self.video_library
.remove_video(video.id, &self.db),
|r| {
if let Err(e) = r {
error!(?e);
}
Message::None
},
)
.map(|m| Ok(m))
} else {
Task::none()
}
@ -1314,21 +1272,17 @@ impl<'a> Library {
image.id,
image.title, "let's remove this image",
);
Task::future(self.db.acquire()).and_then(
move |db| {
Task::perform(
self.image_library
.remove_image(image.id, db),
|r| {
if let Err(e) = r {
error!(?e);
}
Message::None
},
)
.map(|m| Ok(m))
Task::perform(
self.image_library
.remove_image(image.id, &self.db),
|r| {
if let Err(e) = r {
error!(?e);
}
Message::None
},
)
.map(|m| Ok(m))
} else {
Task::none()
}
@ -1337,24 +1291,20 @@ impl<'a> Library {
if let Some(presentation) =
self.presentation_library.get_item(*index)
{
Task::future(self.db.acquire()).and_then(
move |db| {
Task::perform(
self.presentation_library
.remove_presentation(
presentation.id,
db,
),
|r| {
if let Err(e) = r {
error!(?e);
}
Message::None
},
)
.map(|m| Ok(m))
Task::perform(
self.presentation_library
.remove_presentation(
presentation.id,
&self.db,
),
|r| {
if let Err(e) = r {
error!(?e);
}
Message::None
},
)
.map(|m| Ok(m))
} else {
Task::none()
}