diff --git a/crates/backend/src/lib.rs b/crates/backend/src/lib.rs
index b0d5579..42e61f0 100644
--- a/crates/backend/src/lib.rs
+++ b/crates/backend/src/lib.rs
@@ -1,11 +1,11 @@
+use db::SqliteConnection;
use gio::traits::SettingsExt;
use log::warn;
-use musicus_database::Database;
use std::{
cell::{Cell, RefCell},
path::PathBuf,
rc::Rc,
- sync::Arc,
+ sync::{Arc, Mutex},
};
use tokio::sync::{broadcast, broadcast::Sender};
@@ -59,7 +59,7 @@ pub struct Backend {
library_updated_sender: Sender<()>,
/// The database. This can be assumed to exist, when the state is set to BackendState::Ready.
- database: RefCell>>,
+ database: RefCell >>>,
/// The player handling playlist and playback. This can be assumed to exist, when the state is
/// set to BackendState::Ready.
diff --git a/crates/backend/src/library.rs b/crates/backend/src/library.rs
index e4db783..fc74e34 100644
--- a/crates/backend/src/library.rs
+++ b/crates/backend/src/library.rs
@@ -1,9 +1,10 @@
use crate::{Backend, BackendState, Player, Result};
use gio::prelude::*;
use log::warn;
-use musicus_database::Database;
+use musicus_database::SqliteConnection;
use std::path::PathBuf;
use std::rc::Rc;
+use std::sync::{Arc, Mutex};
impl Backend {
/// Initialize the music library if it is set in the settings.
@@ -41,8 +42,11 @@ impl Backend {
let mut db_path = path.clone();
db_path.push("musicus.db");
- let database = Rc::new(Database::new(db_path.to_str().unwrap())?);
- self.database.replace(Some(Rc::clone(&database)));
+ let database = Arc::new(Mutex::new(musicus_database::connect(
+ db_path.to_str().unwrap(),
+ )?));
+
+ self.database.replace(Some(database));
let player = Player::new(path);
self.player.replace(Some(player));
@@ -60,7 +64,7 @@ impl Backend {
}
/// Get an interface to the database and panic if there is none.
- pub fn db(&self) -> Rc {
+ pub fn db(&self) -> Arc> {
self.database.borrow().clone().unwrap()
}
diff --git a/crates/backend/src/player.rs b/crates/backend/src/player.rs
index a05ee0d..df914f4 100644
--- a/crates/backend/src/player.rs
+++ b/crates/backend/src/player.rs
@@ -1,6 +1,7 @@
use crate::{Backend, Error, Result};
+use db::Track;
use glib::clone;
-use musicus_database::Track;
+use musicus_database as db;
use std::cell::{Cell, RefCell};
use std::path::PathBuf;
use std::rc::Rc;
@@ -458,7 +459,7 @@ impl TrackGenerator for RandomTrackGenerator {
}
fn next(&self) -> Vec {
- vec![self.backend.db().random_track().unwrap()]
+ vec![db::random_track(&mut self.backend.db().lock().unwrap()).unwrap()]
}
}
@@ -479,7 +480,7 @@ impl TrackGenerator for RandomRecordingGenerator {
}
fn next(&self) -> Vec {
- let recording = self.backend.db().random_recording().unwrap();
- self.backend.db().get_tracks(&recording.id).unwrap()
+ let recording = db::random_recording(&mut self.backend.db().lock().unwrap()).unwrap();
+ db::get_tracks(&mut self.backend.db().lock().unwrap(), &recording.id).unwrap()
}
}
diff --git a/crates/database/src/ensembles.rs b/crates/database/src/ensembles.rs
index 1d21ca0..421c73e 100644
--- a/crates/database/src/ensembles.rs
+++ b/crates/database/src/ensembles.rs
@@ -1,9 +1,9 @@
-use super::schema::ensembles;
-use super::{Database, Result};
use chrono::Utc;
use diesel::prelude::*;
use log::info;
+use crate::{Result, schema::ensembles, defer_foreign_keys};
+
/// An ensemble that takes part in recordings.
#[derive(Insertable, Queryable, PartialEq, Eq, Hash, Debug, Clone)]
pub struct Ensemble {
@@ -24,54 +24,51 @@ impl Ensemble {
}
}
-impl Database {
- /// Update an existing ensemble or insert a new one.
- pub fn update_ensemble(&self, mut ensemble: Ensemble) -> Result<()> {
- info!("Updating ensemble {:?}", ensemble);
- self.defer_foreign_keys()?;
+/// Update an existing ensemble or insert a new one.
+pub fn update_ensemble(connection: &mut SqliteConnection, mut ensemble: Ensemble) -> Result<()> {
+ info!("Updating ensemble {:?}", ensemble);
+ defer_foreign_keys(connection)?;
- ensemble.last_used = Some(Utc::now().timestamp());
+ ensemble.last_used = Some(Utc::now().timestamp());
- self.connection.lock().unwrap().transaction(|connection| {
- diesel::replace_into(ensembles::table)
- .values(ensemble)
- .execute(connection)
- })?;
+ connection.transaction(|connection| {
+ diesel::replace_into(ensembles::table)
+ .values(ensemble)
+ .execute(connection)
+ })?;
- Ok(())
- }
-
- /// Get an existing ensemble.
- pub fn get_ensemble(&self, id: &str) -> Result> {
- let ensemble = ensembles::table
- .filter(ensembles::id.eq(id))
- .load::(&mut *self.connection.lock().unwrap())?
- .into_iter()
- .next();
-
- Ok(ensemble)
- }
-
- /// Delete an existing ensemble.
- pub fn delete_ensemble(&self, id: &str) -> Result<()> {
- info!("Deleting ensemble {}", id);
- diesel::delete(ensembles::table.filter(ensembles::id.eq(id)))
- .execute(&mut *self.connection.lock().unwrap())?;
- Ok(())
- }
-
- /// Get all existing ensembles.
- pub fn get_ensembles(&self) -> Result> {
- let ensembles = ensembles::table.load::(&mut *self.connection.lock().unwrap())?;
- Ok(ensembles)
- }
-
- /// Get recently used ensembles.
- pub fn get_recent_ensembles(&self) -> Result> {
- let ensembles = ensembles::table
- .order(ensembles::last_used.desc())
- .load::(&mut *self.connection.lock().unwrap())?;
-
- Ok(ensembles)
- }
+ Ok(())
+}
+
+/// Get an existing ensemble.
+pub fn get_ensemble(connection: &mut SqliteConnection, id: &str) -> Result> {
+ let ensemble = ensembles::table
+ .filter(ensembles::id.eq(id))
+ .load::(connection)?
+ .into_iter()
+ .next();
+
+ Ok(ensemble)
+}
+
+/// Delete an existing ensemble.
+pub fn delete_ensemble(connection: &mut SqliteConnection, id: &str) -> Result<()> {
+ info!("Deleting ensemble {}", id);
+ diesel::delete(ensembles::table.filter(ensembles::id.eq(id))).execute(connection)?;
+ Ok(())
+}
+
+/// Get all existing ensembles.
+pub fn get_ensembles(connection: &mut SqliteConnection) -> Result> {
+ let ensembles = ensembles::table.load::(connection)?;
+ Ok(ensembles)
+}
+
+/// Get recently used ensembles.
+pub fn get_recent_ensembles(connection: &mut SqliteConnection) -> Result> {
+ let ensembles = ensembles::table
+ .order(ensembles::last_used.desc())
+ .load::(connection)?;
+
+ Ok(ensembles)
}
diff --git a/crates/database/src/instruments.rs b/crates/database/src/instruments.rs
index 1b3f436..9aabce5 100644
--- a/crates/database/src/instruments.rs
+++ b/crates/database/src/instruments.rs
@@ -1,9 +1,9 @@
-use super::schema::instruments;
-use super::{Database, Result};
use chrono::Utc;
use diesel::prelude::*;
use log::info;
+use crate::{defer_foreign_keys, schema::instruments, Result};
+
/// An instrument or any other possible role within a recording.
#[derive(Insertable, Queryable, PartialEq, Eq, Hash, Debug, Clone)]
pub struct Instrument {
@@ -24,57 +24,56 @@ impl Instrument {
}
}
-impl Database {
- /// Update an existing instrument or insert a new one.
- pub fn update_instrument(&self, mut instrument: Instrument) -> Result<()> {
- info!("Updating instrument {:?}", instrument);
- self.defer_foreign_keys()?;
+/// Update an existing instrument or insert a new one.
+pub fn update_instrument(
+ connection: &mut SqliteConnection,
+ mut instrument: Instrument,
+) -> Result<()> {
+ info!("Updating instrument {:?}", instrument);
+ defer_foreign_keys(connection)?;
- instrument.last_used = Some(Utc::now().timestamp());
+ instrument.last_used = Some(Utc::now().timestamp());
- self.connection.lock().unwrap().transaction(|connection| {
- diesel::replace_into(instruments::table)
- .values(instrument)
- .execute(connection)
- })?;
+ connection.transaction(|connection| {
+ diesel::replace_into(instruments::table)
+ .values(instrument)
+ .execute(connection)
+ })?;
- Ok(())
- }
-
- /// Get an existing instrument.
- pub fn get_instrument(&self, id: &str) -> Result> {
- let instrument = instruments::table
- .filter(instruments::id.eq(id))
- .load::(&mut *self.connection.lock().unwrap())?
- .into_iter()
- .next();
-
- Ok(instrument)
- }
-
- /// Delete an existing instrument.
- pub fn delete_instrument(&self, id: &str) -> Result<()> {
- info!("Deleting instrument {}", id);
- diesel::delete(instruments::table.filter(instruments::id.eq(id)))
- .execute(&mut *self.connection.lock().unwrap())?;
-
- Ok(())
- }
-
- /// Get all existing instruments.
- pub fn get_instruments(&self) -> Result> {
- let instruments =
- instruments::table.load::(&mut *self.connection.lock().unwrap())?;
-
- Ok(instruments)
- }
-
- /// Get recently used instruments.
- pub fn get_recent_instruments(&self) -> Result> {
- let instruments = instruments::table
- .order(instruments::last_used.desc())
- .load::(&mut *self.connection.lock().unwrap())?;
-
- Ok(instruments)
- }
+ Ok(())
+}
+
+/// Get an existing instrument.
+pub fn get_instrument(connection: &mut SqliteConnection, id: &str) -> Result> {
+ let instrument = instruments::table
+ .filter(instruments::id.eq(id))
+ .load::(connection)?
+ .into_iter()
+ .next();
+
+ Ok(instrument)
+}
+
+/// Delete an existing instrument.
+pub fn delete_instrument(connection: &mut SqliteConnection, id: &str) -> Result<()> {
+ info!("Deleting instrument {}", id);
+ diesel::delete(instruments::table.filter(instruments::id.eq(id))).execute(connection)?;
+
+ Ok(())
+}
+
+/// Get all existing instruments.
+pub fn get_instruments(connection: &mut SqliteConnection) -> Result> {
+ let instruments = instruments::table.load::(connection)?;
+
+ Ok(instruments)
+}
+
+/// Get recently used instruments.
+pub fn get_recent_instruments(connection: &mut SqliteConnection) -> Result> {
+ let instruments = instruments::table
+ .order(instruments::last_used.desc())
+ .load::(connection)?;
+
+ Ok(instruments)
}
diff --git a/crates/database/src/lib.rs b/crates/database/src/lib.rs
index 3dc7780..50aa92d 100644
--- a/crates/database/src/lib.rs
+++ b/crates/database/src/lib.rs
@@ -1,9 +1,9 @@
-use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
-use std::sync::{Arc, Mutex};
-
use diesel::prelude::*;
+use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
use log::info;
+pub use diesel::SqliteConnection;
+
pub mod ensembles;
pub use ensembles::*;
@@ -30,35 +30,25 @@ mod schema;
// This makes the SQL migration scripts accessible from the code.
const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
+/// Connect to a Musicus database running migrations if necessary.
+pub fn connect(file_name: &str) -> Result {
+ info!("Opening database file '{}'", file_name);
+ let mut connection = SqliteConnection::establish(file_name)?;
+ diesel::sql_query("PRAGMA foreign_keys = ON").execute(&mut connection)?;
+
+ info!("Running migrations if necessary");
+ connection.run_pending_migrations(MIGRATIONS)?;
+
+ Ok(connection)
+}
+
/// Generate a random string suitable as an item ID.
pub fn generate_id() -> String {
uuid::Uuid::new_v4().simple().to_string()
}
-/// Interface to a Musicus database.
-pub struct Database {
- connection: Arc>,
-}
-
-impl Database {
- /// Create a new database interface and run migrations if necessary.
- pub fn new(file_name: &str) -> Result {
- info!("Opening database file '{}'", file_name);
- let mut connection = SqliteConnection::establish(file_name)?;
- diesel::sql_query("PRAGMA foreign_keys = ON").execute(&mut connection)?;
-
- info!("Running migrations if necessary");
- connection.run_pending_migrations(MIGRATIONS)?;
-
- Ok(Database {
- connection: Arc::new(Mutex::new(connection)),
- })
- }
-
- /// Defer all foreign keys for the next transaction.
- fn defer_foreign_keys(&self) -> Result<()> {
- diesel::sql_query("PRAGMA defer_foreign_keys = ON")
- .execute(&mut *self.connection.lock().unwrap())?;
- Ok(())
- }
+/// Defer all foreign keys for the next transaction.
+fn defer_foreign_keys(connection: &mut SqliteConnection) -> Result<()> {
+ diesel::sql_query("PRAGMA defer_foreign_keys = ON").execute(connection)?;
+ Ok(())
}
diff --git a/crates/database/src/medium.rs b/crates/database/src/medium.rs
index d475287..4ae34e2 100644
--- a/crates/database/src/medium.rs
+++ b/crates/database/src/medium.rs
@@ -1,10 +1,13 @@
-use super::generate_id;
-use super::schema::{ensembles, mediums, performances, persons, recordings, tracks};
-use super::{Database, Error, Recording, Result};
use chrono::{DateTime, TimeZone, Utc};
use diesel::prelude::*;
use log::info;
+use crate::{
+ defer_foreign_keys, generate_id, get_recording,
+ schema::{ensembles, mediums, performances, persons, recordings, tracks},
+ update_recording, Error, Recording, Result,
+};
+
/// Representation of someting like a physical audio disc or a folder with
/// audio files (i.e. a collection of tracks for one or more recordings).
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
@@ -103,244 +106,246 @@ struct TrackRow {
pub last_played: Option,
}
-impl Database {
- /// Update an existing medium or insert a new one.
- pub fn update_medium(&self, medium: Medium) -> Result<()> {
- info!("Updating medium {:?}", medium);
- self.defer_foreign_keys()?;
+/// Update an existing medium or insert a new one.
+pub fn update_medium(connection: &mut SqliteConnection, medium: Medium) -> Result<()> {
+ info!("Updating medium {:?}", medium);
+ defer_foreign_keys(connection)?;
- self.connection
- .lock()
- .unwrap()
- .transaction::<(), Error, _>(|connection| {
- let medium_id = &medium.id;
+ connection.transaction::<(), Error, _>(|connection| {
+ let medium_id = &medium.id;
- // This will also delete the tracks.
- self.delete_medium(medium_id)?;
+ // This will also delete the tracks.
+ delete_medium(connection, medium_id)?;
- // Add the new medium.
+ // Add the new medium.
- let medium_row = MediumRow {
- id: medium_id.to_owned(),
- name: medium.name.clone(),
- discid: medium.discid.clone(),
- last_used: Some(Utc::now().timestamp()),
- last_played: medium.last_played.map(|t| t.timestamp()),
- };
-
- diesel::insert_into(mediums::table)
- .values(medium_row)
- .execute(connection)?;
-
- for (index, track) in medium.tracks.iter().enumerate() {
- // Add associated items from the server, if they don't already exist.
-
- if self.get_recording(&track.recording.id)?.is_none() {
- self.update_recording(track.recording.clone())?;
- }
-
- // Add the actual track data.
-
- let work_parts = track
- .work_parts
- .iter()
- .map(|part_index| part_index.to_string())
- .collect::>()
- .join(",");
-
- let track_row = TrackRow {
- id: generate_id(),
- medium: Some(medium_id.to_owned()),
- index: index as i32,
- recording: track.recording.id.clone(),
- work_parts,
- source_index: track.source_index as i32,
- path: track.path.clone(),
- last_used: Some(Utc::now().timestamp()),
- last_played: track.last_played.map(|t| t.timestamp()),
- };
-
- diesel::insert_into(tracks::table)
- .values(track_row)
- .execute(connection)?;
- }
-
- Ok(())
- })?;
-
- Ok(())
- }
-
- /// Get an existing medium.
- pub fn get_medium(&self, id: &str) -> Result> {
- let row = mediums::table
- .filter(mediums::id.eq(id))
- .load::(&mut *self.connection.lock().unwrap())?
- .into_iter()
- .next();
-
- let medium = match row {
- Some(row) => Some(self.get_medium_data(row)?),
- None => None,
+ let medium_row = MediumRow {
+ id: medium_id.to_owned(),
+ name: medium.name.clone(),
+ discid: medium.discid.clone(),
+ last_used: Some(Utc::now().timestamp()),
+ last_played: medium.last_played.map(|t| t.timestamp()),
};
- Ok(medium)
- }
+ diesel::insert_into(mediums::table)
+ .values(medium_row)
+ .execute(connection)?;
- /// Get mediums that have a specific source ID.
- pub fn get_mediums_by_source_id(&self, source_id: &str) -> Result> {
- let mut mediums: Vec = Vec::new();
+ for (index, track) in medium.tracks.iter().enumerate() {
+ // Add associated items from the server, if they don't already exist.
- let rows = mediums::table
- .filter(mediums::discid.nullable().eq(source_id))
- .load::(&mut *self.connection.lock().unwrap())?;
-
- for row in rows {
- let medium = self.get_medium_data(row)?;
- mediums.push(medium);
- }
-
- Ok(mediums)
- }
-
- /// Get mediums on which this person is performing.
- pub fn get_mediums_for_person(&self, person_id: &str) -> Result> {
- let mut mediums: Vec = Vec::new();
-
- let rows = mediums::table
- .inner_join(tracks::table.on(tracks::medium.eq(mediums::id.nullable())))
- .inner_join(recordings::table.on(recordings::id.eq(tracks::recording)))
- .inner_join(performances::table.on(performances::recording.eq(recordings::id)))
- .inner_join(persons::table.on(persons::id.nullable().eq(performances::person)))
- .filter(persons::id.eq(person_id))
- .select(mediums::table::all_columns())
- .distinct()
- .load::(&mut *self.connection.lock().unwrap())?;
-
- for row in rows {
- let medium = self.get_medium_data(row)?;
- mediums.push(medium);
- }
-
- Ok(mediums)
- }
-
- /// Get mediums on which this ensemble is performing.
- pub fn get_mediums_for_ensemble(&self, ensemble_id: &str) -> Result> {
- let mut mediums: Vec = Vec::new();
-
- let rows = mediums::table
- .inner_join(tracks::table.on(tracks::medium.eq(tracks::id.nullable())))
- .inner_join(recordings::table.on(recordings::id.eq(tracks::recording)))
- .inner_join(performances::table.on(performances::recording.eq(recordings::id)))
- .inner_join(ensembles::table.on(ensembles::id.nullable().eq(performances::ensemble)))
- .filter(ensembles::id.eq(ensemble_id))
- .select(mediums::table::all_columns())
- .distinct()
- .load::(&mut *self.connection.lock().unwrap())?;
-
- for row in rows {
- let medium = self.get_medium_data(row)?;
- mediums.push(medium);
- }
-
- Ok(mediums)
- }
-
- /// 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<()> {
- info!("Deleting medium {}", id);
- diesel::delete(mediums::table.filter(mediums::id.eq(id)))
- .execute(&mut *self.connection.lock().unwrap())?;
- Ok(())
- }
-
- /// Get all available tracks for a recording.
- pub fn get_tracks(&self, recording_id: &str) -> Result> {
- let mut tracks: Vec = Vec::new();
-
- let rows = tracks::table
- .inner_join(recordings::table.on(recordings::id.eq(tracks::recording)))
- .filter(recordings::id.eq(recording_id))
- .select(tracks::table::all_columns())
- .load::(&mut *self.connection.lock().unwrap())?;
-
- for row in rows {
- let track = self.get_track_from_row(row)?;
- tracks.push(track);
- }
-
- Ok(tracks)
- }
-
- /// Get a random track from the database.
- pub fn random_track(&self) -> Result {
- let row = diesel::sql_query("SELECT * FROM tracks ORDER BY RANDOM() LIMIT 1")
- .load::(&mut *self.connection.lock().unwrap())?
- .into_iter()
- .next()
- .ok_or(Error::Other("Failed to generate random track"))?;
-
- self.get_track_from_row(row)
- }
-
- /// Retrieve all available information on a medium from related tables.
- fn get_medium_data(&self, row: MediumRow) -> Result {
- let track_rows = tracks::table
- .filter(tracks::medium.eq(&row.id))
- .order_by(tracks::index)
- .load::(&mut *self.connection.lock().unwrap())?;
-
- let mut tracks = Vec::new();
-
- for track_row in track_rows {
- let track = self.get_track_from_row(track_row)?;
- tracks.push(track);
- }
-
- let medium = Medium {
- id: row.id,
- name: row.name,
- discid: row.discid,
- tracks,
- last_used: row.last_used.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
- last_played: row.last_played.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
- };
-
- Ok(medium)
- }
-
- /// Convert a track row from the database to an actual track.
- fn get_track_from_row(&self, row: TrackRow) -> Result {
- let recording_id = row.recording;
-
- let recording = self
- .get_recording(&recording_id)?
- .ok_or(Error::MissingItem("recording", recording_id))?;
-
- let mut part_indices = Vec::new();
-
- let work_parts = row.work_parts.split(',');
-
- for part_index in work_parts {
- if !part_index.is_empty() {
- let index = str::parse(part_index)
- .map_err(|_| Error::ParsingError("part index", String::from(part_index)))?;
-
- part_indices.push(index);
+ if get_recording(connection, &track.recording.id)?.is_none() {
+ update_recording(connection, track.recording.clone())?;
}
+
+ // Add the actual track data.
+
+ let work_parts = track
+ .work_parts
+ .iter()
+ .map(|part_index| part_index.to_string())
+ .collect::>()
+ .join(",");
+
+ let track_row = TrackRow {
+ id: generate_id(),
+ medium: Some(medium_id.to_owned()),
+ index: index as i32,
+ recording: track.recording.id.clone(),
+ work_parts,
+ source_index: track.source_index as i32,
+ path: track.path.clone(),
+ last_used: Some(Utc::now().timestamp()),
+ last_played: track.last_played.map(|t| t.timestamp()),
+ };
+
+ diesel::insert_into(tracks::table)
+ .values(track_row)
+ .execute(connection)?;
}
- let track = Track {
- recording,
- work_parts: part_indices,
- source_index: row.source_index as usize,
- path: row.path,
- last_used: row.last_used.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
- last_played: row.last_played.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
- };
+ Ok(())
+ })?;
- Ok(track)
- }
+ Ok(())
+}
+
+/// Get an existing medium.
+pub fn get_medium(connection: &mut SqliteConnection, id: &str) -> Result> {
+ let row = mediums::table
+ .filter(mediums::id.eq(id))
+ .load::(connection)?
+ .into_iter()
+ .next();
+
+ let medium = match row {
+ Some(row) => Some(get_medium_data(connection, row)?),
+ None => None,
+ };
+
+ Ok(medium)
+}
+
+/// Get mediums that have a specific source ID.
+pub fn get_mediums_by_source_id(
+ connection: &mut SqliteConnection,
+ source_id: &str,
+) -> Result> {
+ let mut mediums: Vec = Vec::new();
+
+ let rows = mediums::table
+ .filter(mediums::discid.nullable().eq(source_id))
+ .load::(connection)?;
+
+ for row in rows {
+ let medium = get_medium_data(connection, row)?;
+ mediums.push(medium);
+ }
+
+ Ok(mediums)
+}
+
+/// Get mediums on which this person is performing.
+pub fn get_mediums_for_person(
+ connection: &mut SqliteConnection,
+ person_id: &str,
+) -> Result> {
+ let mut mediums: Vec = Vec::new();
+
+ let rows = mediums::table
+ .inner_join(tracks::table.on(tracks::medium.eq(mediums::id.nullable())))
+ .inner_join(recordings::table.on(recordings::id.eq(tracks::recording)))
+ .inner_join(performances::table.on(performances::recording.eq(recordings::id)))
+ .inner_join(persons::table.on(persons::id.nullable().eq(performances::person)))
+ .filter(persons::id.eq(person_id))
+ .select(mediums::table::all_columns())
+ .distinct()
+ .load::(connection)?;
+
+ for row in rows {
+ let medium = get_medium_data(connection, row)?;
+ mediums.push(medium);
+ }
+
+ Ok(mediums)
+}
+
+/// Get mediums on which this ensemble is performing.
+pub fn get_mediums_for_ensemble(
+ connection: &mut SqliteConnection,
+ ensemble_id: &str,
+) -> Result> {
+ let mut mediums: Vec = Vec::new();
+
+ let rows = mediums::table
+ .inner_join(tracks::table.on(tracks::medium.eq(tracks::id.nullable())))
+ .inner_join(recordings::table.on(recordings::id.eq(tracks::recording)))
+ .inner_join(performances::table.on(performances::recording.eq(recordings::id)))
+ .inner_join(ensembles::table.on(ensembles::id.nullable().eq(performances::ensemble)))
+ .filter(ensembles::id.eq(ensemble_id))
+ .select(mediums::table::all_columns())
+ .distinct()
+ .load::(connection)?;
+
+ for row in rows {
+ let medium = get_medium_data(connection, row)?;
+ mediums.push(medium);
+ }
+
+ Ok(mediums)
+}
+
+/// 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(connection: &mut SqliteConnection, id: &str) -> Result<()> {
+ info!("Deleting medium {}", id);
+ diesel::delete(mediums::table.filter(mediums::id.eq(id))).execute(connection)?;
+ Ok(())
+}
+
+/// Get all available tracks for a recording.
+pub fn get_tracks(connection: &mut SqliteConnection, recording_id: &str) -> Result> {
+ let mut tracks: Vec = Vec::new();
+
+ let rows = tracks::table
+ .inner_join(recordings::table.on(recordings::id.eq(tracks::recording)))
+ .filter(recordings::id.eq(recording_id))
+ .select(tracks::table::all_columns())
+ .load::(connection)?;
+
+ for row in rows {
+ let track = get_track_from_row(connection, row)?;
+ tracks.push(track);
+ }
+
+ Ok(tracks)
+}
+
+/// Get a random track from the database.
+pub fn random_track(connection: &mut SqliteConnection) -> Result {
+ let row = diesel::sql_query("SELECT * FROM tracks ORDER BY RANDOM() LIMIT 1")
+ .load::(connection)?
+ .into_iter()
+ .next()
+ .ok_or(Error::Other("Failed to generate random track"))?;
+
+ get_track_from_row(connection, row)
+}
+
+/// Retrieve all available information on a medium from related tables.
+fn get_medium_data(connection: &mut SqliteConnection, row: MediumRow) -> Result {
+ let track_rows = tracks::table
+ .filter(tracks::medium.eq(&row.id))
+ .order_by(tracks::index)
+ .load::(connection)?;
+
+ let mut tracks = Vec::new();
+
+ for track_row in track_rows {
+ let track = get_track_from_row(connection, track_row)?;
+ tracks.push(track);
+ }
+
+ let medium = Medium {
+ id: row.id,
+ name: row.name,
+ discid: row.discid,
+ tracks,
+ last_used: row.last_used.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
+ last_played: row.last_played.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
+ };
+
+ Ok(medium)
+}
+
+/// Convert a track row from the database to an actual track.
+fn get_track_from_row(connection: &mut SqliteConnection, row: TrackRow) -> Result {
+ let recording_id = row.recording;
+
+ let recording = get_recording(connection, &recording_id)?
+ .ok_or(Error::MissingItem("recording", recording_id))?;
+
+ let mut part_indices = Vec::new();
+
+ let work_parts = row.work_parts.split(',');
+
+ for part_index in work_parts {
+ if !part_index.is_empty() {
+ let index = str::parse(part_index)
+ .map_err(|_| Error::ParsingError("part index", String::from(part_index)))?;
+
+ part_indices.push(index);
+ }
+ }
+
+ let track = Track {
+ recording,
+ work_parts: part_indices,
+ source_index: row.source_index as usize,
+ path: row.path,
+ last_used: row.last_used.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
+ last_played: row.last_played.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
+ };
+
+ Ok(track)
}
diff --git a/crates/database/src/persons.rs b/crates/database/src/persons.rs
index 6c401c3..157bb1c 100644
--- a/crates/database/src/persons.rs
+++ b/crates/database/src/persons.rs
@@ -1,9 +1,9 @@
-use super::schema::persons;
-use super::{Database, Result};
use chrono::Utc;
use diesel::prelude::*;
use log::info;
+use crate::{defer_foreign_keys, schema::persons, Result};
+
/// A person that is a composer, an interpret or both.
#[derive(Insertable, Queryable, PartialEq, Eq, Hash, Debug, Clone)]
pub struct Person {
@@ -35,56 +35,52 @@ impl Person {
format!("{}, {}", self.last_name, self.first_name)
}
}
+/// Update an existing person or insert a new one.
+pub fn update_person(connection: &mut SqliteConnection, mut person: Person) -> Result<()> {
+ info!("Updating person {:?}", person);
+ defer_foreign_keys(connection)?;
-impl Database {
- /// Update an existing person or insert a new one.
- pub fn update_person(&self, mut person: Person) -> Result<()> {
- info!("Updating person {:?}", person);
- self.defer_foreign_keys()?;
+ person.last_used = Some(Utc::now().timestamp());
- person.last_used = Some(Utc::now().timestamp());
+ connection.transaction(|connection| {
+ diesel::replace_into(persons::table)
+ .values(person)
+ .execute(connection)
+ })?;
- self.connection.lock().unwrap().transaction(|connection| {
- diesel::replace_into(persons::table)
- .values(person)
- .execute(connection)
- })?;
-
- Ok(())
- }
-
- /// Get an existing person.
- pub fn get_person(&self, id: &str) -> Result> {
- let person = persons::table
- .filter(persons::id.eq(id))
- .load::(&mut *self.connection.lock().unwrap())?
- .into_iter()
- .next();
-
- Ok(person)
- }
-
- /// Delete an existing person.
- pub fn delete_person(&self, id: &str) -> Result<()> {
- info!("Deleting person {}", id);
- diesel::delete(persons::table.filter(persons::id.eq(id)))
- .execute(&mut *self.connection.lock().unwrap())?;
- Ok(())
- }
-
- /// Get all existing persons.
- pub fn get_persons(&self) -> Result> {
- let persons = persons::table.load::(&mut *self.connection.lock().unwrap())?;
-
- Ok(persons)
- }
-
- /// Get recently used persons.
- pub fn get_recent_persons(&self) -> Result> {
- let persons = persons::table
- .order(persons::last_used.desc())
- .load::(&mut *self.connection.lock().unwrap())?;
-
- Ok(persons)
- }
+ Ok(())
+}
+
+/// Get an existing person.
+pub fn get_person(connection: &mut SqliteConnection, id: &str) -> Result> {
+ let person = persons::table
+ .filter(persons::id.eq(id))
+ .load::(connection)?
+ .into_iter()
+ .next();
+
+ Ok(person)
+}
+
+/// Delete an existing person.
+pub fn delete_person(connection: &mut SqliteConnection, id: &str) -> Result<()> {
+ info!("Deleting person {}", id);
+ diesel::delete(persons::table.filter(persons::id.eq(id))).execute(connection)?;
+ Ok(())
+}
+
+/// Get all existing persons.
+pub fn get_persons(connection: &mut SqliteConnection) -> Result> {
+ let persons = persons::table.load::(connection)?;
+
+ Ok(persons)
+}
+
+/// Get recently used persons.
+pub fn get_recent_persons(connection: &mut SqliteConnection) -> Result> {
+ let persons = persons::table
+ .order(persons::last_used.desc())
+ .load::(connection)?;
+
+ Ok(persons)
}
diff --git a/crates/database/src/recordings.rs b/crates/database/src/recordings.rs
index 16a2928..b62f488 100644
--- a/crates/database/src/recordings.rs
+++ b/crates/database/src/recordings.rs
@@ -1,10 +1,14 @@
-use super::generate_id;
-use super::schema::{ensembles, performances, persons, recordings};
-use super::{Database, Ensemble, Error, Instrument, Person, Result, Work};
use chrono::{DateTime, TimeZone, Utc};
use diesel::prelude::*;
use log::info;
+use crate::{
+ defer_foreign_keys, generate_id, get_ensemble, get_instrument, get_person, get_work,
+ schema::{ensembles, performances, persons, recordings},
+ update_ensemble, update_instrument, update_person, update_work, Ensemble, Error, Instrument,
+ Person, Result, Work,
+};
+
/// A specific recording of a work.
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
pub struct Recording {
@@ -125,223 +129,222 @@ struct PerformanceRow {
pub role: Option,
}
-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<()> {
- info!("Updating recording {:?}", recording);
- self.defer_foreign_keys()?;
- self.connection
- .lock()
- .unwrap()
- .transaction::<(), Error, _>(|connection| {
- let recording_id = &recording.id;
- self.delete_recording(recording_id)?;
+/// Update an existing recording or insert a new one.
+// TODO: Think about whether to also insert the other items.
+pub fn update_recording(connection: &mut SqliteConnection, recording: Recording) -> Result<()> {
+ info!("Updating recording {:?}", recording);
+ defer_foreign_keys(connection)?;
- // Add associated items from the server, if they don't already exist.
+ connection.transaction::<(), Error, _>(|connection| {
+ let recording_id = &recording.id;
+ delete_recording(connection, recording_id)?;
- if self.get_work(&recording.work.id)?.is_none() {
- self.update_work(recording.work.clone())?;
- }
+ // Add associated items from the server, if they don't already exist.
- for performance in &recording.performances {
- match &performance.performer {
- PersonOrEnsemble::Person(person) => {
- if self.get_person(&person.id)?.is_none() {
- self.update_person(person.clone())?;
- }
- }
- PersonOrEnsemble::Ensemble(ensemble) => {
- if self.get_ensemble(&ensemble.id)?.is_none() {
- self.update_ensemble(ensemble.clone())?;
- }
- }
- }
+ if get_work(connection, &recording.work.id)?.is_none() {
+ update_work(connection, recording.work.clone())?;
+ }
- if let Some(role) = &performance.role {
- if self.get_instrument(&role.id)?.is_none() {
- self.update_instrument(role.clone())?;
- }
+ for performance in &recording.performances {
+ match &performance.performer {
+ PersonOrEnsemble::Person(person) => {
+ if get_person(connection, &person.id)?.is_none() {
+ update_person(connection, person.clone())?;
}
}
-
- // Add the actual recording.
-
- let row: RecordingRow = recording.clone().into();
- diesel::insert_into(recordings::table)
- .values(row)
- .execute(connection)?;
-
- for performance in recording.performances {
- let (person, ensemble) = match performance.performer {
- PersonOrEnsemble::Person(person) => (Some(person.id), None),
- PersonOrEnsemble::Ensemble(ensemble) => (None, Some(ensemble.id)),
- };
-
- let row = PerformanceRow {
- id: rand::random(),
- recording: recording_id.to_string(),
- person,
- ensemble,
- role: performance.role.map(|role| role.id),
- };
-
- diesel::insert_into(performances::table)
- .values(row)
- .execute(connection)?;
+ PersonOrEnsemble::Ensemble(ensemble) => {
+ if get_ensemble(connection, &ensemble.id)?.is_none() {
+ update_ensemble(connection, ensemble.clone())?;
+ }
}
+ }
- Ok(())
- })?;
+ if let Some(role) = &performance.role {
+ if get_instrument(connection, &role.id)?.is_none() {
+ update_instrument(connection, role.clone())?;
+ }
+ }
+ }
+
+ // Add the actual recording.
+
+ let row: RecordingRow = recording.clone().into();
+ diesel::insert_into(recordings::table)
+ .values(row)
+ .execute(connection)?;
+
+ for performance in recording.performances {
+ let (person, ensemble) = match performance.performer {
+ PersonOrEnsemble::Person(person) => (Some(person.id), None),
+ PersonOrEnsemble::Ensemble(ensemble) => (None, Some(ensemble.id)),
+ };
+
+ let row = PerformanceRow {
+ id: rand::random(),
+ recording: recording_id.to_string(),
+ person,
+ ensemble,
+ role: performance.role.map(|role| role.id),
+ };
+
+ diesel::insert_into(performances::table)
+ .values(row)
+ .execute(connection)?;
+ }
Ok(())
- }
+ })?;
- /// Check whether the database contains a recording.
- pub fn recording_exists(&self, id: &str) -> Result {
- let exists = recordings::table
- .filter(recordings::id.eq(id))
- .load::(&mut *self.connection.lock().unwrap())?
- .first()
- .is_some();
-
- Ok(exists)
- }
-
- /// Get an existing recording.
- pub fn get_recording(&self, id: &str) -> Result> {
- let row = recordings::table
- .filter(recordings::id.eq(id))
- .load::(&mut *self.connection.lock().unwrap())?
- .into_iter()
- .next();
-
- let recording = match row {
- Some(row) => Some(self.get_recording_data(row)?),
- None => None,
- };
-
- Ok(recording)
- }
-
- /// Get a random recording from the database.
- pub fn random_recording(&self) -> Result {
- let row = diesel::sql_query("SELECT * FROM recordings ORDER BY RANDOM() LIMIT 1")
- .load::(&mut *self.connection.lock().unwrap())?
- .into_iter()
- .next()
- .ok_or(Error::Other("Failed to find random recording."))?;
-
- self.get_recording_data(row)
- }
-
- /// Retrieve all available information on a recording from related tables.
- fn get_recording_data(&self, row: RecordingRow) -> Result {
- let mut performance_descriptions: Vec = Vec::new();
-
- let performance_rows = performances::table
- .filter(performances::recording.eq(&row.id))
- .load::(&mut *self.connection.lock().unwrap())?;
-
- for row in performance_rows {
- performance_descriptions.push(Performance {
- performer: if let Some(id) = row.person {
- PersonOrEnsemble::Person(
- self.get_person(&id)?
- .ok_or(Error::MissingItem("person", id))?,
- )
- } else if let Some(id) = row.ensemble {
- PersonOrEnsemble::Ensemble(
- self.get_ensemble(&id)?
- .ok_or(Error::MissingItem("ensemble", id))?,
- )
- } else {
- return Err(Error::Other("Performance without performer"));
- },
- role: match row.role {
- Some(id) => Some(
- self.get_instrument(&id)?
- .ok_or(Error::MissingItem("instrument", id))?,
- ),
- None => None,
- },
- });
- }
-
- let work_id = row.work;
- let work = self
- .get_work(&work_id)?
- .ok_or(Error::MissingItem("work", work_id))?;
-
- let recording_description = Recording {
- id: row.id,
- work,
- comment: row.comment,
- performances: performance_descriptions,
- last_used: row.last_used.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
- last_played: row.last_played.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
- };
-
- Ok(recording_description)
- }
-
- /// Get all available information on all recordings where a person is performing.
- pub fn get_recordings_for_person(&self, person_id: &str) -> Result> {
- let mut recordings: Vec = Vec::new();
-
- let rows = recordings::table
- .inner_join(performances::table.on(performances::recording.eq(recordings::id)))
- .inner_join(persons::table.on(persons::id.nullable().eq(performances::person)))
- .filter(persons::id.eq(person_id))
- .select(recordings::table::all_columns())
- .load::(&mut *self.connection.lock().unwrap())?;
-
- for row in rows {
- recordings.push(self.get_recording_data(row)?);
- }
-
- Ok(recordings)
- }
-
- /// Get all available information on all recordings where an ensemble is performing.
- pub fn get_recordings_for_ensemble(&self, ensemble_id: &str) -> Result> {
- let mut recordings: Vec = Vec::new();
-
- let rows = recordings::table
- .inner_join(performances::table.on(performances::recording.eq(recordings::id)))
- .inner_join(ensembles::table.on(ensembles::id.nullable().eq(performances::ensemble)))
- .filter(ensembles::id.eq(ensemble_id))
- .select(recordings::table::all_columns())
- .load::(&mut *self.connection.lock().unwrap())?;
-
- for row in rows {
- recordings.push(self.get_recording_data(row)?);
- }
-
- Ok(recordings)
- }
-
- /// Get allavailable information on all recordings of a work.
- pub fn get_recordings_for_work(&self, work_id: &str) -> Result> {
- let mut recordings: Vec = Vec::new();
-
- let rows = recordings::table
- .filter(recordings::work.eq(work_id))
- .load::(&mut *self.connection.lock().unwrap())?;
-
- for row in rows {
- recordings.push(self.get_recording_data(row)?);
- }
-
- Ok(recordings)
- }
-
- /// 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<()> {
- info!("Deleting recording {}", id);
- diesel::delete(recordings::table.filter(recordings::id.eq(id)))
- .execute(&mut *self.connection.lock().unwrap())?;
- Ok(())
- }
+ Ok(())
+}
+
+/// Check whether the database contains a recording.
+pub fn recording_exists(connection: &mut SqliteConnection, id: &str) -> Result {
+ let exists = recordings::table
+ .filter(recordings::id.eq(id))
+ .load::(connection)?
+ .first()
+ .is_some();
+
+ Ok(exists)
+}
+
+/// Get an existing recording.
+pub fn get_recording(connection: &mut SqliteConnection, id: &str) -> Result> {
+ let row = recordings::table
+ .filter(recordings::id.eq(id))
+ .load::(connection)?
+ .into_iter()
+ .next();
+
+ let recording = match row {
+ Some(row) => Some(get_recording_data(connection, row)?),
+ None => None,
+ };
+
+ Ok(recording)
+}
+
+/// Get a random recording from the database.
+pub fn random_recording(connection: &mut SqliteConnection) -> Result {
+ let row = diesel::sql_query("SELECT * FROM recordings ORDER BY RANDOM() LIMIT 1")
+ .load::(connection)?
+ .into_iter()
+ .next()
+ .ok_or(Error::Other("Failed to find random recording."))?;
+
+ get_recording_data(connection, row)
+}
+
+/// Retrieve all available information on a recording from related tables.
+fn get_recording_data(connection: &mut SqliteConnection, row: RecordingRow) -> Result {
+ let mut performance_descriptions: Vec = Vec::new();
+
+ let performance_rows = performances::table
+ .filter(performances::recording.eq(&row.id))
+ .load::(connection)?;
+
+ for row in performance_rows {
+ performance_descriptions.push(Performance {
+ performer: if let Some(id) = row.person {
+ PersonOrEnsemble::Person(
+ get_person(connection, &id)?.ok_or(Error::MissingItem("person", id))?,
+ )
+ } else if let Some(id) = row.ensemble {
+ PersonOrEnsemble::Ensemble(
+ get_ensemble(connection, &id)?.ok_or(Error::MissingItem("ensemble", id))?,
+ )
+ } else {
+ return Err(Error::Other("Performance without performer"));
+ },
+ role: match row.role {
+ Some(id) => Some(
+ get_instrument(connection, &id)?.ok_or(Error::MissingItem("instrument", id))?,
+ ),
+ None => None,
+ },
+ });
+ }
+
+ let work_id = row.work;
+ let work = get_work(connection, &work_id)?.ok_or(Error::MissingItem("work", work_id))?;
+
+ let recording_description = Recording {
+ id: row.id,
+ work,
+ comment: row.comment,
+ performances: performance_descriptions,
+ last_used: row.last_used.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
+ last_played: row.last_played.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
+ };
+
+ Ok(recording_description)
+}
+
+/// Get all available information on all recordings where a person is performing.
+pub fn get_recordings_for_person(
+ connection: &mut SqliteConnection,
+ person_id: &str,
+) -> Result> {
+ let mut recordings: Vec = Vec::new();
+
+ let rows = recordings::table
+ .inner_join(performances::table.on(performances::recording.eq(recordings::id)))
+ .inner_join(persons::table.on(persons::id.nullable().eq(performances::person)))
+ .filter(persons::id.eq(person_id))
+ .select(recordings::table::all_columns())
+ .load::(connection)?;
+
+ for row in rows {
+ recordings.push(get_recording_data(connection, row)?);
+ }
+
+ Ok(recordings)
+}
+
+/// Get all available information on all recordings where an ensemble is performing.
+pub fn get_recordings_for_ensemble(
+ connection: &mut SqliteConnection,
+ ensemble_id: &str,
+) -> Result> {
+ let mut recordings: Vec = Vec::new();
+
+ let rows = recordings::table
+ .inner_join(performances::table.on(performances::recording.eq(recordings::id)))
+ .inner_join(ensembles::table.on(ensembles::id.nullable().eq(performances::ensemble)))
+ .filter(ensembles::id.eq(ensemble_id))
+ .select(recordings::table::all_columns())
+ .load::(connection)?;
+
+ for row in rows {
+ recordings.push(get_recording_data(connection, row)?);
+ }
+
+ Ok(recordings)
+}
+
+/// Get allavailable information on all recordings of a work.
+pub fn get_recordings_for_work(
+ connection: &mut SqliteConnection,
+ work_id: &str,
+) -> Result> {
+ let mut recordings: Vec = Vec::new();
+
+ let rows = recordings::table
+ .filter(recordings::work.eq(work_id))
+ .load::(connection)?;
+
+ for row in rows {
+ recordings.push(get_recording_data(connection, row)?);
+ }
+
+ Ok(recordings)
+}
+
+/// 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(connection: &mut SqliteConnection, id: &str) -> Result<()> {
+ info!("Deleting recording {}", id);
+ diesel::delete(recordings::table.filter(recordings::id.eq(id))).execute(connection)?;
+ Ok(())
}
diff --git a/crates/database/src/works.rs b/crates/database/src/works.rs
index 776bab2..ecb8a2c 100644
--- a/crates/database/src/works.rs
+++ b/crates/database/src/works.rs
@@ -1,11 +1,13 @@
-use super::generate_id;
-use super::schema::{instrumentations, work_parts, works};
-use super::{Database, Error, Instrument, Person, Result};
use chrono::{DateTime, TimeZone, Utc};
-use diesel::prelude::*;
-use diesel::{Insertable, Queryable};
+use diesel::{prelude::*, Insertable, Queryable};
use log::info;
+use crate::{
+ defer_foreign_keys, generate_id, get_instrument, get_person,
+ schema::{instrumentations, work_parts, works},
+ update_instrument, update_person, Error, Instrument, Person, Result,
+};
+
/// Table row data for a work.
#[derive(Insertable, Queryable, Debug, Clone)]
#[table_name = "works"]
@@ -105,155 +107,146 @@ 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<()> {
- info!("Updating work {:?}", work);
- self.defer_foreign_keys()?;
+/// Update an existing work or insert a new one.
+// TODO: Think about also inserting related items.
+pub fn update_work(connection: &mut SqliteConnection, work: Work) -> Result<()> {
+ info!("Updating work {:?}", work);
+ defer_foreign_keys(connection)?;
- self.connection
- .lock()
- .unwrap()
- .transaction::<(), Error, _>(|connection| {
- let work_id = &work.id;
- self.delete_work(work_id)?;
+ connection.transaction::<(), Error, _>(|connection| {
+ let work_id = &work.id;
+ delete_work(connection, work_id)?;
- // Add associated items from the server, if they don't already exist.
+ // Add associated items from the server, if they don't already exist.
- if self.get_person(&work.composer.id)?.is_none() {
- self.update_person(work.composer.clone())?;
- }
+ if get_person(connection, &work.composer.id)?.is_none() {
+ update_person(connection, work.composer.clone())?;
+ }
- for instrument in &work.instruments {
- if self.get_instrument(&instrument.id)?.is_none() {
- self.update_instrument(instrument.clone())?;
- }
- }
+ for instrument in &work.instruments {
+ if get_instrument(connection, &instrument.id)?.is_none() {
+ update_instrument(connection, instrument.clone())?;
+ }
+ }
- // Add the actual work.
+ // Add the actual work.
- let row: WorkRow = work.clone().into();
- diesel::insert_into(works::table)
- .values(row)
- .execute(&mut *self.connection.lock().unwrap())?;
+ let row: WorkRow = work.clone().into();
+ diesel::insert_into(works::table)
+ .values(row)
+ .execute(connection)?;
- let Work {
- instruments, parts, ..
- } = work;
+ let Work {
+ instruments, parts, ..
+ } = work;
- for instrument in instruments {
- let row = InstrumentationRow {
- id: rand::random(),
- work: work_id.to_string(),
- instrument: instrument.id,
- };
+ for instrument in instruments {
+ let row = InstrumentationRow {
+ id: rand::random(),
+ work: work_id.to_string(),
+ instrument: instrument.id,
+ };
- diesel::insert_into(instrumentations::table)
- .values(row)
- .execute(connection)?;
- }
+ diesel::insert_into(instrumentations::table)
+ .values(row)
+ .execute(connection)?;
+ }
- for (index, part) in parts.into_iter().enumerate() {
- let row = WorkPartRow {
- id: rand::random(),
- work: work_id.to_string(),
- part_index: index as i64,
- title: part.title,
- };
+ for (index, part) in parts.into_iter().enumerate() {
+ let row = WorkPartRow {
+ id: rand::random(),
+ work: work_id.to_string(),
+ part_index: index as i64,
+ title: part.title,
+ };
- diesel::insert_into(work_parts::table)
- .values(row)
- .execute(connection)?;
- }
-
- Ok(())
- })?;
+ diesel::insert_into(work_parts::table)
+ .values(row)
+ .execute(connection)?;
+ }
Ok(())
- }
+ })?;
- /// Get an existing work.
- pub fn get_work(&self, id: &str) -> Result> {
- let row = works::table
- .filter(works::id.eq(id))
- .load::(&mut *self.connection.lock().unwrap())?
- .first()
- .cloned();
-
- let work = match row {
- Some(row) => Some(self.get_work_data(row)?),
- None => None,
- };
-
- Ok(work)
- }
-
- /// Retrieve all available information on a work from related tables.
- fn get_work_data(&self, row: WorkRow) -> Result {
- let mut instruments: Vec = Vec::new();
-
- let instrumentations = instrumentations::table
- .filter(instrumentations::work.eq(&row.id))
- .load::(&mut *self.connection.lock().unwrap())?;
-
- for instrumentation in instrumentations {
- let id = instrumentation.instrument;
- instruments.push(
- self.get_instrument(&id)?
- .ok_or(Error::MissingItem("instrument", id))?,
- );
- }
-
- let mut parts: Vec = Vec::new();
-
- let part_rows = work_parts::table
- .filter(work_parts::work.eq(&row.id))
- .load::(&mut *self.connection.lock().unwrap())?;
-
- for part_row in part_rows {
- parts.push(WorkPart {
- title: part_row.title,
- });
- }
-
- let person_id = row.composer;
- let person = self
- .get_person(&person_id)?
- .ok_or(Error::MissingItem("person", person_id))?;
-
- Ok(Work {
- id: row.id,
- composer: person,
- title: row.title,
- instruments,
- parts,
- last_used: row.last_used.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
- last_played: row.last_played.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
- })
- }
-
- /// 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<()> {
- info!("Deleting work {}", id);
- diesel::delete(works::table.filter(works::id.eq(id)))
- .execute(&mut *self.connection.lock().unwrap())?;
- Ok(())
- }
-
- /// Get all existing works by a composer and related information from other tables.
- pub fn get_works(&self, composer_id: &str) -> Result> {
- let mut works: Vec = Vec::new();
-
- let rows = works::table
- .filter(works::composer.eq(composer_id))
- .load::(&mut *self.connection.lock().unwrap())?;
-
- for row in rows {
- works.push(self.get_work_data(row)?);
- }
-
- Ok(works)
- }
+ Ok(())
+}
+
+/// Get an existing work.
+pub fn get_work(connection: &mut SqliteConnection, id: &str) -> Result> {
+ let row = works::table
+ .filter(works::id.eq(id))
+ .load::(connection)?
+ .first()
+ .cloned();
+
+ let work = match row {
+ Some(row) => Some(get_work_data(connection, row)?),
+ None => None,
+ };
+
+ Ok(work)
+}
+
+/// Retrieve all available information on a work from related tables.
+fn get_work_data(connection: &mut SqliteConnection, row: WorkRow) -> Result {
+ let mut instruments: Vec = Vec::new();
+
+ let instrumentations = instrumentations::table
+ .filter(instrumentations::work.eq(&row.id))
+ .load::(connection)?;
+
+ for instrumentation in instrumentations {
+ let id = instrumentation.instrument;
+ instruments
+ .push(get_instrument(connection, &id)?.ok_or(Error::MissingItem("instrument", id))?);
+ }
+
+ let mut parts: Vec = Vec::new();
+
+ let part_rows = work_parts::table
+ .filter(work_parts::work.eq(&row.id))
+ .load::(connection)?;
+
+ for part_row in part_rows {
+ parts.push(WorkPart {
+ title: part_row.title,
+ });
+ }
+
+ let person_id = row.composer;
+ let person =
+ get_person(connection, &person_id)?.ok_or(Error::MissingItem("person", person_id))?;
+
+ Ok(Work {
+ id: row.id,
+ composer: person,
+ title: row.title,
+ instruments,
+ parts,
+ last_used: row.last_used.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
+ last_played: row.last_played.map(|t| Utc.timestamp_opt(t, 0).unwrap()),
+ })
+}
+
+/// 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(connection: &mut SqliteConnection, id: &str) -> Result<()> {
+ info!("Deleting work {}", id);
+ diesel::delete(works::table.filter(works::id.eq(id))).execute(connection)?;
+ Ok(())
+}
+
+/// Get all existing works by a composer and related information from other tables.
+pub fn get_works(connection: &mut SqliteConnection, composer_id: &str) -> Result> {
+ let mut works: Vec = Vec::new();
+
+ let rows = works::table
+ .filter(works::composer.eq(composer_id))
+ .load::(connection)?;
+
+ for row in rows {
+ works.push(get_work_data(connection, row)?);
+ }
+
+ Ok(works)
}
diff --git a/crates/musicus/src/editors/ensemble.rs b/crates/musicus/src/editors/ensemble.rs
index 8d45191..1f72dff 100644
--- a/crates/musicus/src/editors/ensemble.rs
+++ b/crates/musicus/src/editors/ensemble.rs
@@ -3,7 +3,7 @@ use crate::widgets::{Editor, Section, Widget};
use anyhow::Result;
use gettextrs::gettext;
use gtk::{builders::ListBoxBuilder, glib::clone, prelude::*};
-use musicus_backend::db::{generate_id, Ensemble};
+use musicus_backend::db::{generate_id, Ensemble, self};
use std::rc::Rc;
/// A dialog for creating or editing a ensemble.
@@ -88,7 +88,7 @@ impl EnsembleEditor {
let ensemble = Ensemble::new(self.id.clone(), name.to_string());
- self.handle.backend.db().update_ensemble(ensemble.clone())?;
+ db::update_ensemble(&mut self.handle.backend.db().lock().unwrap(), ensemble.clone())?;
self.handle.backend.library_changed();
Ok(ensemble)
diff --git a/crates/musicus/src/editors/instrument.rs b/crates/musicus/src/editors/instrument.rs
index 13e8536..daaea20 100644
--- a/crates/musicus/src/editors/instrument.rs
+++ b/crates/musicus/src/editors/instrument.rs
@@ -3,7 +3,7 @@ use crate::widgets::{Editor, Section, Widget};
use anyhow::Result;
use gettextrs::gettext;
use gtk::{builders::ListBoxBuilder, glib::clone, prelude::*};
-use musicus_backend::db::{generate_id, Instrument};
+use musicus_backend::db::{self, generate_id, Instrument};
use std::rc::Rc;
/// A dialog for creating or editing a instrument.
@@ -88,10 +88,11 @@ impl InstrumentEditor {
let instrument = Instrument::new(self.id.clone(), name.to_string());
- self.handle
- .backend
- .db()
- .update_instrument(instrument.clone())?;
+ db::update_instrument(
+ &mut self.handle.backend.db().lock().unwrap(),
+ instrument.clone(),
+ )?;
+
self.handle.backend.library_changed();
Ok(instrument)
diff --git a/crates/musicus/src/editors/person.rs b/crates/musicus/src/editors/person.rs
index 390b865..1d2f692 100644
--- a/crates/musicus/src/editors/person.rs
+++ b/crates/musicus/src/editors/person.rs
@@ -4,7 +4,7 @@ use anyhow::Result;
use gettextrs::gettext;
use glib::clone;
use gtk::{builders::ListBoxBuilder, prelude::*};
-use musicus_backend::db::{generate_id, Person};
+use musicus_backend::db::{generate_id, Person, self};
use std::rc::Rc;
/// A dialog for creating or editing a person.
@@ -110,7 +110,11 @@ impl PersonEditor {
last_name.to_string(),
);
- self.handle.backend.db().update_person(person.clone())?;
+ db::update_person(
+ &mut self.handle.backend.db().lock().unwrap(),
+ person.clone(),
+ )?;
+
self.handle.backend.library_changed();
Ok(person)
diff --git a/crates/musicus/src/editors/recording.rs b/crates/musicus/src/editors/recording.rs
index 4de62e8..c9b8155 100644
--- a/crates/musicus/src/editors/recording.rs
+++ b/crates/musicus/src/editors/recording.rs
@@ -8,7 +8,7 @@ use anyhow::Result;
use gettextrs::gettext;
use glib::clone;
use gtk_macros::get_widget;
-use musicus_backend::db::{generate_id, Performance, Recording, Work};
+use musicus_backend::db::{self, generate_id, Performance, Recording, Work};
use std::cell::RefCell;
use std::rc::Rc;
@@ -188,10 +188,11 @@ impl RecordingEditor {
self.performances.borrow().clone(),
);
- self.handle
- .backend
- .db()
- .update_recording(recording.clone())?;
+ db::update_recording(
+ &mut self.handle.backend.db().lock().unwrap(),
+ recording.clone(),
+ )?;
+
self.handle.backend.library_changed();
Ok(recording)
diff --git a/crates/musicus/src/editors/work.rs b/crates/musicus/src/editors/work.rs
index 5e5f9a3..1d0e9f4 100644
--- a/crates/musicus/src/editors/work.rs
+++ b/crates/musicus/src/editors/work.rs
@@ -8,7 +8,7 @@ use anyhow::Result;
use gettextrs::gettext;
use glib::clone;
use gtk_macros::get_widget;
-use musicus_backend::db::{generate_id, Instrument, Person, Work, WorkPart};
+use musicus_backend::db::{self, generate_id, Instrument, Person, Work, WorkPart};
use std::cell::RefCell;
use std::rc::Rc;
@@ -261,7 +261,7 @@ impl WorkEditor {
self.parts.borrow().clone(),
);
- self.handle.backend.db().update_work(work.clone())?;
+ db::update_work(&mut self.handle.backend.db().lock().unwrap(), work.clone())?;
self.handle.backend.library_changed();
Ok(work)
diff --git a/crates/musicus/src/import/import_screen.rs b/crates/musicus/src/import/import_screen.rs
index c6e6b2c..3ae46e2 100644
--- a/crates/musicus/src/import/import_screen.rs
+++ b/crates/musicus/src/import/import_screen.rs
@@ -7,7 +7,7 @@ use adw::builders::ActionRowBuilder;
use adw::prelude::*;
use glib::clone;
use gtk_macros::get_widget;
-use musicus_backend::db::Medium;
+use musicus_backend::db::{self, Medium};
use musicus_backend::import::ImportSession;
use std::rc::Rc;
use std::sync::Arc;
@@ -29,7 +29,10 @@ impl ImportScreen {
let this = self;
spawn!(@clone this, async move {
- let mediums = this.handle.backend.db().get_mediums_by_source_id(this.session.source_id());
+ let mediums = db::get_mediums_by_source_id(
+ &mut this.handle.backend.db().lock().unwrap(),
+ this.session.source_id()
+ );
match mediums {
Ok(mediums) => {
@@ -110,7 +113,7 @@ impl Screen, ()> for ImportScreen {
get_widget!(builder, adw::ActionRow, error_row);
get_widget!(builder, gtk::ListBox, matching_list);
get_widget!(builder, gtk::Button, select_button);
- get_widget!(builder, gtk::Button, add_button);
+ get_widget!(builder, gtk::Button, add_medium_button);
let this = Rc::new(Self {
handle,
@@ -139,7 +142,7 @@ impl Screen, ()> for ImportScreen {
});
}));
- add_button.connect_clicked(clone!(@weak this => move |_| {
+ add_medium_button.connect_clicked(clone!(@weak this => move |_| {
spawn!(@clone this, async move {
if let Some(medium) = push!(this.handle, MediumEditor, (Arc::clone(&this.session), None)).await {
this.select_medium(medium);
@@ -151,7 +154,7 @@ impl Screen, ()> for ImportScreen {
this.load_matches();
- // Copy the tracks in the background, if neccessary.
+ // Copy the tracks in the background, if necessary.
this.session.copy();
this
diff --git a/crates/musicus/src/import/medium_preview.rs b/crates/musicus/src/import/medium_preview.rs
index c39d3e5..88b479d 100644
--- a/crates/musicus/src/import/medium_preview.rs
+++ b/crates/musicus/src/import/medium_preview.rs
@@ -8,7 +8,7 @@ use glib::clone;
use gtk::builders::{FrameBuilder, ListBoxBuilder};
use gtk::prelude::*;
use gtk_macros::get_widget;
-use musicus_backend::db::Medium;
+use musicus_backend::db::{self, Medium};
use musicus_backend::import::{ImportSession, State};
use std::cell::RefCell;
use std::path::PathBuf;
@@ -226,7 +226,7 @@ impl MediumPreview {
);
let directory = PathBuf::from(&directory_name);
- std::fs::create_dir(&music_library_path.join(&directory))?;
+ std::fs::create_dir(music_library_path.join(&directory))?;
// Copy the tracks to the music library.
@@ -243,7 +243,7 @@ impl MediumPreview {
track.path = track_path.to_str().unwrap().to_owned();
// Copy the corresponding audio file to the music library.
- std::fs::copy(&import_track.path, &music_library_path.join(&track_path))?;
+ std::fs::copy(&import_track.path, music_library_path.join(&track_path))?;
tracks.push(track);
}
@@ -257,7 +257,7 @@ impl MediumPreview {
tracks,
);
- self.handle.backend.db().update_medium(medium)?;
+ db::update_medium(&mut self.handle.backend.db().lock().unwrap(), medium)?;
self.handle.backend.library_changed();
Ok(())
diff --git a/crates/musicus/src/screens/ensemble.rs b/crates/musicus/src/screens/ensemble.rs
index c137c23..9a78944 100644
--- a/crates/musicus/src/screens/ensemble.rs
+++ b/crates/musicus/src/screens/ensemble.rs
@@ -7,7 +7,7 @@ use adw::builders::ActionRowBuilder;
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
-use musicus_backend::db::{Ensemble, Medium, Recording};
+use musicus_backend::db::{self, Ensemble, Medium, Recording};
use std::cell::RefCell;
use std::rc::Rc;
@@ -60,7 +60,7 @@ impl Screen for EnsembleScreen {
&gettext("Delete ensemble"),
clone!(@weak this => move || {
spawn!(@clone this, async move {
- this.handle.backend.db().delete_ensemble(&this.ensemble.id).unwrap();
+ db::delete_ensemble(&mut this.handle.backend.db().lock().unwrap(), &this.ensemble.id).unwrap();
this.handle.backend.library_changed();
});
}),
@@ -131,19 +131,17 @@ impl Screen for EnsembleScreen {
// Load the content.
- let recordings = this
- .handle
- .backend
- .db()
- .get_recordings_for_ensemble(&this.ensemble.id)
- .unwrap();
+ let recordings = db::get_recordings_for_ensemble(
+ &mut this.handle.backend.db().lock().unwrap(),
+ &this.ensemble.id,
+ )
+ .unwrap();
- let mediums = this
- .handle
- .backend
- .db()
- .get_mediums_for_ensemble(&this.ensemble.id)
- .unwrap();
+ let mediums = db::get_mediums_for_ensemble(
+ &mut this.handle.backend.db().lock().unwrap(),
+ &this.ensemble.id,
+ )
+ .unwrap();
if !recordings.is_empty() {
let length = recordings.len();
diff --git a/crates/musicus/src/screens/main.rs b/crates/musicus/src/screens/main.rs
index d4c09d4..9c2c38f 100644
--- a/crates/musicus/src/screens/main.rs
+++ b/crates/musicus/src/screens/main.rs
@@ -9,7 +9,7 @@ use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk_macros::get_widget;
-use musicus_backend::db::PersonOrEnsemble;
+use musicus_backend::db::{self, PersonOrEnsemble};
use std::cell::RefCell;
use std::rc::Rc;
@@ -142,8 +142,9 @@ impl Screen<(), ()> for MainScreen {
);
play_button.connect_clicked(clone!(@weak this => move |_| {
- if let Ok(recording) = this.handle.backend.db().random_recording() {
- this.handle.backend.pl().add_items(this.handle.backend.db().get_tracks(&recording.id).unwrap()).unwrap();
+ let recording = db::random_recording(&mut this.handle.backend.db().lock().unwrap());
+ if let Ok(recording) = recording {
+ this.handle.backend.pl().add_items(db::get_tracks(&mut this.handle.backend.db().lock().unwrap(), &recording.id).unwrap()).unwrap();
}
}));
@@ -164,8 +165,8 @@ impl Screen<(), ()> for MainScreen {
let mut poes = Vec::new();
- let persons = this.handle.backend.db().get_persons().unwrap();
- let ensembles = this.handle.backend.db().get_ensembles().unwrap();
+ let persons = db::get_persons(&mut this.handle.backend.db().lock().unwrap(), ).unwrap();
+ let ensembles = db::get_ensembles(&mut this.handle.backend.db().lock().unwrap(), ).unwrap();
for person in persons {
poes.push(PersonOrEnsemble::Person(person));
diff --git a/crates/musicus/src/screens/person.rs b/crates/musicus/src/screens/person.rs
index 7b7fe98..3ceb2f0 100644
--- a/crates/musicus/src/screens/person.rs
+++ b/crates/musicus/src/screens/person.rs
@@ -7,7 +7,7 @@ use adw::builders::ActionRowBuilder;
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
-use musicus_backend::db::{Medium, Person, Recording, Work};
+use musicus_backend::db::{self, Medium, Person, Recording, Work};
use std::cell::RefCell;
use std::rc::Rc;
@@ -65,7 +65,7 @@ impl Screen for PersonScreen {
&gettext("Delete person"),
clone!(@weak this => move || {
spawn!(@clone this, async move {
- this.handle.backend.db().delete_person(&this.person.id).unwrap();
+ db::delete_person(&mut this.handle.backend.db().lock().unwrap(), &this.person.id).unwrap();
this.handle.backend.library_changed();
});
}),
@@ -165,21 +165,23 @@ impl Screen for PersonScreen {
// Load the content.
- let works = this.handle.backend.db().get_works(&this.person.id).unwrap();
+ let works = db::get_works(
+ &mut this.handle.backend.db().lock().unwrap(),
+ &this.person.id,
+ )
+ .unwrap();
- let recordings = this
- .handle
- .backend
- .db()
- .get_recordings_for_person(&this.person.id)
- .unwrap();
+ let recordings = db::get_recordings_for_person(
+ &mut this.handle.backend.db().lock().unwrap(),
+ &this.person.id,
+ )
+ .unwrap();
- let mediums = this
- .handle
- .backend
- .db()
- .get_mediums_for_person(&this.person.id)
- .unwrap();
+ let mediums = db::get_mediums_for_person(
+ &mut this.handle.backend.db().lock().unwrap(),
+ &this.person.id,
+ )
+ .unwrap();
if !works.is_empty() {
let length = works.len();
diff --git a/crates/musicus/src/screens/recording.rs b/crates/musicus/src/screens/recording.rs
index 5485c57..29666b0 100644
--- a/crates/musicus/src/screens/recording.rs
+++ b/crates/musicus/src/screens/recording.rs
@@ -6,7 +6,7 @@ use adw::builders::ActionRowBuilder;
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
-use musicus_backend::db::{Recording, Track};
+use musicus_backend::db::{self, Recording, Track};
use std::cell::RefCell;
use std::rc::Rc;
@@ -64,7 +64,7 @@ impl Screen for RecordingScreen {
&gettext("Delete recording"),
clone!(@weak this => move || {
spawn!(@clone this, async move {
- this.handle.backend.db().delete_recording(&this.recording.id).unwrap();
+ db::delete_recording(&mut this.handle.backend.db().lock().unwrap(), &this.recording.id).unwrap();
this.handle.backend.library_changed();
});
}),
@@ -94,12 +94,11 @@ impl Screen for RecordingScreen {
// Load the content.
- let tracks = this
- .handle
- .backend
- .db()
- .get_tracks(&this.recording.id)
- .unwrap();
+ let tracks = db::get_tracks(
+ &mut this.handle.backend.db().lock().unwrap(),
+ &this.recording.id,
+ )
+ .unwrap();
this.show_tracks(tracks);
this.widget.ready();
diff --git a/crates/musicus/src/screens/work.rs b/crates/musicus/src/screens/work.rs
index f13c352..02780f7 100644
--- a/crates/musicus/src/screens/work.rs
+++ b/crates/musicus/src/screens/work.rs
@@ -7,7 +7,7 @@ use adw::builders::ActionRowBuilder;
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
-use musicus_backend::db::{Recording, Work};
+use musicus_backend::db::{self, Recording, Work};
use std::cell::RefCell;
use std::rc::Rc;
@@ -56,7 +56,7 @@ impl Screen for WorkScreen {
&gettext("Delete work"),
clone!(@weak this => move || {
spawn!(@clone this, async move {
- this.handle.backend.db().delete_work(&this.work.id).unwrap();
+ db::delete_work(&mut this.handle.backend.db().lock().unwrap(), &this.work.id).unwrap();
this.handle.backend.library_changed();
});
}),
@@ -98,12 +98,11 @@ impl Screen for WorkScreen {
// Load the content.
- let recordings = this
- .handle
- .backend
- .db()
- .get_recordings_for_work(&this.work.id)
- .unwrap();
+ let recordings = db::get_recordings_for_work(
+ &mut this.handle.backend.db().lock().unwrap(),
+ &this.work.id,
+ )
+ .unwrap();
if !recordings.is_empty() {
let length = recordings.len();
diff --git a/crates/musicus/src/selectors/ensemble.rs b/crates/musicus/src/selectors/ensemble.rs
index 2e401b5..c73f527 100644
--- a/crates/musicus/src/selectors/ensemble.rs
+++ b/crates/musicus/src/selectors/ensemble.rs
@@ -7,7 +7,7 @@ use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use log::warn;
-use musicus_backend::db::Ensemble;
+use musicus_backend::db::{Ensemble, self};
use std::rc::Rc;
/// A screen for selecting a ensemble.
@@ -50,7 +50,7 @@ impl Screen<(), Ensemble> for EnsembleSelector {
let ensemble = ensemble.to_owned();
row.connect_activated(clone!(@weak this => move |_| {
- if let Err(err) = this.handle.backend.db().update_ensemble(ensemble.clone()) {
+ if let Err(err) = db::update_ensemble(&mut this.handle.backend.db().lock().unwrap(), ensemble.clone()) {
warn!("Failed to update access time. {err}");
}
@@ -64,7 +64,7 @@ impl Screen<(), Ensemble> for EnsembleSelector {
.set_filter(|search, ensemble| ensemble.name.to_lowercase().contains(search));
this.selector
- .set_items(this.handle.backend.db().get_recent_ensembles().unwrap());
+ .set_items(db::get_recent_ensembles(&mut this.handle.backend.db().lock().unwrap(), ).unwrap());
this
}
diff --git a/crates/musicus/src/selectors/instrument.rs b/crates/musicus/src/selectors/instrument.rs
index 26f1a5e..4843b26 100644
--- a/crates/musicus/src/selectors/instrument.rs
+++ b/crates/musicus/src/selectors/instrument.rs
@@ -7,7 +7,7 @@ use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use log::warn;
-use musicus_backend::db::Instrument;
+use musicus_backend::db::{Instrument, self};
use std::rc::Rc;
/// A screen for selecting a instrument.
@@ -50,7 +50,7 @@ impl Screen<(), Instrument> for InstrumentSelector {
let instrument = instrument.to_owned();
row.connect_activated(clone!(@weak this => move |_| {
- if let Err(err) = this.handle.backend.db().update_instrument(instrument.clone()) {
+ if let Err(err) = db::update_instrument(&mut this.handle.backend.db().lock().unwrap(), instrument.clone()) {
warn!("Failed to update access time. {err}");
}
@@ -64,7 +64,7 @@ impl Screen<(), Instrument> for InstrumentSelector {
.set_filter(|search, instrument| instrument.name.to_lowercase().contains(search));
this.selector
- .set_items(this.handle.backend.db().get_recent_instruments().unwrap());
+ .set_items(db::get_recent_instruments(&mut this.handle.backend.db().lock().unwrap(), ).unwrap());
this
}
diff --git a/crates/musicus/src/selectors/medium.rs b/crates/musicus/src/selectors/medium.rs
index 0bb4f1d..ee68e0b 100644
--- a/crates/musicus/src/selectors/medium.rs
+++ b/crates/musicus/src/selectors/medium.rs
@@ -6,7 +6,7 @@ use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use log::warn;
-use musicus_backend::db::{Medium, PersonOrEnsemble};
+use musicus_backend::db::{self, Medium, PersonOrEnsemble};
use std::rc::Rc;
/// A screen for selecting a medium.
@@ -56,8 +56,10 @@ impl Screen<(), Medium> for MediumSelector {
let mut poes = Vec::new();
- let persons = this.handle.backend.db().get_recent_persons().unwrap();
- let ensembles = this.handle.backend.db().get_recent_ensembles().unwrap();
+ let persons =
+ db::get_recent_persons(&mut this.handle.backend.db().lock().unwrap()).unwrap();
+ let ensembles =
+ db::get_recent_ensembles(&mut this.handle.backend.db().lock().unwrap()).unwrap();
for person in persons {
poes.push(PersonOrEnsemble::Person(person));
@@ -111,7 +113,7 @@ impl Screen for MediumSelectorMediumScreen {
let medium = medium.to_owned();
row.connect_activated(clone!(@weak this => move |_| {
- if let Err(err) = this.handle.backend.db().update_medium(medium.clone()) {
+ if let Err(err) = db::update_medium(&mut this.handle.backend.db().lock().unwrap(), medium.clone()) {
warn!("Failed to update access time. {err}");
}
@@ -128,20 +130,20 @@ impl Screen for MediumSelectorMediumScreen {
match this.poe.clone() {
PersonOrEnsemble::Person(person) => {
this.selector.set_items(
- this.handle
- .backend
- .db()
- .get_mediums_for_person(&person.id)
- .unwrap(),
+ db::get_mediums_for_person(
+ &mut this.handle.backend.db().lock().unwrap(),
+ &person.id,
+ )
+ .unwrap(),
);
}
PersonOrEnsemble::Ensemble(ensemble) => {
this.selector.set_items(
- this.handle
- .backend
- .db()
- .get_mediums_for_ensemble(&ensemble.id)
- .unwrap(),
+ db::get_mediums_for_ensemble(
+ &mut this.handle.backend.db().lock().unwrap(),
+ &ensemble.id,
+ )
+ .unwrap(),
);
}
}
diff --git a/crates/musicus/src/selectors/person.rs b/crates/musicus/src/selectors/person.rs
index 001e864..ef9957d 100644
--- a/crates/musicus/src/selectors/person.rs
+++ b/crates/musicus/src/selectors/person.rs
@@ -7,7 +7,7 @@ use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use log::warn;
-use musicus_backend::db::Person;
+use musicus_backend::db::{Person, self};
use std::rc::Rc;
/// A screen for selecting a person.
@@ -50,7 +50,7 @@ impl Screen<(), Person> for PersonSelector {
let person = person.to_owned();
row.connect_activated(clone!(@weak this => move |_| {
- if let Err(err) = this.handle.backend.db().update_person(person.clone()) {
+ if let Err(err) = db::update_person(&mut this.handle.backend.db().lock().unwrap(), person.clone()) {
warn!("Failed to update access time. {err}");
}
@@ -64,7 +64,7 @@ impl Screen<(), Person> for PersonSelector {
.set_filter(|search, person| person.name_fl().to_lowercase().contains(search));
this.selector
- .set_items(this.handle.backend.db().get_recent_persons().unwrap());
+ .set_items(db::get_recent_persons(&mut this.handle.backend.db().lock().unwrap(), ).unwrap());
this
}
diff --git a/crates/musicus/src/selectors/recording.rs b/crates/musicus/src/selectors/recording.rs
index d904ee7..16e693a 100644
--- a/crates/musicus/src/selectors/recording.rs
+++ b/crates/musicus/src/selectors/recording.rs
@@ -7,7 +7,7 @@ use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use log::warn;
-use musicus_backend::db::{Person, Recording, Work};
+use musicus_backend::db::{self, Person, Recording, Work};
use std::rc::Rc;
/// A screen for selecting a recording.
@@ -81,8 +81,9 @@ impl Screen<(), Recording> for RecordingSelector {
this.selector
.set_filter(|search, person| person.name_fl().to_lowercase().contains(search));
- this.selector
- .set_items(this.handle.backend.db().get_recent_persons().unwrap());
+ this.selector.set_items(
+ db::get_recent_persons(&mut this.handle.backend.db().lock().unwrap()).unwrap(),
+ );
this
}
@@ -144,8 +145,13 @@ impl Screen for RecordingSelectorWorkScreen {
this.selector
.set_filter(|search, work| work.title.to_lowercase().contains(search));
- this.selector
- .set_items(this.handle.backend.db().get_works(&this.person.id).unwrap());
+ this.selector.set_items(
+ db::get_works(
+ &mut this.handle.backend.db().lock().unwrap(),
+ &this.person.id,
+ )
+ .unwrap(),
+ );
this
}
@@ -198,7 +204,7 @@ impl Screen for RecordingSelectorRecordingScreen {
let recording = recording.to_owned();
row.connect_activated(clone!(@weak this => move |_| {
- if let Err(err) = this.handle.backend.db().update_recording(recording.clone()) {
+ if let Err(err) = db::update_recording(&mut this.handle.backend.db().lock().unwrap(), recording.clone()) {
warn!("Failed to update access time. {err}");
}
@@ -213,11 +219,11 @@ impl Screen for RecordingSelectorRecordingScreen {
});
this.selector.set_items(
- this.handle
- .backend
- .db()
- .get_recordings_for_work(&this.work.id)
- .unwrap(),
+ db::get_recordings_for_work(
+ &mut this.handle.backend.db().lock().unwrap(),
+ &this.work.id,
+ )
+ .unwrap(),
);
this
diff --git a/crates/musicus/src/selectors/work.rs b/crates/musicus/src/selectors/work.rs
index e12f125..3fd9697 100644
--- a/crates/musicus/src/selectors/work.rs
+++ b/crates/musicus/src/selectors/work.rs
@@ -7,7 +7,7 @@ use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use log::warn;
-use musicus_backend::db::{Person, Work};
+use musicus_backend::db::{Person, Work, self};
use std::rc::Rc;
/// A screen for selecting a work.
@@ -72,7 +72,7 @@ impl Screen<(), Work> for WorkSelector {
.set_filter(|search, person| person.name_fl().to_lowercase().contains(search));
this.selector
- .set_items(this.handle.backend.db().get_recent_persons().unwrap());
+ .set_items(db::get_recent_persons(&mut this.handle.backend.db().lock().unwrap()).unwrap());
this
}
@@ -125,7 +125,7 @@ impl Screen for WorkSelectorWorkScreen {
let work = work.to_owned();
row.connect_activated(clone!(@weak this => move |_| {
- if let Err(err) = this.handle.backend.db().update_work(work.clone()) {
+ if let Err(err) = db::update_work(&mut this.handle.backend.db().lock().unwrap(), work.clone()) {
warn!("Failed to update access time. {err}");
}
@@ -139,7 +139,7 @@ impl Screen for WorkSelectorWorkScreen {
.set_filter(|search, work| work.title.to_lowercase().contains(search));
this.selector
- .set_items(this.handle.backend.db().get_works(&this.person.id).unwrap());
+ .set_items(db::get_works(&mut this.handle.backend.db().lock().unwrap(), &this.person.id).unwrap());
this
}