mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
Add custom error type to database module
This commit is contained in:
parent
5a41d5008f
commit
d7fb996183
11 changed files with 232 additions and 116 deletions
|
|
@ -1,6 +1,5 @@
|
|||
use super::schema::ensembles;
|
||||
use super::Database;
|
||||
use anyhow::Result;
|
||||
use super::{Database, DatabaseResult};
|
||||
use diesel::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -14,7 +13,7 @@ pub struct Ensemble {
|
|||
|
||||
impl Database {
|
||||
/// Update an existing ensemble or insert a new one.
|
||||
pub fn update_ensemble(&self, ensemble: Ensemble) -> Result<()> {
|
||||
pub fn update_ensemble(&self, ensemble: Ensemble) -> DatabaseResult<()> {
|
||||
self.defer_foreign_keys()?;
|
||||
|
||||
self.connection.transaction(|| {
|
||||
|
|
@ -27,7 +26,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Get an existing ensemble.
|
||||
pub fn get_ensemble(&self, id: &str) -> Result<Option<Ensemble>> {
|
||||
pub fn get_ensemble(&self, id: &str) -> DatabaseResult<Option<Ensemble>> {
|
||||
let ensemble = ensembles::table
|
||||
.filter(ensembles::id.eq(id))
|
||||
.load::<Ensemble>(&self.connection)?
|
||||
|
|
@ -38,14 +37,14 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Delete an existing ensemble.
|
||||
pub fn delete_ensemble(&self, id: &str) -> Result<()> {
|
||||
pub fn delete_ensemble(&self, id: &str) -> DatabaseResult<()> {
|
||||
diesel::delete(ensembles::table.filter(ensembles::id.eq(id))).execute(&self.connection)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get all existing ensembles.
|
||||
pub fn get_ensembles(&self) -> Result<Vec<Ensemble>> {
|
||||
pub fn get_ensembles(&self) -> DatabaseResult<Vec<Ensemble>> {
|
||||
let ensembles = ensembles::table.load::<Ensemble>(&self.connection)?;
|
||||
|
||||
Ok(ensembles)
|
||||
|
|
|
|||
26
src/backend/database/error.rs
Normal file
26
src/backend/database/error.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
use thiserror::Error;
|
||||
|
||||
/// Error that happens within the database module.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DatabaseError {
|
||||
#[error(transparent)]
|
||||
ConnectionError(#[from] diesel::result::ConnectionError),
|
||||
|
||||
#[error(transparent)]
|
||||
MigrationsError(#[from] diesel_migrations::RunMigrationsError),
|
||||
|
||||
#[error(transparent)]
|
||||
QueryError(#[from] diesel::result::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
SendError(#[from] std::sync::mpsc::SendError<super::thread::Action>),
|
||||
|
||||
#[error(transparent)]
|
||||
ReceiveError(#[from] futures_channel::oneshot::Canceled),
|
||||
|
||||
#[error("Database error: {0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
/// Return type for database methods.
|
||||
pub type DatabaseResult<T> = Result<T, DatabaseError>;
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
use super::schema::instruments;
|
||||
use super::Database;
|
||||
use anyhow::Result;
|
||||
use super::{Database, DatabaseResult};
|
||||
use diesel::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -14,7 +13,7 @@ pub struct Instrument {
|
|||
|
||||
impl Database {
|
||||
/// Update an existing instrument or insert a new one.
|
||||
pub fn update_instrument(&self, instrument: Instrument) -> Result<()> {
|
||||
pub fn update_instrument(&self, instrument: Instrument) -> DatabaseResult<()> {
|
||||
self.defer_foreign_keys()?;
|
||||
|
||||
self.connection.transaction(|| {
|
||||
|
|
@ -27,7 +26,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Get an existing instrument.
|
||||
pub fn get_instrument(&self, id: &str) -> Result<Option<Instrument>> {
|
||||
pub fn get_instrument(&self, id: &str) -> DatabaseResult<Option<Instrument>> {
|
||||
let instrument = instruments::table
|
||||
.filter(instruments::id.eq(id))
|
||||
.load::<Instrument>(&self.connection)?
|
||||
|
|
@ -38,7 +37,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Delete an existing instrument.
|
||||
pub fn delete_instrument(&self, id: &str) -> Result<()> {
|
||||
pub fn delete_instrument(&self, id: &str) -> DatabaseResult<()> {
|
||||
diesel::delete(instruments::table.filter(instruments::id.eq(id)))
|
||||
.execute(&self.connection)?;
|
||||
|
||||
|
|
@ -46,7 +45,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Get all existing instruments.
|
||||
pub fn get_instruments(&self) -> Result<Vec<Instrument>> {
|
||||
pub fn get_instruments(&self) -> DatabaseResult<Vec<Instrument>> {
|
||||
let instruments = instruments::table.load::<Instrument>(&self.connection)?;
|
||||
|
||||
Ok(instruments)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use super::generate_id;
|
||||
use super::schema::{mediums, recordings, track_sets, tracks};
|
||||
use super::{Database, Recording};
|
||||
use anyhow::{anyhow, Error, Result};
|
||||
use super::{Database, DatabaseError, Recording, DatabaseResult};
|
||||
use diesel::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -80,10 +79,10 @@ struct TrackRow {
|
|||
|
||||
impl Database {
|
||||
/// Update an existing medium or insert a new one.
|
||||
pub fn update_medium(&self, medium: Medium) -> Result<()> {
|
||||
pub fn update_medium(&self, medium: Medium) -> DatabaseResult<()> {
|
||||
self.defer_foreign_keys()?;
|
||||
|
||||
self.connection.transaction::<(), Error, _>(|| {
|
||||
self.connection.transaction::<(), DatabaseError, _>(|| {
|
||||
let medium_id = &medium.id;
|
||||
|
||||
// This will also delete the track sets and tracks.
|
||||
|
|
@ -153,7 +152,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Get an existing medium.
|
||||
pub fn get_medium(&self, id: &str) -> Result<Option<Medium>> {
|
||||
pub fn get_medium(&self, id: &str) -> DatabaseResult<Option<Medium>> {
|
||||
let row = mediums::table
|
||||
.filter(mediums::id.eq(id))
|
||||
.load::<MediumRow>(&self.connection)?
|
||||
|
|
@ -170,13 +169,13 @@ impl Database {
|
|||
|
||||
/// Delete a medium and all of its tracks. This will fail, if the music
|
||||
/// library contains audio files referencing any of those tracks.
|
||||
pub fn delete_medium(&self, id: &str) -> Result<()> {
|
||||
pub fn delete_medium(&self, id: &str) -> DatabaseResult<()> {
|
||||
diesel::delete(mediums::table.filter(mediums::id.eq(id))).execute(&self.connection)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get all available track sets for a recording.
|
||||
pub fn get_track_sets(&self, recording_id: &str) -> Result<Vec<TrackSet>> {
|
||||
pub fn get_track_sets(&self, recording_id: &str) -> DatabaseResult<Vec<TrackSet>> {
|
||||
let mut track_sets: Vec<TrackSet> = Vec::new();
|
||||
|
||||
let rows = track_sets::table
|
||||
|
|
@ -194,7 +193,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Retrieve all available information on a medium from related tables.
|
||||
fn get_medium_data(&self, row: MediumRow) -> Result<Medium> {
|
||||
fn get_medium_data(&self, row: MediumRow) -> DatabaseResult<Medium> {
|
||||
let track_set_rows = track_sets::table
|
||||
.filter(track_sets::medium.eq(&row.id))
|
||||
.order_by(track_sets::index)
|
||||
|
|
@ -218,12 +217,16 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Convert a track set row from the database to an actual track set.
|
||||
fn get_track_set_from_row(&self, row: TrackSetRow) -> Result<TrackSet> {
|
||||
fn get_track_set_from_row(&self, row: TrackSetRow) -> DatabaseResult<TrackSet> {
|
||||
let recording_id = row.recording;
|
||||
|
||||
let recording = self
|
||||
.get_recording(&recording_id)?
|
||||
.ok_or_else(|| anyhow!("No recording with ID: {}", recording_id))?;
|
||||
.ok_or(DatabaseError::Other(format!(
|
||||
"Failed to get recording ({}) for track set ({}).",
|
||||
recording_id,
|
||||
row.id,
|
||||
)))?;
|
||||
|
||||
let track_rows = tracks::table
|
||||
.filter(tracks::track_set.eq(row.id))
|
||||
|
|
@ -236,8 +239,14 @@ impl Database {
|
|||
let work_parts = track_row
|
||||
.work_parts
|
||||
.split(',')
|
||||
.map(|part_index| Ok(str::parse(part_index)?))
|
||||
.collect::<Result<Vec<usize>>>()?;
|
||||
.map(|part_index| {
|
||||
str::parse(part_index)
|
||||
.or(Err(DatabaseError::Other(format!(
|
||||
"Failed to parse part index from '{}'.",
|
||||
track_row.work_parts,
|
||||
)))?)
|
||||
})
|
||||
.collect::<DatabaseResult<Vec<usize>>>()?;
|
||||
|
||||
let track = Track {
|
||||
work_parts,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use anyhow::Result;
|
||||
use diesel::prelude::*;
|
||||
|
||||
pub mod ensembles;
|
||||
pub use ensembles::*;
|
||||
|
||||
pub mod error;
|
||||
pub use error::*;
|
||||
|
||||
pub mod instruments;
|
||||
pub use instruments::*;
|
||||
|
||||
|
|
@ -42,7 +44,7 @@ pub struct Database {
|
|||
|
||||
impl Database {
|
||||
/// Create a new database interface and run migrations if necessary.
|
||||
pub fn new(file_name: &str) -> Result<Database> {
|
||||
pub fn new(file_name: &str) -> DatabaseResult<Database> {
|
||||
let connection = SqliteConnection::establish(file_name)?;
|
||||
|
||||
diesel::sql_query("PRAGMA foreign_keys = ON").execute(&connection)?;
|
||||
|
|
@ -52,7 +54,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Defer all foreign keys for the next transaction.
|
||||
fn defer_foreign_keys(&self) -> Result<()> {
|
||||
fn defer_foreign_keys(&self) -> DatabaseResult<()> {
|
||||
diesel::sql_query("PRAGMA defer_foreign_keys = ON").execute(&self.connection)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use super::schema::persons;
|
||||
use super::Database;
|
||||
use anyhow::Result;
|
||||
use super::{Database, DatabaseResult};
|
||||
use diesel::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -27,7 +26,7 @@ impl Person {
|
|||
|
||||
impl Database {
|
||||
/// Update an existing person or insert a new one.
|
||||
pub fn update_person(&self, person: Person) -> Result<()> {
|
||||
pub fn update_person(&self, person: Person) -> DatabaseResult<()> {
|
||||
self.defer_foreign_keys()?;
|
||||
|
||||
self.connection.transaction(|| {
|
||||
|
|
@ -40,7 +39,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Get an existing person.
|
||||
pub fn get_person(&self, id: &str) -> Result<Option<Person>> {
|
||||
pub fn get_person(&self, id: &str) -> DatabaseResult<Option<Person>> {
|
||||
let person = persons::table
|
||||
.filter(persons::id.eq(id))
|
||||
.load::<Person>(&self.connection)?
|
||||
|
|
@ -51,14 +50,14 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Delete an existing person.
|
||||
pub fn delete_person(&self, id: &str) -> Result<()> {
|
||||
pub fn delete_person(&self, id: &str) -> DatabaseResult<()> {
|
||||
diesel::delete(persons::table.filter(persons::id.eq(id))).execute(&self.connection)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get all existing persons.
|
||||
pub fn get_persons(&self) -> Result<Vec<Person>> {
|
||||
pub fn get_persons(&self) -> DatabaseResult<Vec<Person>> {
|
||||
let persons = persons::table.load::<Person>(&self.connection)?;
|
||||
|
||||
Ok(persons)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use super::generate_id;
|
||||
use super::schema::{ensembles, performances, persons, recordings};
|
||||
use super::{Database, Ensemble, Instrument, Person, Work};
|
||||
use anyhow::{anyhow, Error, Result};
|
||||
use super::{Database, Ensemble, DatabaseError, Instrument, Person, DatabaseResult, Work};
|
||||
use diesel::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -120,9 +119,9 @@ impl Recording {
|
|||
impl Database {
|
||||
/// Update an existing recording or insert a new one.
|
||||
// TODO: Think about whether to also insert the other items.
|
||||
pub fn update_recording(&self, recording: Recording) -> Result<()> {
|
||||
pub fn update_recording(&self, recording: Recording) -> DatabaseResult<()> {
|
||||
self.defer_foreign_keys()?;
|
||||
self.connection.transaction::<(), Error, _>(|| {
|
||||
self.connection.transaction::<(), DatabaseError, _>(|| {
|
||||
let recording_id = &recording.id;
|
||||
self.delete_recording(recording_id)?;
|
||||
|
||||
|
|
@ -180,7 +179,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Check whether the database contains a recording.
|
||||
pub fn recording_exists(&self, id: &str) -> Result<bool> {
|
||||
pub fn recording_exists(&self, id: &str) -> DatabaseResult<bool> {
|
||||
let exists = recordings::table
|
||||
.filter(recordings::id.eq(id))
|
||||
.load::<RecordingRow>(&self.connection)?
|
||||
|
|
@ -191,7 +190,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Get an existing recording.
|
||||
pub fn get_recording(&self, id: &str) -> Result<Option<Recording>> {
|
||||
pub fn get_recording(&self, id: &str) -> DatabaseResult<Option<Recording>> {
|
||||
let row = recordings::table
|
||||
.filter(recordings::id.eq(id))
|
||||
.load::<RecordingRow>(&self.connection)?
|
||||
|
|
@ -207,7 +206,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Retrieve all available information on a recording from related tables.
|
||||
fn get_recording_data(&self, row: RecordingRow) -> Result<Recording> {
|
||||
fn get_recording_data(&self, row: RecordingRow) -> DatabaseResult<Recording> {
|
||||
let mut performance_descriptions: Vec<Performance> = Vec::new();
|
||||
|
||||
let performance_rows = performances::table
|
||||
|
|
@ -219,21 +218,33 @@ impl Database {
|
|||
person: match row.person {
|
||||
Some(id) => Some(
|
||||
self.get_person(&id)?
|
||||
.ok_or(anyhow!("No person with ID: {}", id))?,
|
||||
.ok_or(DatabaseError::Other(format!(
|
||||
"Failed to get person ({}) for recording ({}).",
|
||||
id,
|
||||
row.id,
|
||||
)))?
|
||||
),
|
||||
None => None,
|
||||
},
|
||||
ensemble: match row.ensemble {
|
||||
Some(id) => Some(
|
||||
self.get_ensemble(&id)?
|
||||
.ok_or(anyhow!("No ensemble with ID: {}", id))?,
|
||||
.ok_or(DatabaseError::Other(format!(
|
||||
"Failed to get ensemble ({}) for recording ({}).",
|
||||
id,
|
||||
row.id,
|
||||
)))?
|
||||
),
|
||||
None => None,
|
||||
},
|
||||
role: match row.role {
|
||||
Some(id) => Some(
|
||||
self.get_instrument(&id)?
|
||||
.ok_or(anyhow!("No instrument with ID: {}", id))?,
|
||||
.ok_or(DatabaseError::Other(format!(
|
||||
"Failed to get instrument ({}) for recording ({}).",
|
||||
id,
|
||||
row.id,
|
||||
)))?
|
||||
),
|
||||
None => None,
|
||||
},
|
||||
|
|
@ -243,7 +254,11 @@ impl Database {
|
|||
let work_id = &row.work;
|
||||
let work = self
|
||||
.get_work(work_id)?
|
||||
.ok_or(anyhow!("Work doesn't exist: {}", work_id))?;
|
||||
.ok_or(DatabaseError::Other(format!(
|
||||
"Failed to get work ({}) for recording ({}).",
|
||||
work_id,
|
||||
row.id,
|
||||
)))?;
|
||||
|
||||
let recording_description = Recording {
|
||||
id: row.id,
|
||||
|
|
@ -256,7 +271,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Get all available information on all recordings where a person is performing.
|
||||
pub fn get_recordings_for_person(&self, person_id: &str) -> Result<Vec<Recording>> {
|
||||
pub fn get_recordings_for_person(&self, person_id: &str) -> DatabaseResult<Vec<Recording>> {
|
||||
let mut recordings: Vec<Recording> = Vec::new();
|
||||
|
||||
let rows = recordings::table
|
||||
|
|
@ -274,7 +289,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Get all available information on all recordings where an ensemble is performing.
|
||||
pub fn get_recordings_for_ensemble(&self, ensemble_id: &str) -> Result<Vec<Recording>> {
|
||||
pub fn get_recordings_for_ensemble(&self, ensemble_id: &str) -> DatabaseResult<Vec<Recording>> {
|
||||
let mut recordings: Vec<Recording> = Vec::new();
|
||||
|
||||
let rows = recordings::table
|
||||
|
|
@ -292,7 +307,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Get allavailable information on all recordings of a work.
|
||||
pub fn get_recordings_for_work(&self, work_id: &str) -> Result<Vec<Recording>> {
|
||||
pub fn get_recordings_for_work(&self, work_id: &str) -> DatabaseResult<Vec<Recording>> {
|
||||
let mut recordings: Vec<Recording> = Vec::new();
|
||||
|
||||
let rows = recordings::table
|
||||
|
|
@ -308,7 +323,7 @@ impl Database {
|
|||
|
||||
/// Delete an existing recording. This will fail if there are still references to this
|
||||
/// recording from other tables that are not directly part of the recording data.
|
||||
pub fn delete_recording(&self, id: &str) -> Result<()> {
|
||||
pub fn delete_recording(&self, id: &str) -> DatabaseResult<()> {
|
||||
diesel::delete(recordings::table.filter(recordings::id.eq(id)))
|
||||
.execute(&self.connection)?;
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1,37 +1,36 @@
|
|||
use super::*;
|
||||
use anyhow::Result;
|
||||
use futures_channel::oneshot;
|
||||
use futures_channel::oneshot::Sender;
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
|
||||
/// An action the database thread can perform.
|
||||
enum Action {
|
||||
UpdatePerson(Person, Sender<Result<()>>),
|
||||
GetPerson(String, Sender<Result<Option<Person>>>),
|
||||
DeletePerson(String, Sender<Result<()>>),
|
||||
GetPersons(Sender<Result<Vec<Person>>>),
|
||||
UpdateInstrument(Instrument, Sender<Result<()>>),
|
||||
GetInstrument(String, Sender<Result<Option<Instrument>>>),
|
||||
DeleteInstrument(String, Sender<Result<()>>),
|
||||
GetInstruments(Sender<Result<Vec<Instrument>>>),
|
||||
UpdateWork(Work, Sender<Result<()>>),
|
||||
DeleteWork(String, Sender<Result<()>>),
|
||||
GetWorks(String, Sender<Result<Vec<Work>>>),
|
||||
UpdateEnsemble(Ensemble, Sender<Result<()>>),
|
||||
GetEnsemble(String, Sender<Result<Option<Ensemble>>>),
|
||||
DeleteEnsemble(String, Sender<Result<()>>),
|
||||
GetEnsembles(Sender<Result<Vec<Ensemble>>>),
|
||||
UpdateRecording(Recording, Sender<Result<()>>),
|
||||
DeleteRecording(String, Sender<Result<()>>),
|
||||
GetRecordingsForPerson(String, Sender<Result<Vec<Recording>>>),
|
||||
GetRecordingsForEnsemble(String, Sender<Result<Vec<Recording>>>),
|
||||
GetRecordingsForWork(String, Sender<Result<Vec<Recording>>>),
|
||||
RecordingExists(String, Sender<Result<bool>>),
|
||||
UpdateMedium(Medium, Sender<Result<()>>),
|
||||
GetMedium(String, Sender<Result<Option<Medium>>>),
|
||||
DeleteMedium(String, Sender<Result<()>>),
|
||||
GetTrackSets(String, Sender<Result<Vec<TrackSet>>>),
|
||||
pub enum Action {
|
||||
UpdatePerson(Person, Sender<DatabaseResult<()>>),
|
||||
GetPerson(String, Sender<DatabaseResult<Option<Person>>>),
|
||||
DeletePerson(String, Sender<DatabaseResult<()>>),
|
||||
GetPersons(Sender<DatabaseResult<Vec<Person>>>),
|
||||
UpdateInstrument(Instrument, Sender<DatabaseResult<()>>),
|
||||
GetInstrument(String, Sender<DatabaseResult<Option<Instrument>>>),
|
||||
DeleteInstrument(String, Sender<DatabaseResult<()>>),
|
||||
GetInstruments(Sender<DatabaseResult<Vec<Instrument>>>),
|
||||
UpdateWork(Work, Sender<DatabaseResult<()>>),
|
||||
DeleteWork(String, Sender<DatabaseResult<()>>),
|
||||
GetWorks(String, Sender<DatabaseResult<Vec<Work>>>),
|
||||
UpdateEnsemble(Ensemble, Sender<DatabaseResult<()>>),
|
||||
GetEnsemble(String, Sender<DatabaseResult<Option<Ensemble>>>),
|
||||
DeleteEnsemble(String, Sender<DatabaseResult<()>>),
|
||||
GetEnsembles(Sender<DatabaseResult<Vec<Ensemble>>>),
|
||||
UpdateRecording(Recording, Sender<DatabaseResult<()>>),
|
||||
DeleteRecording(String, Sender<DatabaseResult<()>>),
|
||||
GetRecordingsForPerson(String, Sender<DatabaseResult<Vec<Recording>>>),
|
||||
GetRecordingsForEnsemble(String, Sender<DatabaseResult<Vec<Recording>>>),
|
||||
GetRecordingsForWork(String, Sender<DatabaseResult<Vec<Recording>>>),
|
||||
RecordingExists(String, Sender<DatabaseResult<bool>>),
|
||||
UpdateMedium(Medium, Sender<DatabaseResult<()>>),
|
||||
GetMedium(String, Sender<DatabaseResult<Option<Medium>>>),
|
||||
DeleteMedium(String, Sender<DatabaseResult<()>>),
|
||||
GetTrackSets(String, Sender<DatabaseResult<Vec<TrackSet>>>),
|
||||
Stop(Sender<()>),
|
||||
}
|
||||
|
||||
|
|
@ -44,7 +43,7 @@ pub struct DbThread {
|
|||
|
||||
impl DbThread {
|
||||
/// Create a new database connection in a background thread.
|
||||
pub async fn new(path: String) -> Result<Self> {
|
||||
pub async fn new(path: String) -> DatabaseResult<Self> {
|
||||
let (action_sender, action_receiver) = mpsc::channel();
|
||||
let (ready_sender, ready_receiver) = oneshot::channel();
|
||||
|
||||
|
|
@ -150,14 +149,14 @@ impl DbThread {
|
|||
}
|
||||
|
||||
/// Update an existing person or insert a new one.
|
||||
pub async fn update_person(&self, person: Person) -> Result<()> {
|
||||
pub async fn update_person(&self, person: Person) -> DatabaseResult<()> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender.send(UpdatePerson(person, sender))?;
|
||||
receiver.await?
|
||||
}
|
||||
|
||||
/// Get an existing person.
|
||||
pub async fn get_person(&self, id: &str) -> Result<Option<Person>> {
|
||||
pub async fn get_person(&self, id: &str) -> DatabaseResult<Option<Person>> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender.send(GetPerson(id.to_string(), sender))?;
|
||||
receiver.await?
|
||||
|
|
@ -165,7 +164,7 @@ impl DbThread {
|
|||
|
||||
/// Delete an existing person. This will fail, if there are still other items referencing
|
||||
/// this person.
|
||||
pub async fn delete_person(&self, id: &str) -> Result<()> {
|
||||
pub async fn delete_person(&self, id: &str) -> DatabaseResult<()> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender
|
||||
.send(DeletePerson(id.to_string(), sender))?;
|
||||
|
|
@ -173,14 +172,14 @@ impl DbThread {
|
|||
}
|
||||
|
||||
/// Get all existing persons.
|
||||
pub async fn get_persons(&self) -> Result<Vec<Person>> {
|
||||
pub async fn get_persons(&self) -> DatabaseResult<Vec<Person>> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender.send(GetPersons(sender))?;
|
||||
receiver.await?
|
||||
}
|
||||
|
||||
/// Update an existing instrument or insert a new one.
|
||||
pub async fn update_instrument(&self, instrument: Instrument) -> Result<()> {
|
||||
pub async fn update_instrument(&self, instrument: Instrument) -> DatabaseResult<()> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender
|
||||
.send(UpdateInstrument(instrument, sender))?;
|
||||
|
|
@ -188,7 +187,7 @@ impl DbThread {
|
|||
}
|
||||
|
||||
/// Get an existing instrument.
|
||||
pub async fn get_instrument(&self, id: &str) -> Result<Option<Instrument>> {
|
||||
pub async fn get_instrument(&self, id: &str) -> DatabaseResult<Option<Instrument>> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender
|
||||
.send(GetInstrument(id.to_string(), sender))?;
|
||||
|
|
@ -197,7 +196,7 @@ impl DbThread {
|
|||
|
||||
/// Delete an existing instrument. This will fail, if there are still other items referencing
|
||||
/// this instrument.
|
||||
pub async fn delete_instrument(&self, id: &str) -> Result<()> {
|
||||
pub async fn delete_instrument(&self, id: &str) -> DatabaseResult<()> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender
|
||||
.send(DeleteInstrument(id.to_string(), sender))?;
|
||||
|
|
@ -205,14 +204,14 @@ impl DbThread {
|
|||
}
|
||||
|
||||
/// Get all existing instruments.
|
||||
pub async fn get_instruments(&self) -> Result<Vec<Instrument>> {
|
||||
pub async fn get_instruments(&self) -> DatabaseResult<Vec<Instrument>> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender.send(GetInstruments(sender))?;
|
||||
receiver.await?
|
||||
}
|
||||
|
||||
/// Update an existing work or insert a new one.
|
||||
pub async fn update_work(&self, work: Work) -> Result<()> {
|
||||
pub async fn update_work(&self, work: Work) -> DatabaseResult<()> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender.send(UpdateWork(work, sender))?;
|
||||
receiver.await?
|
||||
|
|
@ -220,7 +219,7 @@ impl DbThread {
|
|||
|
||||
/// Delete an existing work. This will fail, if there are still other items referencing
|
||||
/// this work.
|
||||
pub async fn delete_work(&self, id: &str) -> Result<()> {
|
||||
pub async fn delete_work(&self, id: &str) -> DatabaseResult<()> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender
|
||||
.send(DeleteWork(id.to_string(), sender))?;
|
||||
|
|
@ -228,7 +227,7 @@ impl DbThread {
|
|||
}
|
||||
|
||||
/// Get information on all existing works by a composer.
|
||||
pub async fn get_works(&self, person_id: &str) -> Result<Vec<Work>> {
|
||||
pub async fn get_works(&self, person_id: &str) -> DatabaseResult<Vec<Work>> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender
|
||||
.send(GetWorks(person_id.to_string(), sender))?;
|
||||
|
|
@ -236,14 +235,14 @@ impl DbThread {
|
|||
}
|
||||
|
||||
/// Update an existing ensemble or insert a new one.
|
||||
pub async fn update_ensemble(&self, ensemble: Ensemble) -> Result<()> {
|
||||
pub async fn update_ensemble(&self, ensemble: Ensemble) -> DatabaseResult<()> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender.send(UpdateEnsemble(ensemble, sender))?;
|
||||
receiver.await?
|
||||
}
|
||||
|
||||
/// Get an existing ensemble.
|
||||
pub async fn get_ensemble(&self, id: &str) -> Result<Option<Ensemble>> {
|
||||
pub async fn get_ensemble(&self, id: &str) -> DatabaseResult<Option<Ensemble>> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender
|
||||
.send(GetEnsemble(id.to_string(), sender))?;
|
||||
|
|
@ -252,7 +251,7 @@ impl DbThread {
|
|||
|
||||
/// Delete an existing ensemble. This will fail, if there are still other items referencing
|
||||
/// this ensemble.
|
||||
pub async fn delete_ensemble(&self, id: &str) -> Result<()> {
|
||||
pub async fn delete_ensemble(&self, id: &str) -> DatabaseResult<()> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender
|
||||
.send(DeleteEnsemble(id.to_string(), sender))?;
|
||||
|
|
@ -260,14 +259,14 @@ impl DbThread {
|
|||
}
|
||||
|
||||
/// Get all existing ensembles.
|
||||
pub async fn get_ensembles(&self) -> Result<Vec<Ensemble>> {
|
||||
pub async fn get_ensembles(&self) -> DatabaseResult<Vec<Ensemble>> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender.send(GetEnsembles(sender))?;
|
||||
receiver.await?
|
||||
}
|
||||
|
||||
/// Update an existing recording or insert a new one.
|
||||
pub async fn update_recording(&self, recording: Recording) -> Result<()> {
|
||||
pub async fn update_recording(&self, recording: Recording) -> DatabaseResult<()> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender
|
||||
.send(UpdateRecording(recording, sender))?;
|
||||
|
|
@ -275,7 +274,7 @@ impl DbThread {
|
|||
}
|
||||
|
||||
/// Delete an existing recording.
|
||||
pub async fn delete_recording(&self, id: &str) -> Result<()> {
|
||||
pub async fn delete_recording(&self, id: &str) -> DatabaseResult<()> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender
|
||||
.send(DeleteRecording(id.to_string(), sender))?;
|
||||
|
|
@ -283,7 +282,7 @@ impl DbThread {
|
|||
}
|
||||
|
||||
/// Get information on all recordings in which a person performs.
|
||||
pub async fn get_recordings_for_person(&self, person_id: &str) -> Result<Vec<Recording>> {
|
||||
pub async fn get_recordings_for_person(&self, person_id: &str) -> DatabaseResult<Vec<Recording>> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender
|
||||
.send(GetRecordingsForPerson(person_id.to_string(), sender))?;
|
||||
|
|
@ -291,7 +290,7 @@ impl DbThread {
|
|||
}
|
||||
|
||||
/// Get information on all recordings in which an ensemble performs.
|
||||
pub async fn get_recordings_for_ensemble(&self, ensemble_id: &str) -> Result<Vec<Recording>> {
|
||||
pub async fn get_recordings_for_ensemble(&self, ensemble_id: &str) -> DatabaseResult<Vec<Recording>> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender
|
||||
.send(GetRecordingsForEnsemble(ensemble_id.to_string(), sender))?;
|
||||
|
|
@ -299,7 +298,7 @@ impl DbThread {
|
|||
}
|
||||
|
||||
/// Get information on all recordings of a work.
|
||||
pub async fn get_recordings_for_work(&self, work_id: &str) -> Result<Vec<Recording>> {
|
||||
pub async fn get_recordings_for_work(&self, work_id: &str) -> DatabaseResult<Vec<Recording>> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender
|
||||
.send(GetRecordingsForWork(work_id.to_string(), sender))?;
|
||||
|
|
@ -307,7 +306,7 @@ impl DbThread {
|
|||
}
|
||||
|
||||
/// Check whether a recording exists within the database.
|
||||
pub async fn recording_exists(&self, id: &str) -> Result<bool> {
|
||||
pub async fn recording_exists(&self, id: &str) -> DatabaseResult<bool> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender
|
||||
.send(RecordingExists(id.to_string(), sender))?;
|
||||
|
|
@ -315,7 +314,7 @@ impl DbThread {
|
|||
}
|
||||
|
||||
/// Update an existing medium or insert a new one.
|
||||
pub async fn update_medium(&self, medium: Medium) -> Result<()> {
|
||||
pub async fn update_medium(&self, medium: Medium) -> DatabaseResult<()> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender.send(UpdateMedium(medium, sender))?;
|
||||
receiver.await?
|
||||
|
|
@ -323,7 +322,7 @@ impl DbThread {
|
|||
|
||||
/// Delete an existing medium. This will fail, if there are still other
|
||||
/// items referencing this medium.
|
||||
pub async fn delete_medium(&self, id: &str) -> Result<()> {
|
||||
pub async fn delete_medium(&self, id: &str) -> DatabaseResult<()> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
|
||||
self.action_sender
|
||||
|
|
@ -333,21 +332,21 @@ impl DbThread {
|
|||
}
|
||||
|
||||
/// Get an existing medium.
|
||||
pub async fn get_medium(&self, id: &str) -> Result<Option<Medium>> {
|
||||
pub async fn get_medium(&self, id: &str) -> DatabaseResult<Option<Medium>> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender.send(GetMedium(id.to_owned(), sender))?;
|
||||
receiver.await?
|
||||
}
|
||||
|
||||
/// Get all track sets for a recording.
|
||||
pub async fn get_track_sets(&self, recording_id: &str) -> Result<Vec<TrackSet>> {
|
||||
pub async fn get_track_sets(&self, recording_id: &str) -> DatabaseResult<Vec<TrackSet>> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender.send(GetTrackSets(recording_id.to_owned(), sender))?;
|
||||
receiver.await?
|
||||
}
|
||||
|
||||
/// Stop the database thread. Any future access to the database will fail.
|
||||
pub async fn stop(&self) -> Result<()> {
|
||||
pub async fn stop(&self) -> DatabaseResult<()> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.action_sender.send(Stop(sender))?;
|
||||
Ok(receiver.await?)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use super::generate_id;
|
||||
use super::schema::{instrumentations, work_parts, work_sections, works};
|
||||
use super::{Database, Instrument, Person};
|
||||
use anyhow::{anyhow, Error, Result};
|
||||
use super::{Database, DatabaseError, Instrument, Person, DatabaseResult};
|
||||
use diesel::prelude::*;
|
||||
use diesel::{Insertable, Queryable};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -106,10 +105,10 @@ impl Work {
|
|||
impl Database {
|
||||
/// Update an existing work or insert a new one.
|
||||
// TODO: Think about also inserting related items.
|
||||
pub fn update_work(&self, work: Work) -> Result<()> {
|
||||
pub fn update_work(&self, work: Work) -> DatabaseResult<()> {
|
||||
self.defer_foreign_keys()?;
|
||||
|
||||
self.connection.transaction::<(), Error, _>(|| {
|
||||
self.connection.transaction::<(), DatabaseError, _>(|| {
|
||||
let work_id = &work.id;
|
||||
self.delete_work(work_id)?;
|
||||
|
||||
|
|
@ -163,7 +162,7 @@ impl Database {
|
|||
let row = WorkPartRow {
|
||||
id: rand::random(),
|
||||
work: work_id.to_string(),
|
||||
part_index: index.try_into()?,
|
||||
part_index: index as i64,
|
||||
title: part.title,
|
||||
composer: part.composer.map(|person| person.id),
|
||||
};
|
||||
|
|
@ -178,7 +177,7 @@ impl Database {
|
|||
id: rand::random(),
|
||||
work: work_id.to_string(),
|
||||
title: section.title,
|
||||
before_index: section.before_index.try_into()?,
|
||||
before_index: section.before_index as i64,
|
||||
};
|
||||
|
||||
diesel::insert_into(work_sections::table)
|
||||
|
|
@ -195,7 +194,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Get an existing work.
|
||||
pub fn get_work(&self, id: &str) -> Result<Option<Work>> {
|
||||
pub fn get_work(&self, id: &str) -> DatabaseResult<Option<Work>> {
|
||||
let row = works::table
|
||||
.filter(works::id.eq(id))
|
||||
.load::<WorkRow>(&self.connection)?
|
||||
|
|
@ -211,7 +210,7 @@ impl Database {
|
|||
}
|
||||
|
||||
/// Retrieve all available information on a work from related tables.
|
||||
fn get_work_data(&self, row: WorkRow) -> Result<Work> {
|
||||
fn get_work_data(&self, row: WorkRow) -> DatabaseResult<Work> {
|
||||
let mut instruments: Vec<Instrument> = Vec::new();
|
||||
|
||||
let instrumentations = instrumentations::table
|
||||
|
|
@ -222,7 +221,11 @@ impl Database {
|
|||
let id = &instrumentation.instrument;
|
||||
instruments.push(
|
||||
self.get_instrument(id)?
|
||||
.ok_or(anyhow!("No instrument with ID: {}", id))?,
|
||||
.ok_or(DatabaseError::Other(format!(
|
||||
"Failed to get instrument ({}) for work ({}).",
|
||||
id,
|
||||
row.id,
|
||||
)))?
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -238,7 +241,11 @@ impl Database {
|
|||
composer: match part_row.composer {
|
||||
Some(composer) => Some(
|
||||
self.get_person(&composer)?
|
||||
.ok_or(anyhow!("No person with ID: {}", composer))?,
|
||||
.ok_or(DatabaseError::Other(format!(
|
||||
"Failed to get person ({}) for work ({}).",
|
||||
composer,
|
||||
row.id,
|
||||
)))?
|
||||
),
|
||||
None => None,
|
||||
},
|
||||
|
|
@ -254,14 +261,18 @@ impl Database {
|
|||
for section_row in section_rows {
|
||||
sections.push(WorkSection {
|
||||
title: section_row.title,
|
||||
before_index: section_row.before_index.try_into()?,
|
||||
before_index: section_row.before_index as usize,
|
||||
});
|
||||
}
|
||||
|
||||
let person_id = &row.composer;
|
||||
let person = self
|
||||
.get_person(person_id)?
|
||||
.ok_or(anyhow!("Person doesn't exist: {}", person_id))?;
|
||||
.ok_or(DatabaseError::Other(format!(
|
||||
"Failed to get person ({}) for work ({}).",
|
||||
person_id,
|
||||
row.id,
|
||||
)))?;
|
||||
|
||||
Ok(Work {
|
||||
id: row.id,
|
||||
|
|
@ -275,13 +286,13 @@ impl Database {
|
|||
|
||||
/// Delete an existing work. This will fail if there are still other tables that relate to
|
||||
/// this work except for the things that are part of the information on the work it
|
||||
pub fn delete_work(&self, id: &str) -> Result<()> {
|
||||
pub fn delete_work(&self, id: &str) -> DatabaseResult<()> {
|
||||
diesel::delete(works::table.filter(works::id.eq(id))).execute(&self.connection)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get all existing works by a composer and related information from other tables.
|
||||
pub fn get_works(&self, composer_id: &str) -> Result<Vec<Work>> {
|
||||
pub fn get_works(&self, composer_id: &str) -> DatabaseResult<Vec<Work>> {
|
||||
let mut works: Vec<Work> = Vec::new();
|
||||
|
||||
let rows = works::table
|
||||
|
|
|
|||
55
src/backend/error.rs
Normal file
55
src/backend/error.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
use isahc::http::StatusCode;
|
||||
|
||||
/// An error that can happen within the backend.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("The users login credentials were wrong.")]
|
||||
LoginFailed,
|
||||
|
||||
#[error("The user has to be logged in to perform this action.")]
|
||||
Unauthorized,
|
||||
|
||||
#[error("The user is not allowed to perform this action.")]
|
||||
Forbidden,
|
||||
|
||||
#[error("The server returned an unexpected status code: {0}.")]
|
||||
UnexpectedResponse(StatusCode),
|
||||
|
||||
#[error("A networking error happened.")]
|
||||
NetworkError(#[from] isahc::Error),
|
||||
|
||||
#[error("A networking error happened.")]
|
||||
HttpError(#[from] isahc::http::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
DatabaseError(#[from] crate::backend::DatabaseError),
|
||||
|
||||
#[error("An IO error happened.")]
|
||||
IoError(#[from] std::io::Error),
|
||||
|
||||
#[error("An error happened using the SecretService.")]
|
||||
SecretServiceError(#[from] secret_service::Error),
|
||||
|
||||
#[error("An error happened while serializing or deserializing.")]
|
||||
SerdeError(#[from] serde_json::Error),
|
||||
|
||||
#[error("An error happened in GLib.")]
|
||||
GlibError(#[from] glib::BoolError),
|
||||
|
||||
#[error("A channel was canceled.")]
|
||||
ChannelError(#[from] futures_channel::oneshot::Canceled),
|
||||
|
||||
#[error("Error decoding to UTF8.")]
|
||||
Utf8Error(#[from] std::str::Utf8Error),
|
||||
|
||||
#[error("An error happened: {0}")]
|
||||
Other(&'static str),
|
||||
|
||||
// TODO: Remove this once anyhow has been dropped as a dependency.
|
||||
#[error("An unkown error happened.")]
|
||||
Unknown(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
|
|
@ -42,6 +42,7 @@ sources = files(
|
|||
'backend/client/register.rs',
|
||||
'backend/client/works.rs',
|
||||
'backend/database/ensembles.rs',
|
||||
'backend/database/error.rs',
|
||||
'backend/database/instruments.rs',
|
||||
'backend/database/medium.rs',
|
||||
'backend/database/mod.rs',
|
||||
|
|
@ -50,6 +51,7 @@ sources = files(
|
|||
'backend/database/schema.rs',
|
||||
'backend/database/thread.rs',
|
||||
'backend/database/works.rs',
|
||||
'backend/error.rs',
|
||||
'backend/library.rs',
|
||||
'backend/mod.rs',
|
||||
'backend/player.rs',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue