Rewrite using Relm4
Yes, this is a monster commit but at this stage I'm the only one working on this project anyway. Further commits will follow Best Practises™ again.
This commit is contained in:
parent
d25fdd0a32
commit
4d4b7eb1c7
43 changed files with 2159 additions and 1248 deletions
196
src/persist/sqlite_manager.rs
Normal file
196
src/persist/sqlite_manager.rs
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
use std::ffi::OsStr;
|
||||
use std::fmt;
|
||||
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::concat_os_str;
|
||||
use crate::persist::data_manager::DataManagerError;
|
||||
use crate::views::overview::{FilmOverview, SeriesOverview};
|
||||
|
||||
|
||||
|
||||
pub struct SqliteManager {
|
||||
client_shared: Client,
|
||||
client_local: Client,
|
||||
}
|
||||
|
||||
impl SqliteManager {
|
||||
pub async fn new(data_dir: &OsStr) -> Result<SqliteManager, DataManagerError> {
|
||||
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_shared
|
||||
.conn(|connection| {
|
||||
let overview = connection
|
||||
.prepare(
|
||||
"
|
||||
select uuid, name, original_name, release_date, runtime_minutes
|
||||
from films
|
||||
",
|
||||
)
|
||||
.expect("films overview statement should be valid SQL")
|
||||
.query(())
|
||||
.expect("parameters in films overview query should match those in its statement")
|
||||
.map(row_to_film_overview)
|
||||
.collect()?;
|
||||
Ok(overview)
|
||||
})
|
||||
.await;
|
||||
|
||||
overview.map_err(|async_sqlite_error| match async_sqlite_error {
|
||||
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")
|
||||
}
|
||||
rusqlite::Error::InvalidColumnName(_) => {
|
||||
panic!("column names obtained from films overview query should exist")
|
||||
}
|
||||
rusqlite::Error::InvalidColumnType(..) => panic!(
|
||||
"values obtained from films overview query should have a type matching their column"
|
||||
),
|
||||
_ => DataManagerError::UnknownSharedDBError,
|
||||
},
|
||||
_ => DataManagerError::UnknownSharedDBError,
|
||||
})
|
||||
}
|
||||
|
||||
// The order of the items is undefined.
|
||||
pub async fn series_overview(&self) -> Result<Vec<SeriesOverview>, DataManagerError> {
|
||||
let overview = self
|
||||
.client_shared
|
||||
.conn(|connection| {
|
||||
let overview = connection
|
||||
.prepare(
|
||||
"
|
||||
select series.uuid, series.name, series.original_name,
|
||||
min(episodes.release_date) as first_release_date
|
||||
from series, seasons, episodes
|
||||
where series.uuid = seasons.series and seasons.uuid = episodes.season
|
||||
group by series.uuid
|
||||
",
|
||||
)
|
||||
.expect("series overview statement should be valid SQL")
|
||||
.query(())
|
||||
.expect("parameters in series overview query should match those in its statement")
|
||||
.map(row_to_series_overview)
|
||||
.collect()?;
|
||||
Ok(overview)
|
||||
})
|
||||
.await;
|
||||
|
||||
overview.map_err(|async_sqlite_error| match async_sqlite_error {
|
||||
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")
|
||||
}
|
||||
rusqlite::Error::InvalidColumnName(_) => {
|
||||
panic!("column names obtained from series overview query should exist")
|
||||
}
|
||||
rusqlite::Error::InvalidColumnType(..) => panic!(
|
||||
"values obtained from series overview query should have a type matching their column"
|
||||
),
|
||||
_ => DataManagerError::UnknownSharedDBError,
|
||||
},
|
||||
_ => DataManagerError::UnknownSharedDBError,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for SqliteManager {
|
||||
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
|
||||
formatter.write_str("SqliteManager { Client, Client }")
|
||||
}
|
||||
}
|
||||
|
||||
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(concat_os_str!(data_dir, filename))
|
||||
.flags(open_mode | OpenFlags::SQLITE_OPEN_NO_MUTEX)
|
||||
.open()
|
||||
.await;
|
||||
|
||||
client.map_err(|async_sqlite_error| match async_sqlite_error {
|
||||
async_sqlite::Error::Rusqlite(rusqlite_error) => match rusqlite_error {
|
||||
rusqlite::Error::SqliteFailure(sqlite_error, _) => match sqlite_error.code {
|
||||
rusqlite::ffi::ErrorCode::CannotOpen => cannot_open_err,
|
||||
_ => unknown_err,
|
||||
},
|
||||
_ => unknown_err,
|
||||
},
|
||||
_ => unknown_err,
|
||||
})
|
||||
}
|
||||
|
||||
fn row_to_film_overview(row: &Row) -> rusqlite::Result<FilmOverview> {
|
||||
let uuid = row.get("uuid")?;
|
||||
let name = row.get("name")?;
|
||||
let original_name = row.get("original_name")?;
|
||||
let release_date = row.get("release_date")?;
|
||||
let runtime = row.get("runtime_minutes")?;
|
||||
|
||||
Ok(FilmOverview {
|
||||
uuid,
|
||||
name,
|
||||
original_name,
|
||||
release_date,
|
||||
runtime,
|
||||
})
|
||||
}
|
||||
|
||||
fn row_to_series_overview(row: &Row) -> rusqlite::Result<SeriesOverview> {
|
||||
let uuid = row.get("uuid")?;
|
||||
let name = row.get("name")?;
|
||||
let original_name = row.get("original_name")?;
|
||||
let first_release_date = row.get("first_release_date")?;
|
||||
|
||||
Ok(SeriesOverview {
|
||||
uuid,
|
||||
name,
|
||||
original_name,
|
||||
first_release_date,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum DBType {
|
||||
Shared,
|
||||
Local,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue