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::schema::ensembles; | ||||||
| use super::Database; | use super::{Database, DatabaseResult}; | ||||||
| use anyhow::Result; |  | ||||||
| use diesel::prelude::*; | use diesel::prelude::*; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| 
 | 
 | ||||||
|  | @ -14,7 +13,7 @@ pub struct Ensemble { | ||||||
| 
 | 
 | ||||||
| impl Database { | impl Database { | ||||||
|     /// Update an existing ensemble or insert a new one.
 |     /// 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.defer_foreign_keys()?; | ||||||
| 
 | 
 | ||||||
|         self.connection.transaction(|| { |         self.connection.transaction(|| { | ||||||
|  | @ -27,7 +26,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get an existing ensemble.
 |     /// 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 |         let ensemble = ensembles::table | ||||||
|             .filter(ensembles::id.eq(id)) |             .filter(ensembles::id.eq(id)) | ||||||
|             .load::<Ensemble>(&self.connection)? |             .load::<Ensemble>(&self.connection)? | ||||||
|  | @ -38,14 +37,14 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Delete an existing ensemble.
 |     /// 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)?; |         diesel::delete(ensembles::table.filter(ensembles::id.eq(id))).execute(&self.connection)?; | ||||||
| 
 | 
 | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get all existing ensembles.
 |     /// 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)?; |         let ensembles = ensembles::table.load::<Ensemble>(&self.connection)?; | ||||||
| 
 | 
 | ||||||
|         Ok(ensembles) |         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::schema::instruments; | ||||||
| use super::Database; | use super::{Database, DatabaseResult}; | ||||||
| use anyhow::Result; |  | ||||||
| use diesel::prelude::*; | use diesel::prelude::*; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| 
 | 
 | ||||||
|  | @ -14,7 +13,7 @@ pub struct Instrument { | ||||||
| 
 | 
 | ||||||
| impl Database { | impl Database { | ||||||
|     /// Update an existing instrument or insert a new one.
 |     /// 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.defer_foreign_keys()?; | ||||||
| 
 | 
 | ||||||
|         self.connection.transaction(|| { |         self.connection.transaction(|| { | ||||||
|  | @ -27,7 +26,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get an existing instrument.
 |     /// 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 |         let instrument = instruments::table | ||||||
|             .filter(instruments::id.eq(id)) |             .filter(instruments::id.eq(id)) | ||||||
|             .load::<Instrument>(&self.connection)? |             .load::<Instrument>(&self.connection)? | ||||||
|  | @ -38,7 +37,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Delete an existing instrument.
 |     /// 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))) |         diesel::delete(instruments::table.filter(instruments::id.eq(id))) | ||||||
|             .execute(&self.connection)?; |             .execute(&self.connection)?; | ||||||
| 
 | 
 | ||||||
|  | @ -46,7 +45,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get all existing instruments.
 |     /// 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)?; |         let instruments = instruments::table.load::<Instrument>(&self.connection)?; | ||||||
| 
 | 
 | ||||||
|         Ok(instruments) |         Ok(instruments) | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| use super::generate_id; | use super::generate_id; | ||||||
| use super::schema::{mediums, recordings, track_sets, tracks}; | use super::schema::{mediums, recordings, track_sets, tracks}; | ||||||
| use super::{Database, Recording}; | use super::{Database, DatabaseError, Recording, DatabaseResult}; | ||||||
| use anyhow::{anyhow, Error, Result}; |  | ||||||
| use diesel::prelude::*; | use diesel::prelude::*; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| 
 | 
 | ||||||
|  | @ -80,10 +79,10 @@ struct TrackRow { | ||||||
| 
 | 
 | ||||||
| impl Database { | impl Database { | ||||||
|     /// Update an existing medium or insert a new one.
 |     /// 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.defer_foreign_keys()?; | ||||||
| 
 | 
 | ||||||
|         self.connection.transaction::<(), Error, _>(|| { |         self.connection.transaction::<(), DatabaseError, _>(|| { | ||||||
|             let medium_id = &medium.id; |             let medium_id = &medium.id; | ||||||
| 
 | 
 | ||||||
|             // This will also delete the track sets and tracks.
 |             // This will also delete the track sets and tracks.
 | ||||||
|  | @ -153,7 +152,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get an existing medium.
 |     /// 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 |         let row = mediums::table | ||||||
|             .filter(mediums::id.eq(id)) |             .filter(mediums::id.eq(id)) | ||||||
|             .load::<MediumRow>(&self.connection)? |             .load::<MediumRow>(&self.connection)? | ||||||
|  | @ -170,13 +169,13 @@ impl Database { | ||||||
| 
 | 
 | ||||||
|     /// Delete a medium and all of its tracks. This will fail, if the music
 |     /// Delete a medium and all of its tracks. This will fail, if the music
 | ||||||
|     /// library contains audio files referencing any of those tracks.
 |     /// 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)?; |         diesel::delete(mediums::table.filter(mediums::id.eq(id))).execute(&self.connection)?; | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get all available track sets for a recording.
 |     /// 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 mut track_sets: Vec<TrackSet> = Vec::new(); | ||||||
| 
 | 
 | ||||||
|         let rows = track_sets::table |         let rows = track_sets::table | ||||||
|  | @ -194,7 +193,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Retrieve all available information on a medium from related tables.
 |     /// 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 |         let track_set_rows = track_sets::table | ||||||
|             .filter(track_sets::medium.eq(&row.id)) |             .filter(track_sets::medium.eq(&row.id)) | ||||||
|             .order_by(track_sets::index) |             .order_by(track_sets::index) | ||||||
|  | @ -218,12 +217,16 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Convert a track set row from the database to an actual track set.
 |     /// 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_id = row.recording; | ||||||
| 
 | 
 | ||||||
|         let recording = self |         let recording = self | ||||||
|             .get_recording(&recording_id)? |             .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 |         let track_rows = tracks::table | ||||||
|             .filter(tracks::track_set.eq(row.id)) |             .filter(tracks::track_set.eq(row.id)) | ||||||
|  | @ -236,8 +239,14 @@ impl Database { | ||||||
|             let work_parts = track_row |             let work_parts = track_row | ||||||
|                 .work_parts |                 .work_parts | ||||||
|                 .split(',') |                 .split(',') | ||||||
|                 .map(|part_index| Ok(str::parse(part_index)?)) |                 .map(|part_index| { | ||||||
|                 .collect::<Result<Vec<usize>>>()?; |                     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 { |             let track = Track { | ||||||
|                 work_parts, |                 work_parts, | ||||||
|  |  | ||||||
|  | @ -1,9 +1,11 @@ | ||||||
| use anyhow::Result; |  | ||||||
| use diesel::prelude::*; | use diesel::prelude::*; | ||||||
| 
 | 
 | ||||||
| pub mod ensembles; | pub mod ensembles; | ||||||
| pub use ensembles::*; | pub use ensembles::*; | ||||||
| 
 | 
 | ||||||
|  | pub mod error; | ||||||
|  | pub use error::*; | ||||||
|  | 
 | ||||||
| pub mod instruments; | pub mod instruments; | ||||||
| pub use instruments::*; | pub use instruments::*; | ||||||
| 
 | 
 | ||||||
|  | @ -42,7 +44,7 @@ pub struct Database { | ||||||
| 
 | 
 | ||||||
| impl Database { | impl Database { | ||||||
|     /// Create a new database interface and run migrations if necessary.
 |     /// 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)?; |         let connection = SqliteConnection::establish(file_name)?; | ||||||
| 
 | 
 | ||||||
|         diesel::sql_query("PRAGMA foreign_keys = ON").execute(&connection)?; |         diesel::sql_query("PRAGMA foreign_keys = ON").execute(&connection)?; | ||||||
|  | @ -52,7 +54,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Defer all foreign keys for the next transaction.
 |     /// 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)?; |         diesel::sql_query("PRAGMA defer_foreign_keys = ON").execute(&self.connection)?; | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,5 @@ | ||||||
| use super::schema::persons; | use super::schema::persons; | ||||||
| use super::Database; | use super::{Database, DatabaseResult}; | ||||||
| use anyhow::Result; |  | ||||||
| use diesel::prelude::*; | use diesel::prelude::*; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| 
 | 
 | ||||||
|  | @ -27,7 +26,7 @@ impl Person { | ||||||
| 
 | 
 | ||||||
| impl Database { | impl Database { | ||||||
|     /// Update an existing person or insert a new one.
 |     /// 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.defer_foreign_keys()?; | ||||||
| 
 | 
 | ||||||
|         self.connection.transaction(|| { |         self.connection.transaction(|| { | ||||||
|  | @ -40,7 +39,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get an existing person.
 |     /// 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 |         let person = persons::table | ||||||
|             .filter(persons::id.eq(id)) |             .filter(persons::id.eq(id)) | ||||||
|             .load::<Person>(&self.connection)? |             .load::<Person>(&self.connection)? | ||||||
|  | @ -51,14 +50,14 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Delete an existing person.
 |     /// 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)?; |         diesel::delete(persons::table.filter(persons::id.eq(id))).execute(&self.connection)?; | ||||||
| 
 | 
 | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get all existing persons.
 |     /// 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)?; |         let persons = persons::table.load::<Person>(&self.connection)?; | ||||||
| 
 | 
 | ||||||
|         Ok(persons) |         Ok(persons) | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| use super::generate_id; | use super::generate_id; | ||||||
| use super::schema::{ensembles, performances, persons, recordings}; | use super::schema::{ensembles, performances, persons, recordings}; | ||||||
| use super::{Database, Ensemble, Instrument, Person, Work}; | use super::{Database, Ensemble, DatabaseError, Instrument, Person, DatabaseResult, Work}; | ||||||
| use anyhow::{anyhow, Error, Result}; |  | ||||||
| use diesel::prelude::*; | use diesel::prelude::*; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| 
 | 
 | ||||||
|  | @ -120,9 +119,9 @@ impl Recording { | ||||||
| impl Database { | impl Database { | ||||||
|     /// Update an existing recording or insert a new one.
 |     /// Update an existing recording or insert a new one.
 | ||||||
|     // TODO: Think about whether to also insert the other items.
 |     // 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.defer_foreign_keys()?; | ||||||
|         self.connection.transaction::<(), Error, _>(|| { |         self.connection.transaction::<(), DatabaseError, _>(|| { | ||||||
|             let recording_id = &recording.id; |             let recording_id = &recording.id; | ||||||
|             self.delete_recording(recording_id)?; |             self.delete_recording(recording_id)?; | ||||||
| 
 | 
 | ||||||
|  | @ -180,7 +179,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Check whether the database contains a recording.
 |     /// 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 |         let exists = recordings::table | ||||||
|             .filter(recordings::id.eq(id)) |             .filter(recordings::id.eq(id)) | ||||||
|             .load::<RecordingRow>(&self.connection)? |             .load::<RecordingRow>(&self.connection)? | ||||||
|  | @ -191,7 +190,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get an existing recording.
 |     /// 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 |         let row = recordings::table | ||||||
|             .filter(recordings::id.eq(id)) |             .filter(recordings::id.eq(id)) | ||||||
|             .load::<RecordingRow>(&self.connection)? |             .load::<RecordingRow>(&self.connection)? | ||||||
|  | @ -207,7 +206,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Retrieve all available information on a recording from related tables.
 |     /// 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 mut performance_descriptions: Vec<Performance> = Vec::new(); | ||||||
| 
 | 
 | ||||||
|         let performance_rows = performances::table |         let performance_rows = performances::table | ||||||
|  | @ -219,21 +218,33 @@ impl Database { | ||||||
|                 person: match row.person { |                 person: match row.person { | ||||||
|                     Some(id) => Some( |                     Some(id) => Some( | ||||||
|                         self.get_person(&id)? |                         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, |                     None => None, | ||||||
|                 }, |                 }, | ||||||
|                 ensemble: match row.ensemble { |                 ensemble: match row.ensemble { | ||||||
|                     Some(id) => Some( |                     Some(id) => Some( | ||||||
|                         self.get_ensemble(&id)? |                         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, |                     None => None, | ||||||
|                 }, |                 }, | ||||||
|                 role: match row.role { |                 role: match row.role { | ||||||
|                     Some(id) => Some( |                     Some(id) => Some( | ||||||
|                         self.get_instrument(&id)? |                         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, |                     None => None, | ||||||
|                 }, |                 }, | ||||||
|  | @ -243,7 +254,11 @@ impl Database { | ||||||
|         let work_id = &row.work; |         let work_id = &row.work; | ||||||
|         let work = self |         let work = self | ||||||
|             .get_work(work_id)? |             .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 { |         let recording_description = Recording { | ||||||
|             id: row.id, |             id: row.id, | ||||||
|  | @ -256,7 +271,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get all available information on all recordings where a person is performing.
 |     /// 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 mut recordings: Vec<Recording> = Vec::new(); | ||||||
| 
 | 
 | ||||||
|         let rows = recordings::table |         let rows = recordings::table | ||||||
|  | @ -274,7 +289,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get all available information on all recordings where an ensemble is performing.
 |     /// 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 mut recordings: Vec<Recording> = Vec::new(); | ||||||
| 
 | 
 | ||||||
|         let rows = recordings::table |         let rows = recordings::table | ||||||
|  | @ -292,7 +307,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get allavailable information on all recordings of a work.
 |     /// 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 mut recordings: Vec<Recording> = Vec::new(); | ||||||
| 
 | 
 | ||||||
|         let rows = recordings::table |         let rows = recordings::table | ||||||
|  | @ -308,7 +323,7 @@ impl Database { | ||||||
| 
 | 
 | ||||||
|     /// Delete an existing recording. This will fail if there are still references to this
 |     /// 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.
 |     /// 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))) |         diesel::delete(recordings::table.filter(recordings::id.eq(id))) | ||||||
|             .execute(&self.connection)?; |             .execute(&self.connection)?; | ||||||
|         Ok(()) |         Ok(()) | ||||||
|  |  | ||||||
|  | @ -1,37 +1,36 @@ | ||||||
| use super::*; | use super::*; | ||||||
| use anyhow::Result; |  | ||||||
| use futures_channel::oneshot; | use futures_channel::oneshot; | ||||||
| use futures_channel::oneshot::Sender; | use futures_channel::oneshot::Sender; | ||||||
| use std::sync::mpsc; | use std::sync::mpsc; | ||||||
| use std::thread; | use std::thread; | ||||||
| 
 | 
 | ||||||
| /// An action the database thread can perform.
 | /// An action the database thread can perform.
 | ||||||
| enum Action { | pub enum Action { | ||||||
|     UpdatePerson(Person, Sender<Result<()>>), |     UpdatePerson(Person, Sender<DatabaseResult<()>>), | ||||||
|     GetPerson(String, Sender<Result<Option<Person>>>), |     GetPerson(String, Sender<DatabaseResult<Option<Person>>>), | ||||||
|     DeletePerson(String, Sender<Result<()>>), |     DeletePerson(String, Sender<DatabaseResult<()>>), | ||||||
|     GetPersons(Sender<Result<Vec<Person>>>), |     GetPersons(Sender<DatabaseResult<Vec<Person>>>), | ||||||
|     UpdateInstrument(Instrument, Sender<Result<()>>), |     UpdateInstrument(Instrument, Sender<DatabaseResult<()>>), | ||||||
|     GetInstrument(String, Sender<Result<Option<Instrument>>>), |     GetInstrument(String, Sender<DatabaseResult<Option<Instrument>>>), | ||||||
|     DeleteInstrument(String, Sender<Result<()>>), |     DeleteInstrument(String, Sender<DatabaseResult<()>>), | ||||||
|     GetInstruments(Sender<Result<Vec<Instrument>>>), |     GetInstruments(Sender<DatabaseResult<Vec<Instrument>>>), | ||||||
|     UpdateWork(Work, Sender<Result<()>>), |     UpdateWork(Work, Sender<DatabaseResult<()>>), | ||||||
|     DeleteWork(String, Sender<Result<()>>), |     DeleteWork(String, Sender<DatabaseResult<()>>), | ||||||
|     GetWorks(String, Sender<Result<Vec<Work>>>), |     GetWorks(String, Sender<DatabaseResult<Vec<Work>>>), | ||||||
|     UpdateEnsemble(Ensemble, Sender<Result<()>>), |     UpdateEnsemble(Ensemble, Sender<DatabaseResult<()>>), | ||||||
|     GetEnsemble(String, Sender<Result<Option<Ensemble>>>), |     GetEnsemble(String, Sender<DatabaseResult<Option<Ensemble>>>), | ||||||
|     DeleteEnsemble(String, Sender<Result<()>>), |     DeleteEnsemble(String, Sender<DatabaseResult<()>>), | ||||||
|     GetEnsembles(Sender<Result<Vec<Ensemble>>>), |     GetEnsembles(Sender<DatabaseResult<Vec<Ensemble>>>), | ||||||
|     UpdateRecording(Recording, Sender<Result<()>>), |     UpdateRecording(Recording, Sender<DatabaseResult<()>>), | ||||||
|     DeleteRecording(String, Sender<Result<()>>), |     DeleteRecording(String, Sender<DatabaseResult<()>>), | ||||||
|     GetRecordingsForPerson(String, Sender<Result<Vec<Recording>>>), |     GetRecordingsForPerson(String, Sender<DatabaseResult<Vec<Recording>>>), | ||||||
|     GetRecordingsForEnsemble(String, Sender<Result<Vec<Recording>>>), |     GetRecordingsForEnsemble(String, Sender<DatabaseResult<Vec<Recording>>>), | ||||||
|     GetRecordingsForWork(String, Sender<Result<Vec<Recording>>>), |     GetRecordingsForWork(String, Sender<DatabaseResult<Vec<Recording>>>), | ||||||
|     RecordingExists(String, Sender<Result<bool>>), |     RecordingExists(String, Sender<DatabaseResult<bool>>), | ||||||
|     UpdateMedium(Medium, Sender<Result<()>>), |     UpdateMedium(Medium, Sender<DatabaseResult<()>>), | ||||||
|     GetMedium(String, Sender<Result<Option<Medium>>>), |     GetMedium(String, Sender<DatabaseResult<Option<Medium>>>), | ||||||
|     DeleteMedium(String, Sender<Result<()>>), |     DeleteMedium(String, Sender<DatabaseResult<()>>), | ||||||
|     GetTrackSets(String, Sender<Result<Vec<TrackSet>>>), |     GetTrackSets(String, Sender<DatabaseResult<Vec<TrackSet>>>), | ||||||
|     Stop(Sender<()>), |     Stop(Sender<()>), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -44,7 +43,7 @@ pub struct DbThread { | ||||||
| 
 | 
 | ||||||
| impl DbThread { | impl DbThread { | ||||||
|     /// Create a new database connection in a background thread.
 |     /// 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 (action_sender, action_receiver) = mpsc::channel(); | ||||||
|         let (ready_sender, ready_receiver) = oneshot::channel(); |         let (ready_sender, ready_receiver) = oneshot::channel(); | ||||||
| 
 | 
 | ||||||
|  | @ -150,14 +149,14 @@ impl DbThread { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Update an existing person or insert a new one.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender.send(UpdatePerson(person, sender))?; |         self.action_sender.send(UpdatePerson(person, sender))?; | ||||||
|         receiver.await? |         receiver.await? | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get an existing person.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender.send(GetPerson(id.to_string(), sender))?; |         self.action_sender.send(GetPerson(id.to_string(), sender))?; | ||||||
|         receiver.await? |         receiver.await? | ||||||
|  | @ -165,7 +164,7 @@ impl DbThread { | ||||||
| 
 | 
 | ||||||
|     /// Delete an existing person. This will fail, if there are still other items referencing
 |     /// Delete an existing person. This will fail, if there are still other items referencing
 | ||||||
|     /// this person.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender |         self.action_sender | ||||||
|             .send(DeletePerson(id.to_string(), sender))?; |             .send(DeletePerson(id.to_string(), sender))?; | ||||||
|  | @ -173,14 +172,14 @@ impl DbThread { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get all existing persons.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender.send(GetPersons(sender))?; |         self.action_sender.send(GetPersons(sender))?; | ||||||
|         receiver.await? |         receiver.await? | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Update an existing instrument or insert a new one.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender |         self.action_sender | ||||||
|             .send(UpdateInstrument(instrument, sender))?; |             .send(UpdateInstrument(instrument, sender))?; | ||||||
|  | @ -188,7 +187,7 @@ impl DbThread { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get an existing instrument.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender |         self.action_sender | ||||||
|             .send(GetInstrument(id.to_string(), 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
 |     /// Delete an existing instrument. This will fail, if there are still other items referencing
 | ||||||
|     /// this instrument.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender |         self.action_sender | ||||||
|             .send(DeleteInstrument(id.to_string(), sender))?; |             .send(DeleteInstrument(id.to_string(), sender))?; | ||||||
|  | @ -205,14 +204,14 @@ impl DbThread { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get all existing instruments.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender.send(GetInstruments(sender))?; |         self.action_sender.send(GetInstruments(sender))?; | ||||||
|         receiver.await? |         receiver.await? | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Update an existing work or insert a new one.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender.send(UpdateWork(work, sender))?; |         self.action_sender.send(UpdateWork(work, sender))?; | ||||||
|         receiver.await? |         receiver.await? | ||||||
|  | @ -220,7 +219,7 @@ impl DbThread { | ||||||
| 
 | 
 | ||||||
|     /// Delete an existing work. This will fail, if there are still other items referencing
 |     /// Delete an existing work. This will fail, if there are still other items referencing
 | ||||||
|     /// this work.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender |         self.action_sender | ||||||
|             .send(DeleteWork(id.to_string(), sender))?; |             .send(DeleteWork(id.to_string(), sender))?; | ||||||
|  | @ -228,7 +227,7 @@ impl DbThread { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get information on all existing works by a composer.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender |         self.action_sender | ||||||
|             .send(GetWorks(person_id.to_string(), sender))?; |             .send(GetWorks(person_id.to_string(), sender))?; | ||||||
|  | @ -236,14 +235,14 @@ impl DbThread { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Update an existing ensemble or insert a new one.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender.send(UpdateEnsemble(ensemble, sender))?; |         self.action_sender.send(UpdateEnsemble(ensemble, sender))?; | ||||||
|         receiver.await? |         receiver.await? | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get an existing ensemble.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender |         self.action_sender | ||||||
|             .send(GetEnsemble(id.to_string(), 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
 |     /// Delete an existing ensemble. This will fail, if there are still other items referencing
 | ||||||
|     /// this ensemble.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender |         self.action_sender | ||||||
|             .send(DeleteEnsemble(id.to_string(), sender))?; |             .send(DeleteEnsemble(id.to_string(), sender))?; | ||||||
|  | @ -260,14 +259,14 @@ impl DbThread { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get all existing ensembles.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender.send(GetEnsembles(sender))?; |         self.action_sender.send(GetEnsembles(sender))?; | ||||||
|         receiver.await? |         receiver.await? | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Update an existing recording or insert a new one.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender |         self.action_sender | ||||||
|             .send(UpdateRecording(recording, sender))?; |             .send(UpdateRecording(recording, sender))?; | ||||||
|  | @ -275,7 +274,7 @@ impl DbThread { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Delete an existing recording.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender |         self.action_sender | ||||||
|             .send(DeleteRecording(id.to_string(), sender))?; |             .send(DeleteRecording(id.to_string(), sender))?; | ||||||
|  | @ -283,7 +282,7 @@ impl DbThread { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get information on all recordings in which a person performs.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender |         self.action_sender | ||||||
|             .send(GetRecordingsForPerson(person_id.to_string(), sender))?; |             .send(GetRecordingsForPerson(person_id.to_string(), sender))?; | ||||||
|  | @ -291,7 +290,7 @@ impl DbThread { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get information on all recordings in which an ensemble performs.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender |         self.action_sender | ||||||
|             .send(GetRecordingsForEnsemble(ensemble_id.to_string(), sender))?; |             .send(GetRecordingsForEnsemble(ensemble_id.to_string(), sender))?; | ||||||
|  | @ -299,7 +298,7 @@ impl DbThread { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get information on all recordings of a work.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender |         self.action_sender | ||||||
|             .send(GetRecordingsForWork(work_id.to_string(), sender))?; |             .send(GetRecordingsForWork(work_id.to_string(), sender))?; | ||||||
|  | @ -307,7 +306,7 @@ impl DbThread { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Check whether a recording exists within the database.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender |         self.action_sender | ||||||
|             .send(RecordingExists(id.to_string(), sender))?; |             .send(RecordingExists(id.to_string(), sender))?; | ||||||
|  | @ -315,7 +314,7 @@ impl DbThread { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Update an existing medium or insert a new one.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender.send(UpdateMedium(medium, sender))?; |         self.action_sender.send(UpdateMedium(medium, sender))?; | ||||||
|         receiver.await? |         receiver.await? | ||||||
|  | @ -323,7 +322,7 @@ impl DbThread { | ||||||
| 
 | 
 | ||||||
|     /// Delete an existing medium. This will fail, if there are still other
 |     /// Delete an existing medium. This will fail, if there are still other
 | ||||||
|     /// items referencing this medium.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
| 
 | 
 | ||||||
|         self.action_sender |         self.action_sender | ||||||
|  | @ -333,21 +332,21 @@ impl DbThread { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get an existing medium.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender.send(GetMedium(id.to_owned(), sender))?; |         self.action_sender.send(GetMedium(id.to_owned(), sender))?; | ||||||
|         receiver.await? |         receiver.await? | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get all track sets for a recording.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender.send(GetTrackSets(recording_id.to_owned(), sender))?; |         self.action_sender.send(GetTrackSets(recording_id.to_owned(), sender))?; | ||||||
|         receiver.await? |         receiver.await? | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Stop the database thread. Any future access to the database will fail.
 |     /// 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(); |         let (sender, receiver) = oneshot::channel(); | ||||||
|         self.action_sender.send(Stop(sender))?; |         self.action_sender.send(Stop(sender))?; | ||||||
|         Ok(receiver.await?) |         Ok(receiver.await?) | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| use super::generate_id; | use super::generate_id; | ||||||
| use super::schema::{instrumentations, work_parts, work_sections, works}; | use super::schema::{instrumentations, work_parts, work_sections, works}; | ||||||
| use super::{Database, Instrument, Person}; | use super::{Database, DatabaseError, Instrument, Person, DatabaseResult}; | ||||||
| use anyhow::{anyhow, Error, Result}; |  | ||||||
| use diesel::prelude::*; | use diesel::prelude::*; | ||||||
| use diesel::{Insertable, Queryable}; | use diesel::{Insertable, Queryable}; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
|  | @ -106,10 +105,10 @@ impl Work { | ||||||
| impl Database { | impl Database { | ||||||
|     /// Update an existing work or insert a new one.
 |     /// Update an existing work or insert a new one.
 | ||||||
|     // TODO: Think about also inserting related items.
 |     // 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.defer_foreign_keys()?; | ||||||
| 
 | 
 | ||||||
|         self.connection.transaction::<(), Error, _>(|| { |         self.connection.transaction::<(), DatabaseError, _>(|| { | ||||||
|             let work_id = &work.id; |             let work_id = &work.id; | ||||||
|             self.delete_work(work_id)?; |             self.delete_work(work_id)?; | ||||||
| 
 | 
 | ||||||
|  | @ -163,7 +162,7 @@ impl Database { | ||||||
|                         let row = WorkPartRow { |                         let row = WorkPartRow { | ||||||
|                             id: rand::random(), |                             id: rand::random(), | ||||||
|                             work: work_id.to_string(), |                             work: work_id.to_string(), | ||||||
|                             part_index: index.try_into()?, |                             part_index: index as i64, | ||||||
|                             title: part.title, |                             title: part.title, | ||||||
|                             composer: part.composer.map(|person| person.id), |                             composer: part.composer.map(|person| person.id), | ||||||
|                         }; |                         }; | ||||||
|  | @ -178,7 +177,7 @@ impl Database { | ||||||
|                             id: rand::random(), |                             id: rand::random(), | ||||||
|                             work: work_id.to_string(), |                             work: work_id.to_string(), | ||||||
|                             title: section.title, |                             title: section.title, | ||||||
|                             before_index: section.before_index.try_into()?, |                             before_index: section.before_index as i64, | ||||||
|                         }; |                         }; | ||||||
| 
 | 
 | ||||||
|                         diesel::insert_into(work_sections::table) |                         diesel::insert_into(work_sections::table) | ||||||
|  | @ -195,7 +194,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get an existing work.
 |     /// 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 |         let row = works::table | ||||||
|             .filter(works::id.eq(id)) |             .filter(works::id.eq(id)) | ||||||
|             .load::<WorkRow>(&self.connection)? |             .load::<WorkRow>(&self.connection)? | ||||||
|  | @ -211,7 +210,7 @@ impl Database { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Retrieve all available information on a work from related tables.
 |     /// 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 mut instruments: Vec<Instrument> = Vec::new(); | ||||||
| 
 | 
 | ||||||
|         let instrumentations = instrumentations::table |         let instrumentations = instrumentations::table | ||||||
|  | @ -222,7 +221,11 @@ impl Database { | ||||||
|             let id = &instrumentation.instrument; |             let id = &instrumentation.instrument; | ||||||
|             instruments.push( |             instruments.push( | ||||||
|                 self.get_instrument(id)? |                 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 { |                 composer: match part_row.composer { | ||||||
|                     Some(composer) => Some( |                     Some(composer) => Some( | ||||||
|                         self.get_person(&composer)? |                         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, |                     None => None, | ||||||
|                 }, |                 }, | ||||||
|  | @ -254,14 +261,18 @@ impl Database { | ||||||
|         for section_row in section_rows { |         for section_row in section_rows { | ||||||
|             sections.push(WorkSection { |             sections.push(WorkSection { | ||||||
|                 title: section_row.title, |                 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_id = &row.composer; | ||||||
|         let person = self |         let person = self | ||||||
|             .get_person(person_id)? |             .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 { |         Ok(Work { | ||||||
|             id: row.id, |             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
 |     /// 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
 |     /// 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)?; |         diesel::delete(works::table.filter(works::id.eq(id))).execute(&self.connection)?; | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Get all existing works by a composer and related information from other tables.
 |     /// 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 mut works: Vec<Work> = Vec::new(); | ||||||
| 
 | 
 | ||||||
|         let rows = works::table |         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/register.rs', | ||||||
|   'backend/client/works.rs', |   'backend/client/works.rs', | ||||||
|   'backend/database/ensembles.rs', |   'backend/database/ensembles.rs', | ||||||
|  |   'backend/database/error.rs', | ||||||
|   'backend/database/instruments.rs', |   'backend/database/instruments.rs', | ||||||
|   'backend/database/medium.rs', |   'backend/database/medium.rs', | ||||||
|   'backend/database/mod.rs', |   'backend/database/mod.rs', | ||||||
|  | @ -50,6 +51,7 @@ sources = files( | ||||||
|   'backend/database/schema.rs', |   'backend/database/schema.rs', | ||||||
|   'backend/database/thread.rs', |   'backend/database/thread.rs', | ||||||
|   'backend/database/works.rs', |   'backend/database/works.rs', | ||||||
|  |   'backend/error.rs', | ||||||
|   'backend/library.rs', |   'backend/library.rs', | ||||||
|   'backend/mod.rs', |   'backend/mod.rs', | ||||||
|   'backend/player.rs', |   'backend/player.rs', | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Elias Projahn
						Elias Projahn