Compare commits
No commits in common. "8a8ea5fb94de53491117fe4315da53cf34d1ad08" and "5eb18e231f6b5f94f0385f571d8aa40f724a1ecf" have entirely different histories.
8a8ea5fb94
...
5eb18e231f
5 changed files with 65 additions and 93 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -10,24 +10,4 @@ macro_rules! concat_os_str {
|
|||
|
||||
|
||||
|
||||
pub trait ResultExt<T, E> {
|
||||
async fn and_then_async<U, F>(self, op: F) -> Result<U, E>
|
||||
where
|
||||
F: AsyncFnOnce(T) -> Result<U, E>;
|
||||
}
|
||||
|
||||
impl<T, E> ResultExt<T, E> for Result<T, E> {
|
||||
async fn and_then_async<U, F>(self, op: F) -> Result<U, E>
|
||||
where
|
||||
F: AsyncFnOnce(T) -> Result<U, E>,
|
||||
{
|
||||
match self {
|
||||
Ok(value) => op(value).await,
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub(crate) use concat_os_str;
|
||||
|
|
|
|||
|
|
@ -74,7 +74,9 @@ impl DataManager {
|
|||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum DataManagerError {
|
||||
NoHomeDir,
|
||||
CannotOpenDB,
|
||||
UnknownDBError,
|
||||
CannotOpenSharedDB,
|
||||
UnknownSharedDBError,
|
||||
CannotOpenLocalDB,
|
||||
UnknownLocalDBError,
|
||||
UnknownTextureError,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<SqliteManager, DataManagerError> {
|
||||
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<Vec<FilmOverview>, 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<Vec<SeriesOverview>, 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<Client, DataManagerError> {
|
||||
async fn create_client(data_dir: &OsStr, db_type: DBType) -> Result<Client, DataManagerError> {
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue