diff --git a/src/main.rs b/src/main.rs index 6b69c7d..5681a37 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use std::cmp::max; use std::thread; use gtk4::gdk::Display; +use gtk4::gio::File; use gtk4::{CssProvider, Settings, gio}; use relm4::RelmApp; @@ -39,11 +40,12 @@ fn include_app_css() { gio::resources_register_include!("zoodex.gresource") .expect("CSS resource bundle should have valid format"); + let app_css = File::for_uri("resource:///com/kernelmaft/zoodex/application.css"); let provider = CssProvider::new(); let display = Display::default().expect("getting the default GDK4 display should never fail"); - provider.load_from_resource("/com/kernelmaft/zoodex/application.css"); + provider.load_from_file(&app_css); gtk4::style_context_add_provider_for_display( &display, &provider, diff --git a/src/persist/common.rs b/src/persist/common.rs index 5a26a55..1ea4719 100644 --- a/src/persist/common.rs +++ b/src/persist/common.rs @@ -10,24 +10,4 @@ macro_rules! concat_os_str { -pub trait ResultExt { - async fn and_then_async(self, op: F) -> Result - where - F: AsyncFnOnce(T) -> Result; -} - -impl ResultExt for Result { - async fn and_then_async(self, op: F) -> Result - where - F: AsyncFnOnce(T) -> Result, - { - match self { - Ok(value) => op(value).await, - Err(error) => Err(error), - } - } -} - - - pub(crate) use concat_os_str; diff --git a/src/persist/data_manager.rs b/src/persist/data_manager.rs index d4a6278..2e265a5 100644 --- a/src/persist/data_manager.rs +++ b/src/persist/data_manager.rs @@ -74,7 +74,9 @@ impl DataManager { #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum DataManagerError { NoHomeDir, - CannotOpenDB, - UnknownDBError, + CannotOpenSharedDB, + UnknownSharedDBError, + CannotOpenLocalDB, + UnknownLocalDBError, UnknownTextureError, } diff --git a/src/persist/sqlite_manager.rs b/src/persist/sqlite_manager.rs index f3ace8a..2599f44 100644 --- a/src/persist/sqlite_manager.rs +++ b/src/persist/sqlite_manager.rs @@ -5,29 +5,38 @@ use std::fmt::{Debug, Formatter}; use async_sqlite::rusqlite::fallible_iterator::FallibleIterator; use async_sqlite::rusqlite::{OpenFlags, Row}; use async_sqlite::{Client, ClientBuilder, rusqlite}; +use tokio::try_join; -use crate::persist::common::{ResultExt, concat_os_str}; +use crate::persist::common::concat_os_str; use crate::persist::data_manager::DataManagerError; use crate::views::overview::{FilmOverview, SeriesOverview}; pub struct SqliteManager { - client: Client, + client_shared: Client, + client_local: Client, } impl SqliteManager { pub async fn new(data_dir: &OsStr) -> Result { - let client = create_client(data_dir).await?; - Ok(SqliteManager { client }) + let (client_shared, client_local) = try_join!( + create_client(data_dir, DBType::Shared), + create_client(data_dir, DBType::Local), + )?; + + Ok(SqliteManager { + client_shared, + client_local, + }) } // The order of the items is undefined. pub async fn films_overview(&self) -> Result, DataManagerError> { let overview = self - .client + .client_shared .conn(|connection| { - connection + let overview = connection .prepare( " select uuid, name, original_name, release_date, runtime_minutes @@ -38,14 +47,15 @@ impl SqliteManager { .query(()) .expect("parameters in films overview query should match those in its statement") .map(row_to_film_overview) - .collect() + .collect()?; + Ok(overview) }) .await; overview.map_err(|async_sqlite_error| match async_sqlite_error { - async_sqlite::Error::Closed => { - panic!("database connection should remain open as long as the application is running") - } + async_sqlite::Error::Closed => panic!( + "shared database connection should remain open as long as the application is running" + ), async_sqlite::Error::Rusqlite(rusqlite_error) => match rusqlite_error { rusqlite::Error::InvalidColumnIndex(_) => { panic!("column indices obtained from films overview query should exist") @@ -56,18 +66,18 @@ impl SqliteManager { rusqlite::Error::InvalidColumnType(..) => panic!( "values obtained from films overview query should have a type matching their column" ), - _ => DataManagerError::UnknownDBError, + _ => DataManagerError::UnknownSharedDBError, }, - _ => DataManagerError::UnknownDBError, + _ => DataManagerError::UnknownSharedDBError, }) } // The order of the items is undefined. pub async fn series_overview(&self) -> Result, DataManagerError> { let overview = self - .client + .client_shared .conn(|connection| { - connection + let overview = connection .prepare( " select series.uuid, series.name, series.original_name, @@ -81,14 +91,15 @@ impl SqliteManager { .query(()) .expect("parameters in series overview query should match those in its statement") .map(row_to_series_overview) - .collect() + .collect()?; + Ok(overview) }) .await; overview.map_err(|async_sqlite_error| match async_sqlite_error { - async_sqlite::Error::Closed => { - panic!("database connection should remain open as long as the application is running") - } + async_sqlite::Error::Closed => panic!( + "shared database connection should remain open as long as the application is running" + ), async_sqlite::Error::Rusqlite(rusqlite_error) => match rusqlite_error { rusqlite::Error::InvalidColumnIndex(_) => { panic!("column indices obtained from series overview query should exist") @@ -99,9 +110,9 @@ impl SqliteManager { rusqlite::Error::InvalidColumnType(..) => panic!( "values obtained from series overview query should have a type matching their column" ), - _ => DataManagerError::UnknownDBError, + _ => DataManagerError::UnknownSharedDBError, }, - _ => DataManagerError::UnknownDBError, + _ => DataManagerError::UnknownSharedDBError, }) } } @@ -112,57 +123,39 @@ impl Debug for SqliteManager { } } -async fn create_client(data_dir: &OsStr) -> Result { +async fn create_client(data_dir: &OsStr, db_type: DBType) -> Result { + let open_mode = match db_type { + DBType::Shared => OpenFlags::SQLITE_OPEN_READ_ONLY, + DBType::Local => OpenFlags::SQLITE_OPEN_READ_WRITE, + }; + let filename = match db_type { + DBType::Shared => "/shared.sqlite", + DBType::Local => "/local.sqlite", + }; + let cannot_open_err = match db_type { + DBType::Shared => DataManagerError::CannotOpenSharedDB, + DBType::Local => DataManagerError::CannotOpenLocalDB, + }; + let unknown_err = match db_type { + DBType::Shared => DataManagerError::UnknownSharedDBError, + DBType::Local => DataManagerError::UnknownLocalDBError, + }; + let client = ClientBuilder::new() - .path("") - .flags(OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_NO_MUTEX) + .path(concat_os_str!(data_dir, filename)) + .flags(open_mode | OpenFlags::SQLITE_OPEN_NO_MUTEX) .open() .await; - let data_dir = data_dir.to_os_string(); - let client = client - .and_then_async(async move |client| { - client - .conn(move |connection| { - let shared_path_os_str = concat_os_str!(&data_dir, "/shared.sqlite"); - let shared_path = shared_path_os_str - .to_str() - .expect("shared database path should be valid Unicode"); - connection - .execute("attach database :path as shared", &[(":path", shared_path)]) - .expect( - "shared database attaching statement should be valid SQL with matching parameters", - ); - - let local_path_os_str = concat_os_str!(&data_dir, "/local.sqlite"); - let local_path = local_path_os_str - .to_str() - .expect("local database path should be valid Unicode"); - connection - .execute("attach database :path as local", &[(":path", local_path)]) - .expect( - "local database attaching statement should be valid SQL with matching parameters", - ); - - Ok(()) - }) - .await - .map(|_| client) - }) - .await; - client.map_err(|async_sqlite_error| match async_sqlite_error { - async_sqlite::Error::Closed => { - panic!("database connection should remain open as long as the application is running") - } async_sqlite::Error::Rusqlite(rusqlite_error) => match rusqlite_error { rusqlite::Error::SqliteFailure(sqlite_error, _) => match sqlite_error.code { - rusqlite::ffi::ErrorCode::CannotOpen => DataManagerError::CannotOpenDB, - _ => DataManagerError::UnknownDBError, + rusqlite::ffi::ErrorCode::CannotOpen => cannot_open_err, + _ => unknown_err, }, - _ => DataManagerError::UnknownDBError, + _ => unknown_err, }, - _ => DataManagerError::UnknownDBError, + _ => unknown_err, }) } diff --git a/src/ui/components/media_details/film_details.rs b/src/ui/components/media_details/film_details.rs index 1638587..a1e0e87 100644 --- a/src/ui/components/media_details/film_details.rs +++ b/src/ui/components/media_details/film_details.rs @@ -1,5 +1,5 @@ -use gtk4::prelude::{BoxExt, ButtonExt, OrientableExt, WidgetExt}; -use gtk4::{Button, Label, Orientation}; +use gtk4::prelude::{BoxExt, OrientableExt, WidgetExt}; +use gtk4::{Label, Orientation}; use relm4::{ComponentParts, ComponentSender, RelmWidgetExt, SimpleComponent, component}; use crate::views::overview::FilmOverview; @@ -25,12 +25,7 @@ impl SimpleComponent for FilmDetails { Label { set_css_classes: &["title-1"], set_label: model.film_overview.name.as_str(), - }, - - Button { - set_css_classes: &["suggested-action", "circular"], - set_icon_name: "media-playback-start-symbolic", - }, + } } }