client: Switch to string IDs

This commit is contained in:
Elias Projahn 2020-11-28 21:45:22 +01:00
parent 157bdb2917
commit 5c3377e246
24 changed files with 249 additions and 363 deletions

View file

@ -25,3 +25,4 @@ rand = "0.7.3"
secret-service = "1.1.1" secret-service = "1.1.1"
serde = { version = "1.0.117", features = ["derive"] } serde = { version = "1.0.117", features = ["derive"] }
serde_json = "1.0.59" serde_json = "1.0.59"
uuid = { version = "0.8", features = ["v4"] }

View file

@ -1,64 +1,64 @@
CREATE TABLE persons ( CREATE TABLE persons (
id BIGINT NOT NULL PRIMARY KEY, id TEXT NOT NULL PRIMARY KEY,
first_name TEXT NOT NULL, first_name TEXT NOT NULL,
last_name TEXT NOT NULL last_name TEXT NOT NULL
); );
CREATE TABLE instruments ( CREATE TABLE instruments (
id BIGINT NOT NULL PRIMARY KEY, id TEXT NOT NULL PRIMARY KEY,
name TEXT NOT NULL name TEXT NOT NULL
); );
CREATE TABLE works ( CREATE TABLE works (
id BIGINT NOT NULL PRIMARY KEY, id TEXT NOT NULL PRIMARY KEY,
composer BIGINT NOT NULL REFERENCES persons(id), composer TEXT NOT NULL REFERENCES persons(id),
title TEXT NOT NULL title TEXT NOT NULL
); );
CREATE TABLE instrumentations ( CREATE TABLE instrumentations (
id BIGINT NOT NULL PRIMARY KEY, id BIGINT NOT NULL PRIMARY KEY,
work BIGINT NOT NULL REFERENCES works(id) ON DELETE CASCADE, work TEXT NOT NULL REFERENCES works(id) ON DELETE CASCADE,
instrument BIGINT NOT NULL REFERENCES instruments(id) ON DELETE CASCADE instrument TEXT NOT NULL REFERENCES instruments(id) ON DELETE CASCADE
); );
CREATE TABLE work_parts ( CREATE TABLE work_parts (
id BIGINT NOT NULL PRIMARY KEY, id BIGINT NOT NULL PRIMARY KEY,
work BIGINT NOT NULL REFERENCES works(id) ON DELETE CASCADE, work TEXT NOT NULL REFERENCES works(id) ON DELETE CASCADE,
part_index BIGINT NOT NULL, part_index BIGINT NOT NULL,
title TEXT NOT NULL, title TEXT NOT NULL,
composer BIGINT REFERENCES persons(id) composer TEXT REFERENCES persons(id)
); );
CREATE TABLE work_sections ( CREATE TABLE work_sections (
id BIGINT NOT NULL PRIMARY KEY, id BIGINT NOT NULL PRIMARY KEY,
work BIGINT NOT NULL REFERENCES works(id) ON DELETE CASCADE, work TEXT NOT NULL REFERENCES works(id) ON DELETE CASCADE,
title TEXT NOT NULL, title TEXT NOT NULL,
before_index BIGINT NOT NULL before_index BIGINT NOT NULL
); );
CREATE TABLE ensembles ( CREATE TABLE ensembles (
id BIGINT NOT NULL PRIMARY KEY, id TEXT NOT NULL PRIMARY KEY,
name TEXT NOT NULL name TEXT NOT NULL
); );
CREATE TABLE recordings ( CREATE TABLE recordings (
id BIGINT NOT NULL PRIMARY KEY, id TEXT NOT NULL PRIMARY KEY,
work BIGINT NOT NULL REFERENCES works(id), work TEXT NOT NULL REFERENCES works(id),
comment TEXT NOT NULL comment TEXT NOT NULL
); );
CREATE TABLE performances ( CREATE TABLE performances (
id BIGINT NOT NULL PRIMARY KEY, id BIGINT NOT NULL PRIMARY KEY,
recording BIGINT NOT NULL REFERENCES recordings(id) ON DELETE CASCADE, recording TEXT NOT NULL REFERENCES recordings(id) ON DELETE CASCADE,
person BIGINT REFERENCES persons(id), person TEXT REFERENCES persons(id),
ensemble BIGINT REFERENCES ensembles(id), ensemble TEXT REFERENCES ensembles(id),
role BIGINT REFERENCES instruments(id) role TEXT REFERENCES instruments(id)
); );
CREATE TABLE tracks ( CREATE TABLE tracks (
id BIGINT NOT NULL PRIMARY KEY, id BIGINT NOT NULL PRIMARY KEY,
file_name TEXT NOT NULL, file_name TEXT NOT NULL,
recording BIGINT NOT NULL REFERENCES recordings(id), recording TEXT NOT NULL REFERENCES recordings(id),
track_index INTEGER NOT NULL, track_index INTEGER NOT NULL,
work_parts TEXT NOT NULL work_parts TEXT NOT NULL
); );

View file

@ -1,57 +1,25 @@
use super::schema::ensembles; use super::schema::ensembles;
use super::Database; use super::Database;
use anyhow::{Error, Result}; use anyhow::Result;
use diesel::prelude::*; use diesel::prelude::*;
use diesel::{Insertable, Queryable};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
/// Database table data for an ensemble.
#[derive(Insertable, Queryable, Debug, Clone)]
#[table_name = "ensembles"]
struct EnsembleRow {
pub id: i64,
pub name: String,
}
impl From<Ensemble> for EnsembleRow {
fn from(ensemble: Ensemble) -> Self {
EnsembleRow {
id: ensemble.id as i64,
name: ensemble.name,
}
}
}
/// An ensemble that takes part in recordings. /// An ensemble that takes part in recordings.
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Insertable, Queryable, Debug, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Ensemble { pub struct Ensemble {
pub id: u32, pub id: String,
pub name: String, pub name: String,
} }
impl TryFrom<EnsembleRow> for Ensemble {
type Error = Error;
fn try_from(row: EnsembleRow) -> Result<Self> {
let ensemble = Ensemble {
id: row.id.try_into()?,
name: row.name,
};
Ok(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) -> Result<()> {
self.defer_foreign_keys()?; self.defer_foreign_keys()?;
self.connection.transaction(|| { self.connection.transaction(|| {
let row: EnsembleRow = ensemble.into();
diesel::replace_into(ensembles::table) diesel::replace_into(ensembles::table)
.values(row) .values(ensemble)
.execute(&self.connection) .execute(&self.connection)
})?; })?;
@ -59,37 +27,26 @@ impl Database {
} }
/// Get an existing ensemble. /// Get an existing ensemble.
pub fn get_ensemble(&self, id: u32) -> Result<Option<Ensemble>> { pub fn get_ensemble(&self, id: &str) -> Result<Option<Ensemble>> {
let row = ensembles::table let ensemble = ensembles::table
.filter(ensembles::id.eq(id as i64)) .filter(ensembles::id.eq(id))
.load::<EnsembleRow>(&self.connection)? .load::<Ensemble>(&self.connection)?
.first() .into_iter()
.cloned(); .next();
let ensemble = match row {
Some(row) => Some(row.try_into()?),
None => None,
};
Ok(ensemble) Ok(ensemble)
} }
/// Delete an existing ensemble. /// Delete an existing ensemble.
pub fn delete_ensemble(&self, id: u32) -> Result<()> { pub fn delete_ensemble(&self, id: &str) -> Result<()> {
diesel::delete(ensembles::table.filter(ensembles::id.eq(id as i64))) diesel::delete(ensembles::table.filter(ensembles::id.eq(id))).execute(&self.connection)?;
.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) -> Result<Vec<Ensemble>> {
let mut ensembles = Vec::<Ensemble>::new(); let ensembles = ensembles::table.load::<Ensemble>(&self.connection)?;
let rows = ensembles::table.load::<EnsembleRow>(&self.connection)?;
for row in rows {
ensembles.push(row.try_into()?);
}
Ok(ensembles) Ok(ensembles)
} }

View file

@ -1,57 +1,25 @@
use super::schema::instruments; use super::schema::instruments;
use super::Database; use super::Database;
use anyhow::{Error, Result}; use anyhow::Result;
use diesel::prelude::*; use diesel::prelude::*;
use diesel::{Insertable, Queryable};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
/// Table row data for an instrument.
#[derive(Insertable, Queryable, Debug, Clone)]
#[table_name = "instruments"]
struct InstrumentRow {
pub id: i64,
pub name: String,
}
impl From<Instrument> for InstrumentRow {
fn from(instrument: Instrument) -> Self {
InstrumentRow {
id: instrument.id as i64,
name: instrument.name,
}
}
}
/// An instrument or any other possible role within a recording. /// An instrument or any other possible role within a recording.
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Insertable, Queryable, Debug, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Instrument { pub struct Instrument {
pub id: u32, pub id: String,
pub name: String, pub name: String,
} }
impl TryFrom<InstrumentRow> for Instrument {
type Error = Error;
fn try_from(row: InstrumentRow) -> Result<Self> {
let instrument = Instrument {
id: row.id.try_into()?,
name: row.name,
};
Ok(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) -> Result<()> {
self.defer_foreign_keys()?; self.defer_foreign_keys()?;
self.connection.transaction(|| { self.connection.transaction(|| {
let row: InstrumentRow = instrument.into();
diesel::replace_into(instruments::table) diesel::replace_into(instruments::table)
.values(row) .values(instrument)
.execute(&self.connection) .execute(&self.connection)
})?; })?;
@ -59,24 +27,19 @@ impl Database {
} }
/// Get an existing instrument. /// Get an existing instrument.
pub fn get_instrument(&self, id: u32) -> Result<Option<Instrument>> { pub fn get_instrument(&self, id: &str) -> Result<Option<Instrument>> {
let row = instruments::table let instrument = instruments::table
.filter(instruments::id.eq(id as i64)) .filter(instruments::id.eq(id))
.load::<InstrumentRow>(&self.connection)? .load::<Instrument>(&self.connection)?
.first() .into_iter()
.cloned(); .next();
let instrument = match row {
Some(row) => Some(row.try_into()?),
None => None,
};
Ok(instrument) Ok(instrument)
} }
/// Delete an existing instrument. /// Delete an existing instrument.
pub fn delete_instrument(&self, id: u32) -> Result<()> { pub fn delete_instrument(&self, id: &str) -> Result<()> {
diesel::delete(instruments::table.filter(instruments::id.eq(id as i64))) diesel::delete(instruments::table.filter(instruments::id.eq(id)))
.execute(&self.connection)?; .execute(&self.connection)?;
Ok(()) Ok(())
@ -84,12 +47,7 @@ impl Database {
/// Get all existing instruments. /// Get all existing instruments.
pub fn get_instruments(&self) -> Result<Vec<Instrument>> { pub fn get_instruments(&self) -> Result<Vec<Instrument>> {
let mut instruments = Vec::<Instrument>::new(); let instruments = instruments::table.load::<Instrument>(&self.connection)?;
let rows = instruments::table.load::<InstrumentRow>(&self.connection)?;
for row in rows {
instruments.push(row.try_into()?);
}
Ok(instruments) Ok(instruments)
} }

View file

@ -27,6 +27,14 @@ mod schema;
// This makes the SQL migration scripts accessible from the code. // This makes the SQL migration scripts accessible from the code.
embed_migrations!(); embed_migrations!();
/// Generate a random string suitable as an item ID.
pub fn generate_id() -> String {
let mut buffer = uuid::Uuid::encode_buffer();
let id = uuid::Uuid::new_v4().to_simple().encode_lower(&mut buffer);
id.to_string()
}
/// Interface to a Musicus database. /// Interface to a Musicus database.
pub struct Database { pub struct Database {
connection: SqliteConnection, connection: SqliteConnection,

View file

@ -1,52 +1,18 @@
use super::schema::persons; use super::schema::persons;
use super::Database; use super::Database;
use anyhow::{Error, Result}; use anyhow::Result;
use diesel::prelude::*; use diesel::prelude::*;
use diesel::{Insertable, Queryable};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
/// Database table data for a person.
#[derive(Insertable, Queryable, Debug, Clone)]
#[table_name = "persons"]
struct PersonRow {
pub id: i64,
pub first_name: String,
pub last_name: String,
}
impl From<Person> for PersonRow {
fn from(person: Person) -> Self {
PersonRow {
id: person.id as i64,
first_name: person.first_name,
last_name: person.last_name,
}
}
}
/// A person that is a composer, an interpret or both. /// A person that is a composer, an interpret or both.
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Insertable, Queryable, Debug, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Person { pub struct Person {
pub id: u32, pub id: String,
pub first_name: String, pub first_name: String,
pub last_name: String, pub last_name: String,
} }
impl TryFrom<PersonRow> for Person {
type Error = Error;
fn try_from(row: PersonRow) -> Result<Self> {
let person = Person {
id: row.id.try_into()?,
first_name: row.first_name,
last_name: row.last_name,
};
Ok(person)
}
}
impl Person { impl Person {
/// Get the full name in the form "First Last". /// Get the full name in the form "First Last".
pub fn name_fl(&self) -> String { pub fn name_fl(&self) -> String {
@ -65,9 +31,8 @@ impl Database {
self.defer_foreign_keys()?; self.defer_foreign_keys()?;
self.connection.transaction(|| { self.connection.transaction(|| {
let row: PersonRow = person.into();
diesel::replace_into(persons::table) diesel::replace_into(persons::table)
.values(row) .values(person)
.execute(&self.connection) .execute(&self.connection)
})?; })?;
@ -75,36 +40,26 @@ impl Database {
} }
/// Get an existing person. /// Get an existing person.
pub fn get_person(&self, id: u32) -> Result<Option<Person>> { pub fn get_person(&self, id: &str) -> Result<Option<Person>> {
let row = persons::table let person = persons::table
.filter(persons::id.eq(id as i64)) .filter(persons::id.eq(id))
.load::<PersonRow>(&self.connection)? .load::<Person>(&self.connection)?
.first() .into_iter()
.cloned(); .next();
let person = match row {
Some(row) => Some(row.try_into()?),
None => None,
};
Ok(person) Ok(person)
} }
/// Delete an existing person. /// Delete an existing person.
pub fn delete_person(&self, id: u32) -> Result<()> { pub fn delete_person(&self, id: &str) -> Result<()> {
diesel::delete(persons::table.filter(persons::id.eq(id as i64))) diesel::delete(persons::table.filter(persons::id.eq(id))).execute(&self.connection)?;
.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) -> Result<Vec<Person>> {
let mut persons = Vec::<Person>::new(); let persons = persons::table.load::<Person>(&self.connection)?;
let rows = persons::table.load::<PersonRow>(&self.connection)?;
for row in rows {
persons.push(row.try_into()?);
}
Ok(persons) Ok(persons)
} }

View file

@ -10,16 +10,16 @@ use std::convert::TryInto;
#[derive(Insertable, Queryable, Debug, Clone)] #[derive(Insertable, Queryable, Debug, Clone)]
#[table_name = "recordings"] #[table_name = "recordings"]
struct RecordingRow { struct RecordingRow {
pub id: i64, pub id: String,
pub work: i64, pub work: String,
pub comment: String, pub comment: String,
} }
impl From<Recording> for RecordingRow { impl From<Recording> for RecordingRow {
fn from(recording: Recording) -> Self { fn from(recording: Recording) -> Self {
RecordingRow { RecordingRow {
id: recording.id as i64, id: recording.id,
work: recording.work.id as i64, work: recording.work.id,
comment: recording.comment, comment: recording.comment,
} }
} }
@ -30,10 +30,10 @@ impl From<Recording> for RecordingRow {
#[table_name = "performances"] #[table_name = "performances"]
struct PerformanceRow { struct PerformanceRow {
pub id: i64, pub id: i64,
pub recording: i64, pub recording: String,
pub person: Option<i64>, pub person: Option<String>,
pub ensemble: Option<i64>, pub ensemble: Option<String>,
pub role: Option<i64>, pub role: Option<String>,
} }
/// How a person or ensemble was involved in a recording. /// How a person or ensemble was involved in a recording.
@ -88,7 +88,7 @@ impl Performance {
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Recording { pub struct Recording {
pub id: u32, pub id: String,
pub work: Work, pub work: Work,
pub comment: String, pub comment: String,
pub performances: Vec<Performance>, pub performances: Vec<Performance>,
@ -114,9 +114,9 @@ impl Database {
pub fn update_recording(&self, recording: Recording) -> Result<()> { pub fn update_recording(&self, recording: Recording) -> Result<()> {
self.defer_foreign_keys()?; self.defer_foreign_keys()?;
self.connection.transaction::<(), Error, _>(|| { self.connection.transaction::<(), Error, _>(|| {
self.delete_recording(recording.id)?; let recording_id = &recording.id;
self.delete_recording(recording_id)?;
let recording_id = recording.id as i64;
let row: RecordingRow = recording.clone().into(); let row: RecordingRow = recording.clone().into();
diesel::insert_into(recordings::table) diesel::insert_into(recordings::table)
.values(row) .values(row)
@ -125,10 +125,10 @@ impl Database {
for performance in recording.performances { for performance in recording.performances {
let row = PerformanceRow { let row = PerformanceRow {
id: rand::random(), id: rand::random(),
recording: recording_id, recording: recording_id.to_string(),
person: performance.person.map(|person| person.id as i64), person: performance.person.map(|person| person.id),
ensemble: performance.ensemble.map(|ensemble| ensemble.id as i64), ensemble: performance.ensemble.map(|ensemble| ensemble.id),
role: performance.role.map(|role| role.id as i64), role: performance.role.map(|role| role.id),
}; };
diesel::insert_into(performances::table) diesel::insert_into(performances::table)
@ -147,28 +147,28 @@ impl Database {
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
.filter(performances::recording.eq(row.id)) .filter(performances::recording.eq(&row.id))
.load::<PerformanceRow>(&self.connection)?; .load::<PerformanceRow>(&self.connection)?;
for row in performance_rows { for row in performance_rows {
performance_descriptions.push(Performance { performance_descriptions.push(Performance {
person: match row.person { person: match row.person {
Some(id) => Some( Some(id) => Some(
self.get_person(id.try_into()?)? self.get_person(&id)?
.ok_or(anyhow!("No person with ID: {}", id))?, .ok_or(anyhow!("No person with ID: {}", id))?,
), ),
None => None, None => None,
}, },
ensemble: match row.ensemble { ensemble: match row.ensemble {
Some(id) => Some( Some(id) => Some(
self.get_ensemble(id.try_into()?)? self.get_ensemble(&id)?
.ok_or(anyhow!("No ensemble with ID: {}", id))?, .ok_or(anyhow!("No ensemble with ID: {}", id))?,
), ),
None => None, None => None,
}, },
role: match row.role { role: match row.role {
Some(id) => Some( Some(id) => Some(
self.get_instrument(id.try_into()?)? self.get_instrument(&id)?
.ok_or(anyhow!("No instrument with ID: {}", id))?, .ok_or(anyhow!("No instrument with ID: {}", id))?,
), ),
None => None, None => None,
@ -176,13 +176,13 @@ impl Database {
}); });
} }
let work_id: u32 = row.work.try_into()?; 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(anyhow!("Work doesn't exist: {}", work_id))?;
let recording_description = Recording { let recording_description = Recording {
id: row.id.try_into()?, id: row.id,
work, work,
comment: row.comment.clone(), comment: row.comment.clone(),
performances: performance_descriptions, performances: performance_descriptions,
@ -192,13 +192,13 @@ 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: u32) -> Result<Vec<Recording>> { pub fn get_recordings_for_person(&self, person_id: &str) -> Result<Vec<Recording>> {
let mut recordings: Vec<Recording> = Vec::new(); let mut recordings: Vec<Recording> = Vec::new();
let rows = recordings::table let rows = recordings::table
.inner_join(performances::table.on(performances::recording.eq(recordings::id))) .inner_join(performances::table.on(performances::recording.eq(recordings::id)))
.inner_join(persons::table.on(persons::id.nullable().eq(performances::person))) .inner_join(persons::table.on(persons::id.nullable().eq(performances::person)))
.filter(persons::id.eq(person_id as i64)) .filter(persons::id.eq(person_id))
.select(recordings::table::all_columns()) .select(recordings::table::all_columns())
.load::<RecordingRow>(&self.connection)?; .load::<RecordingRow>(&self.connection)?;
@ -210,13 +210,13 @@ 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: u32) -> Result<Vec<Recording>> { pub fn get_recordings_for_ensemble(&self, ensemble_id: &str) -> Result<Vec<Recording>> {
let mut recordings: Vec<Recording> = Vec::new(); let mut recordings: Vec<Recording> = Vec::new();
let rows = recordings::table let rows = recordings::table
.inner_join(performances::table.on(performances::recording.eq(recordings::id))) .inner_join(performances::table.on(performances::recording.eq(recordings::id)))
.inner_join(ensembles::table.on(ensembles::id.nullable().eq(performances::ensemble))) .inner_join(ensembles::table.on(ensembles::id.nullable().eq(performances::ensemble)))
.filter(ensembles::id.eq(ensemble_id as i64)) .filter(ensembles::id.eq(ensemble_id))
.select(recordings::table::all_columns()) .select(recordings::table::all_columns())
.load::<RecordingRow>(&self.connection)?; .load::<RecordingRow>(&self.connection)?;
@ -228,11 +228,11 @@ 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: u32) -> Result<Vec<Recording>> { pub fn get_recordings_for_work(&self, work_id: &str) -> Result<Vec<Recording>> {
let mut recordings: Vec<Recording> = Vec::new(); let mut recordings: Vec<Recording> = Vec::new();
let rows = recordings::table let rows = recordings::table
.filter(recordings::work.eq(work_id as i64)) .filter(recordings::work.eq(work_id))
.load::<RecordingRow>(&self.connection)?; .load::<RecordingRow>(&self.connection)?;
for row in rows { for row in rows {
@ -244,8 +244,8 @@ 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: u32) -> Result<()> { pub fn delete_recording(&self, id: &str) -> Result<()> {
diesel::delete(recordings::table.filter(recordings::id.eq(id as i64))) diesel::delete(recordings::table.filter(recordings::id.eq(id)))
.execute(&self.connection)?; .execute(&self.connection)?;
Ok(()) Ok(())
} }

View file

@ -1,6 +1,6 @@
table! { table! {
ensembles (id) { ensembles (id) {
id -> BigInt, id -> Text,
name -> Text, name -> Text,
} }
} }
@ -8,14 +8,14 @@ table! {
table! { table! {
instrumentations (id) { instrumentations (id) {
id -> BigInt, id -> BigInt,
work -> BigInt, work -> Text,
instrument -> BigInt, instrument -> Text,
} }
} }
table! { table! {
instruments (id) { instruments (id) {
id -> BigInt, id -> Text,
name -> Text, name -> Text,
} }
} }
@ -23,16 +23,16 @@ table! {
table! { table! {
performances (id) { performances (id) {
id -> BigInt, id -> BigInt,
recording -> BigInt, recording -> Text,
person -> Nullable<BigInt>, person -> Nullable<Text>,
ensemble -> Nullable<BigInt>, ensemble -> Nullable<Text>,
role -> Nullable<BigInt>, role -> Nullable<Text>,
} }
} }
table! { table! {
persons (id) { persons (id) {
id -> BigInt, id -> Text,
first_name -> Text, first_name -> Text,
last_name -> Text, last_name -> Text,
} }
@ -40,8 +40,8 @@ table! {
table! { table! {
recordings (id) { recordings (id) {
id -> BigInt, id -> Text,
work -> BigInt, work -> Text,
comment -> Text, comment -> Text,
} }
} }
@ -50,7 +50,7 @@ table! {
tracks (id) { tracks (id) {
id -> BigInt, id -> BigInt,
file_name -> Text, file_name -> Text,
recording -> BigInt, recording -> Text,
track_index -> Integer, track_index -> Integer,
work_parts -> Text, work_parts -> Text,
} }
@ -59,17 +59,17 @@ table! {
table! { table! {
work_parts (id) { work_parts (id) {
id -> BigInt, id -> BigInt,
work -> BigInt, work -> Text,
part_index -> BigInt, part_index -> BigInt,
title -> Text, title -> Text,
composer -> Nullable<BigInt>, composer -> Nullable<Text>,
} }
} }
table! { table! {
work_sections (id) { work_sections (id) {
id -> BigInt, id -> BigInt,
work -> BigInt, work -> Text,
title -> Text, title -> Text,
before_index -> BigInt, before_index -> BigInt,
} }
@ -77,8 +77,8 @@ table! {
table! { table! {
works (id) { works (id) {
id -> BigInt, id -> Text,
composer -> BigInt, composer -> Text,
title -> Text, title -> Text,
} }
} }

View file

@ -8,28 +8,28 @@ use std::thread;
/// An action the database thread can perform. /// An action the database thread can perform.
enum Action { enum Action {
UpdatePerson(Person, Sender<Result<()>>), UpdatePerson(Person, Sender<Result<()>>),
GetPerson(u32, Sender<Result<Option<Person>>>), GetPerson(String, Sender<Result<Option<Person>>>),
DeletePerson(u32, Sender<Result<()>>), DeletePerson(String, Sender<Result<()>>),
GetPersons(Sender<Result<Vec<Person>>>), GetPersons(Sender<Result<Vec<Person>>>),
UpdateInstrument(Instrument, Sender<Result<()>>), UpdateInstrument(Instrument, Sender<Result<()>>),
GetInstrument(u32, Sender<Result<Option<Instrument>>>), GetInstrument(String, Sender<Result<Option<Instrument>>>),
DeleteInstrument(u32, Sender<Result<()>>), DeleteInstrument(String, Sender<Result<()>>),
GetInstruments(Sender<Result<Vec<Instrument>>>), GetInstruments(Sender<Result<Vec<Instrument>>>),
UpdateWork(Work, Sender<Result<()>>), UpdateWork(Work, Sender<Result<()>>),
DeleteWork(u32, Sender<Result<()>>), DeleteWork(String, Sender<Result<()>>),
GetWorks(u32, Sender<Result<Vec<Work>>>), GetWorks(String, Sender<Result<Vec<Work>>>),
UpdateEnsemble(Ensemble, Sender<Result<()>>), UpdateEnsemble(Ensemble, Sender<Result<()>>),
GetEnsemble(u32, Sender<Result<Option<Ensemble>>>), GetEnsemble(String, Sender<Result<Option<Ensemble>>>),
DeleteEnsemble(u32, Sender<Result<()>>), DeleteEnsemble(String, Sender<Result<()>>),
GetEnsembles(Sender<Result<Vec<Ensemble>>>), GetEnsembles(Sender<Result<Vec<Ensemble>>>),
UpdateRecording(Recording, Sender<Result<()>>), UpdateRecording(Recording, Sender<Result<()>>),
DeleteRecording(u32, Sender<Result<()>>), DeleteRecording(String, Sender<Result<()>>),
GetRecordingsForPerson(u32, Sender<Result<Vec<Recording>>>), GetRecordingsForPerson(String, Sender<Result<Vec<Recording>>>),
GetRecordingsForEnsemble(u32, Sender<Result<Vec<Recording>>>), GetRecordingsForEnsemble(String, Sender<Result<Vec<Recording>>>),
GetRecordingsForWork(u32, Sender<Result<Vec<Recording>>>), GetRecordingsForWork(String, Sender<Result<Vec<Recording>>>),
UpdateTracks(u32, Vec<Track>, Sender<Result<()>>), UpdateTracks(String, Vec<Track>, Sender<Result<()>>),
DeleteTracks(u32, Sender<Result<()>>), DeleteTracks(String, Sender<Result<()>>),
GetTracks(u32, Sender<Result<Vec<Track>>>), GetTracks(String, Sender<Result<Vec<Track>>>),
Stop(Sender<()>), Stop(Sender<()>),
} }
@ -64,10 +64,10 @@ impl DbThread {
sender.send(db.update_person(person)).unwrap(); sender.send(db.update_person(person)).unwrap();
} }
GetPerson(id, sender) => { GetPerson(id, sender) => {
sender.send(db.get_person(id)).unwrap(); sender.send(db.get_person(&id)).unwrap();
} }
DeletePerson(id, sender) => { DeletePerson(id, sender) => {
sender.send(db.delete_person(id)).unwrap(); sender.send(db.delete_person(&id)).unwrap();
} }
GetPersons(sender) => { GetPersons(sender) => {
sender.send(db.get_persons()).unwrap(); sender.send(db.get_persons()).unwrap();
@ -76,10 +76,10 @@ impl DbThread {
sender.send(db.update_instrument(instrument)).unwrap(); sender.send(db.update_instrument(instrument)).unwrap();
} }
GetInstrument(id, sender) => { GetInstrument(id, sender) => {
sender.send(db.get_instrument(id)).unwrap(); sender.send(db.get_instrument(&id)).unwrap();
} }
DeleteInstrument(id, sender) => { DeleteInstrument(id, sender) => {
sender.send(db.delete_instrument(id)).unwrap(); sender.send(db.delete_instrument(&id)).unwrap();
} }
GetInstruments(sender) => { GetInstruments(sender) => {
sender.send(db.get_instruments()).unwrap(); sender.send(db.get_instruments()).unwrap();
@ -88,19 +88,19 @@ impl DbThread {
sender.send(db.update_work(work)).unwrap(); sender.send(db.update_work(work)).unwrap();
} }
DeleteWork(id, sender) => { DeleteWork(id, sender) => {
sender.send(db.delete_work(id)).unwrap(); sender.send(db.delete_work(&id)).unwrap();
} }
GetWorks(id, sender) => { GetWorks(id, sender) => {
sender.send(db.get_works(id)).unwrap(); sender.send(db.get_works(&id)).unwrap();
} }
UpdateEnsemble(ensemble, sender) => { UpdateEnsemble(ensemble, sender) => {
sender.send(db.update_ensemble(ensemble)).unwrap(); sender.send(db.update_ensemble(ensemble)).unwrap();
} }
GetEnsemble(id, sender) => { GetEnsemble(id, sender) => {
sender.send(db.get_ensemble(id)).unwrap(); sender.send(db.get_ensemble(&id)).unwrap();
} }
DeleteEnsemble(id, sender) => { DeleteEnsemble(id, sender) => {
sender.send(db.delete_ensemble(id)).unwrap(); sender.send(db.delete_ensemble(&id)).unwrap();
} }
GetEnsembles(sender) => { GetEnsembles(sender) => {
sender.send(db.get_ensembles()).unwrap(); sender.send(db.get_ensembles()).unwrap();
@ -109,25 +109,27 @@ impl DbThread {
sender.send(db.update_recording(recording)).unwrap(); sender.send(db.update_recording(recording)).unwrap();
} }
DeleteRecording(id, sender) => { DeleteRecording(id, sender) => {
sender.send(db.delete_recording(id)).unwrap(); sender.send(db.delete_recording(&id)).unwrap();
} }
GetRecordingsForPerson(id, sender) => { GetRecordingsForPerson(id, sender) => {
sender.send(db.get_recordings_for_person(id)).unwrap(); sender.send(db.get_recordings_for_person(&id)).unwrap();
} }
GetRecordingsForEnsemble(id, sender) => { GetRecordingsForEnsemble(id, sender) => {
sender.send(db.get_recordings_for_ensemble(id)).unwrap(); sender.send(db.get_recordings_for_ensemble(&id)).unwrap();
} }
GetRecordingsForWork(id, sender) => { GetRecordingsForWork(id, sender) => {
sender.send(db.get_recordings_for_work(id)).unwrap(); sender.send(db.get_recordings_for_work(&id)).unwrap();
} }
UpdateTracks(recording_id, tracks, sender) => { UpdateTracks(recording_id, tracks, sender) => {
sender.send(db.update_tracks(recording_id, tracks)).unwrap(); sender
.send(db.update_tracks(&recording_id, tracks))
.unwrap();
} }
DeleteTracks(recording_id, sender) => { DeleteTracks(recording_id, sender) => {
sender.send(db.delete_tracks(recording_id)).unwrap(); sender.send(db.delete_tracks(&recording_id)).unwrap();
} }
GetTracks(recording_id, sender) => { GetTracks(recording_id, sender) => {
sender.send(db.get_tracks(recording_id)).unwrap(); sender.send(db.get_tracks(&recording_id)).unwrap();
} }
Stop(sender) => { Stop(sender) => {
sender.send(()).unwrap(); sender.send(()).unwrap();
@ -149,17 +151,18 @@ impl DbThread {
} }
/// Get an existing person. /// Get an existing person.
pub async fn get_person(&self, id: u32) -> Result<Option<Person>> { pub async fn get_person(&self, id: &str) -> Result<Option<Person>> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetPerson(id, sender))?; self.action_sender.send(GetPerson(id.to_string(), sender))?;
receiver.await? receiver.await?
} }
/// 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: u32) -> Result<()> { pub async fn delete_person(&self, id: &str) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(DeletePerson(id, sender))?; self.action_sender
.send(DeletePerson(id.to_string(), sender))?;
receiver.await? receiver.await?
} }
@ -179,17 +182,19 @@ impl DbThread {
} }
/// Get an existing instrument. /// Get an existing instrument.
pub async fn get_instrument(&self, id: u32) -> Result<Option<Instrument>> { pub async fn get_instrument(&self, id: &str) -> Result<Option<Instrument>> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetInstrument(id, sender))?; self.action_sender
.send(GetInstrument(id.to_string(), sender))?;
receiver.await? receiver.await?
} }
/// 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: u32) -> Result<()> { pub async fn delete_instrument(&self, id: &str) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(DeleteInstrument(id, sender))?; self.action_sender
.send(DeleteInstrument(id.to_string(), sender))?;
receiver.await? receiver.await?
} }
@ -209,16 +214,18 @@ 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: u32) -> Result<()> { pub async fn delete_work(&self, id: &str) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(DeleteWork(id, sender))?; self.action_sender
.send(DeleteWork(id.to_string(), sender))?;
receiver.await? receiver.await?
} }
/// 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: u32) -> Result<Vec<Work>> { pub async fn get_works(&self, person_id: &str) -> Result<Vec<Work>> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetWorks(person_id, sender))?; self.action_sender
.send(GetWorks(person_id.to_string(), sender))?;
receiver.await? receiver.await?
} }
@ -230,17 +237,19 @@ impl DbThread {
} }
/// Get an existing ensemble. /// Get an existing ensemble.
pub async fn get_ensemble(&self, id: u32) -> Result<Option<Ensemble>> { pub async fn get_ensemble(&self, id: &str) -> Result<Option<Ensemble>> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetEnsemble(id, sender))?; self.action_sender
.send(GetEnsemble(id.to_string(), sender))?;
receiver.await? receiver.await?
} }
/// 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: u32) -> Result<()> { pub async fn delete_ensemble(&self, id: &str) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(DeleteEnsemble(id, sender))?; self.action_sender
.send(DeleteEnsemble(id.to_string(), sender))?;
receiver.await? receiver.await?
} }
@ -260,61 +269,59 @@ impl DbThread {
} }
/// Delete an existing recording. /// Delete an existing recording.
pub async fn delete_recording(&self, id: u32) -> Result<()> { pub async fn delete_recording(&self, id: &str) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(DeleteRecording(id, sender))?; self.action_sender
.send(DeleteRecording(id.to_string(), sender))?;
receiver.await? receiver.await?
} }
/// 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: u32) -> Result<Vec<Recording>> { pub async fn get_recordings_for_person(&self, person_id: &str) -> Result<Vec<Recording>> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender self.action_sender
.send(GetRecordingsForPerson(person_id, sender))?; .send(GetRecordingsForPerson(person_id.to_string(), sender))?;
receiver.await? receiver.await?
} }
/// 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: u32) -> Result<Vec<Recording>> { pub async fn get_recordings_for_ensemble(&self, ensemble_id: &str) -> Result<Vec<Recording>> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender self.action_sender
.send(GetRecordingsForEnsemble(ensemble_id, sender))?; .send(GetRecordingsForEnsemble(ensemble_id.to_string(), sender))?;
receiver.await? receiver.await?
} }
/// 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: u32) -> Result<Vec<Recording>> { pub async fn get_recordings_for_work(&self, work_id: &str) -> Result<Vec<Recording>> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender self.action_sender
.send(GetRecordingsForWork(work_id, sender))?; .send(GetRecordingsForWork(work_id.to_string(), sender))?;
receiver.await? receiver.await?
} }
/// Add or change the tracks associated with a recording. This will fail, if there are still /// Add or change the tracks associated with a recording. This will fail, if there are still
/// other items referencing this recording. /// other items referencing this recording.
pub async fn update_tracks( pub async fn update_tracks(&self, recording_id: &str, tracks: Vec<Track>) -> Result<()> {
&self,
recording_id: u32,
tracks: Vec<Track>,
) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender self.action_sender
.send(UpdateTracks(recording_id, tracks, sender))?; .send(UpdateTracks(recording_id.to_string(), tracks, sender))?;
receiver.await? receiver.await?
} }
/// Delete all tracks associated with a recording. /// Delete all tracks associated with a recording.
pub async fn delete_tracks(&self, recording_id: u32) -> Result<()> { pub async fn delete_tracks(&self, recording_id: &str) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender self.action_sender
.send(DeleteTracks(recording_id, sender))?; .send(DeleteTracks(recording_id.to_string(), sender))?;
receiver.await? receiver.await?
} }
/// Get all tracks associated with a recording. /// Get all tracks associated with a recording.
pub async fn get_tracks(&self, recording_id: u32) -> Result<Vec<Track>> { pub async fn get_tracks(&self, recording_id: &str) -> Result<Vec<Track>> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetTracks(recording_id, sender))?; self.action_sender
.send(GetTracks(recording_id.to_string(), sender))?;
receiver.await? receiver.await?
} }

View file

@ -10,7 +10,7 @@ use std::convert::{TryFrom, TryInto};
struct TrackRow { struct TrackRow {
pub id: i64, pub id: i64,
pub file_name: String, pub file_name: String,
pub recording: i64, pub recording: String,
pub track_index: i32, pub track_index: i32,
pub work_parts: String, pub work_parts: String,
} }
@ -43,14 +43,14 @@ impl TryFrom<TrackRow> for Track {
impl Database { impl Database {
/// Insert or update tracks for the specified recording. /// Insert or update tracks for the specified recording.
pub fn update_tracks(&self, recording_id: u32, tracks: Vec<Track>) -> Result<()> { pub fn update_tracks(&self, recording_id: &str, tracks: Vec<Track>) -> Result<()> {
self.delete_tracks(recording_id)?; self.delete_tracks(recording_id)?;
for (index, track) in tracks.iter().enumerate() { for (index, track) in tracks.iter().enumerate() {
let row = TrackRow { let row = TrackRow {
id: rand::random(), id: rand::random(),
file_name: track.file_name.clone(), file_name: track.file_name.clone(),
recording: recording_id as i64, recording: recording_id.to_string(),
track_index: index.try_into()?, track_index: index.try_into()?,
work_parts: track work_parts: track
.work_parts .work_parts
@ -69,19 +69,19 @@ impl Database {
} }
/// Delete all tracks for the specified recording. /// Delete all tracks for the specified recording.
pub fn delete_tracks(&self, recording_id: u32) -> Result<()> { pub fn delete_tracks(&self, recording_id: &str) -> Result<()> {
diesel::delete(tracks::table.filter(tracks::recording.eq(recording_id as i64))) diesel::delete(tracks::table.filter(tracks::recording.eq(recording_id)))
.execute(&self.connection)?; .execute(&self.connection)?;
Ok(()) Ok(())
} }
/// Get all tracks of the specified recording. /// Get all tracks of the specified recording.
pub fn get_tracks(&self, recording_id: u32) -> Result<Vec<Track>> { pub fn get_tracks(&self, recording_id: &str) -> Result<Vec<Track>> {
let mut tracks = Vec::<Track>::new(); let mut tracks = Vec::<Track>::new();
let rows = tracks::table let rows = tracks::table
.filter(tracks::recording.eq(recording_id as i64)) .filter(tracks::recording.eq(recording_id))
.order_by(tracks::track_index) .order_by(tracks::track_index)
.load::<TrackRow>(&self.connection)?; .load::<TrackRow>(&self.connection)?;

View file

@ -10,16 +10,16 @@ use std::convert::TryInto;
#[derive(Insertable, Queryable, Debug, Clone)] #[derive(Insertable, Queryable, Debug, Clone)]
#[table_name = "works"] #[table_name = "works"]
struct WorkRow { struct WorkRow {
pub id: i64, pub id: String,
pub composer: i64, pub composer: String,
pub title: String, pub title: String,
} }
impl From<Work> for WorkRow { impl From<Work> for WorkRow {
fn from(work: Work) -> Self { fn from(work: Work) -> Self {
WorkRow { WorkRow {
id: work.id as i64, id: work.id,
composer: work.composer.id as i64, composer: work.composer.id,
title: work.title, title: work.title,
} }
} }
@ -30,8 +30,8 @@ impl From<Work> for WorkRow {
#[table_name = "instrumentations"] #[table_name = "instrumentations"]
struct InstrumentationRow { struct InstrumentationRow {
pub id: i64, pub id: i64,
pub work: i64, pub work: String,
pub instrument: i64, pub instrument: String,
} }
/// Table row data for a work part. /// Table row data for a work part.
@ -39,10 +39,10 @@ struct InstrumentationRow {
#[table_name = "work_parts"] #[table_name = "work_parts"]
struct WorkPartRow { struct WorkPartRow {
pub id: i64, pub id: i64,
pub work: i64, pub work: String,
pub part_index: i64, pub part_index: i64,
pub title: String, pub title: String,
pub composer: Option<i64>, pub composer: Option<String>,
} }
/// Table row data for a work section. /// Table row data for a work section.
@ -50,7 +50,7 @@ struct WorkPartRow {
#[table_name = "work_sections"] #[table_name = "work_sections"]
struct WorkSectionRow { struct WorkSectionRow {
pub id: i64, pub id: i64,
pub work: i64, pub work: String,
pub title: String, pub title: String,
pub before_index: i64, pub before_index: i64,
} }
@ -74,7 +74,7 @@ pub struct WorkSection {
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Work { pub struct Work {
pub id: u32, pub id: String,
pub title: String, pub title: String,
pub composer: Person, pub composer: Person,
pub instruments: Vec<Instrument>, pub instruments: Vec<Instrument>,
@ -97,9 +97,9 @@ impl Database {
self.defer_foreign_keys()?; self.defer_foreign_keys()?;
self.connection.transaction::<(), Error, _>(|| { self.connection.transaction::<(), Error, _>(|| {
self.delete_work(work.id)?; let work_id = &work.id;
self.delete_work(work_id)?;
let work_id = work.id as i64;
let row: WorkRow = work.clone().into(); let row: WorkRow = work.clone().into();
diesel::insert_into(works::table) diesel::insert_into(works::table)
.values(row) .values(row)
@ -115,8 +115,8 @@ impl Database {
for instrument in instruments { for instrument in instruments {
let row = InstrumentationRow { let row = InstrumentationRow {
id: rand::random(), id: rand::random(),
work: work_id, work: work_id.to_string(),
instrument: instrument.id as i64, instrument: instrument.id,
}; };
diesel::insert_into(instrumentations::table) diesel::insert_into(instrumentations::table)
@ -127,10 +127,10 @@ impl Database {
for (index, part) in parts.into_iter().enumerate() { for (index, part) in parts.into_iter().enumerate() {
let row = WorkPartRow { let row = WorkPartRow {
id: rand::random(), id: rand::random(),
work: work_id, work: work_id.to_string(),
part_index: index.try_into()?, part_index: index.try_into()?,
title: part.title, title: part.title,
composer: part.composer.map(|person| person.id as i64), composer: part.composer.map(|person| person.id),
}; };
diesel::insert_into(work_parts::table) diesel::insert_into(work_parts::table)
@ -141,7 +141,7 @@ impl Database {
for section in sections { for section in sections {
let row = WorkSectionRow { let row = WorkSectionRow {
id: rand::random(), id: rand::random(),
work: work_id, work: work_id.to_string(),
title: section.title, title: section.title,
before_index: section.before_index.try_into()?, before_index: section.before_index.try_into()?,
}; };
@ -160,9 +160,9 @@ impl Database {
} }
/// Get an existing work. /// Get an existing work.
pub fn get_work(&self, id: u32) -> Result<Option<Work>> { pub fn get_work(&self, id: &str) -> Result<Option<Work>> {
let row = works::table let row = works::table
.filter(works::id.eq(id as i64)) .filter(works::id.eq(id))
.load::<WorkRow>(&self.connection)? .load::<WorkRow>(&self.connection)?
.first() .first()
.cloned(); .cloned();
@ -180,11 +180,11 @@ impl Database {
let mut instruments: Vec<Instrument> = Vec::new(); let mut instruments: Vec<Instrument> = Vec::new();
let instrumentations = instrumentations::table let instrumentations = instrumentations::table
.filter(instrumentations::work.eq(row.id)) .filter(instrumentations::work.eq(&row.id))
.load::<InstrumentationRow>(&self.connection)?; .load::<InstrumentationRow>(&self.connection)?;
for instrumentation in instrumentations { for instrumentation in instrumentations {
let id: u32 = instrumentation.instrument.try_into()?; 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(anyhow!("No instrument with ID: {}", id))?,
@ -194,7 +194,7 @@ impl Database {
let mut parts: Vec<WorkPart> = Vec::new(); let mut parts: Vec<WorkPart> = Vec::new();
let part_rows = work_parts::table let part_rows = work_parts::table
.filter(work_parts::work.eq(row.id)) .filter(work_parts::work.eq(&row.id))
.load::<WorkPartRow>(&self.connection)?; .load::<WorkPartRow>(&self.connection)?;
for part_row in part_rows { for part_row in part_rows {
@ -202,7 +202,7 @@ impl Database {
title: part_row.title, title: part_row.title,
composer: match part_row.composer { composer: match part_row.composer {
Some(composer) => Some( Some(composer) => Some(
self.get_person(composer.try_into()?)? self.get_person(&composer)?
.ok_or(anyhow!("No person with ID: {}", composer))?, .ok_or(anyhow!("No person with ID: {}", composer))?,
), ),
None => None, None => None,
@ -213,7 +213,7 @@ impl Database {
let mut sections: Vec<WorkSection> = Vec::new(); let mut sections: Vec<WorkSection> = Vec::new();
let section_rows = work_sections::table let section_rows = work_sections::table
.filter(work_sections::work.eq(row.id)) .filter(work_sections::work.eq(&row.id))
.load::<WorkSectionRow>(&self.connection)?; .load::<WorkSectionRow>(&self.connection)?;
for section_row in section_rows { for section_row in section_rows {
@ -223,13 +223,13 @@ impl Database {
}); });
} }
let person_id = row.composer.try_into()?; 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(anyhow!("Person doesn't exist: {}", person_id))?;
Ok(Work { Ok(Work {
id: row.id.try_into()?, id: row.id,
composer: person, composer: person,
title: row.title, title: row.title,
instruments, instruments,
@ -240,17 +240,17 @@ 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: u32) -> Result<()> { pub fn delete_work(&self, id: &str) -> Result<()> {
diesel::delete(works::table.filter(works::id.eq(id as i64))).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: u32) -> Result<Vec<Work>> { pub fn get_works(&self, composer_id: &str) -> Result<Vec<Work>> {
let mut works: Vec<Work> = Vec::new(); let mut works: Vec<Work> = Vec::new();
let rows = works::table let rows = works::table
.filter(works::composer.eq(composer_id as i64)) .filter(works::composer.eq(composer_id))
.load::<WorkRow>(&self.connection)?; .load::<WorkRow>(&self.connection)?;
for row in rows { for row in rows {

View file

@ -12,7 +12,7 @@ where
backend: Rc<Backend>, backend: Rc<Backend>,
window: libhandy::Window, window: libhandy::Window,
callback: F, callback: F,
id: u32, id: String,
name_entry: gtk::Entry, name_entry: gtk::Entry,
} }
@ -38,7 +38,7 @@ where
name_entry.set_text(&ensemble.name); name_entry.set_text(&ensemble.name);
ensemble.id ensemble.id
} }
None => rand::random::<u32>().into(), None => generate_id(),
}; };
let result = Rc::new(EnsembleEditor { let result = Rc::new(EnsembleEditor {
@ -55,7 +55,7 @@ where
save_button.connect_clicked(clone!(@strong result => move |_| { save_button.connect_clicked(clone!(@strong result => move |_| {
let ensemble = Ensemble { let ensemble = Ensemble {
id: result.id, id: result.id.clone(),
name: result.name_entry.get_text().to_string(), name: result.name_entry.get_text().to_string(),
}; };

View file

@ -12,7 +12,7 @@ where
backend: Rc<Backend>, backend: Rc<Backend>,
window: libhandy::Window, window: libhandy::Window,
callback: F, callback: F,
id: u32, id: String,
name_entry: gtk::Entry, name_entry: gtk::Entry,
} }
@ -26,8 +26,7 @@ where
instrument: Option<Instrument>, instrument: Option<Instrument>,
callback: F, callback: F,
) -> Rc<Self> { ) -> Rc<Self> {
let builder = let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/instrument_editor.ui");
gtk::Builder::from_resource("/de/johrpan/musicus/ui/instrument_editor.ui");
get_widget!(builder, libhandy::Window, window); get_widget!(builder, libhandy::Window, window);
get_widget!(builder, gtk::Button, cancel_button); get_widget!(builder, gtk::Button, cancel_button);
@ -39,7 +38,7 @@ where
name_entry.set_text(&instrument.name); name_entry.set_text(&instrument.name);
instrument.id instrument.id
} }
None => rand::random::<u32>().into(), None => generate_id(),
}; };
let result = Rc::new(InstrumentEditor { let result = Rc::new(InstrumentEditor {
@ -56,7 +55,7 @@ where
save_button.connect_clicked(clone!(@strong result => move |_| { save_button.connect_clicked(clone!(@strong result => move |_| {
let instrument = Instrument { let instrument = Instrument {
id: result.id, id: result.id.clone(),
name: result.name_entry.get_text().to_string(), name: result.name_entry.get_text().to_string(),
}; };

View file

@ -10,6 +10,7 @@ where
F: Fn(Person) -> () + 'static, F: Fn(Person) -> () + 'static,
{ {
backend: Rc<Backend>, backend: Rc<Backend>,
id: String,
window: libhandy::Window, window: libhandy::Window,
callback: F, callback: F,
id: u32, id: u32,

View file

@ -20,7 +20,7 @@ pub struct RecordingEditor {
work_label: gtk::Label, work_label: gtk::Label,
comment_entry: gtk::Entry, comment_entry: gtk::Entry,
performance_list: Rc<List<Performance>>, performance_list: Rc<List<Performance>>,
id: u32, id: String,
work: RefCell<Option<Work>>, work: RefCell<Option<Work>>,
performances: RefCell<Vec<Performance>>, performances: RefCell<Vec<Performance>>,
selected_cb: RefCell<Option<Box<dyn Fn(Recording) -> ()>>>, selected_cb: RefCell<Option<Box<dyn Fn(Recording) -> ()>>>,
@ -59,7 +59,7 @@ impl RecordingEditor {
comment_entry.set_text(&recording.comment); comment_entry.set_text(&recording.comment);
(recording.id, Some(recording.work), recording.performances) (recording.id, Some(recording.work), recording.performances)
} }
None => (rand::random::<u32>().into(), None, Vec::new()), None => (generate_id(), None, Vec::new()),
}; };
let this = Rc::new(RecordingEditor { let this = Rc::new(RecordingEditor {
@ -88,7 +88,7 @@ impl RecordingEditor {
this.save_button this.save_button
.connect_clicked(clone!(@strong this => move |_| { .connect_clicked(clone!(@strong this => move |_| {
let recording = Recording { let recording = Recording {
id: this.id, id: this.id.clone(),
work: this.work.borrow().clone().expect("Tried to create recording without work!"), work: this.work.borrow().clone().expect("Tried to create recording without work!"),
comment: this.comment_entry.get_text().to_string(), comment: this.comment_entry.get_text().to_string(),
performances: this.performances.borrow().clone(), performances: this.performances.borrow().clone(),

View file

@ -92,7 +92,7 @@ impl RecordingSelectorPersonScreen {
let context = glib::MainContext::default(); let context = glib::MainContext::default();
let clone = this.clone(); let clone = this.clone();
context.spawn_local(async move { context.spawn_local(async move {
let works = clone.backend.db().get_works(person.id).await.unwrap(); let works = clone.backend.db().get_works(&person.id).await.unwrap();
clone.work_list.show_items(works); clone.work_list.show_items(works);
clone.stack.set_visible_child_name("content"); clone.stack.set_visible_child_name("content");

View file

@ -90,7 +90,7 @@ impl RecordingSelectorWorkScreen {
let recordings = clone let recordings = clone
.backend .backend
.db() .db()
.get_recordings_for_work(work.id) .get_recordings_for_work(&work.id)
.await .await
.unwrap(); .unwrap();

View file

@ -81,7 +81,7 @@ impl TracksEditor {
let this = this.clone(); let this = this.clone();
context.spawn_local(async move { context.spawn_local(async move {
this.backend.db().update_tracks( this.backend.db().update_tracks(
this.recording.borrow().as_ref().unwrap().id as u32, &this.recording.borrow().as_ref().unwrap().id,
this.tracks.borrow().clone(), this.tracks.borrow().clone(),
).await.unwrap(); ).await.unwrap();

View file

@ -29,7 +29,7 @@ pub struct WorkEditor {
composer_label: gtk::Label, composer_label: gtk::Label,
instrument_list: Rc<List<Instrument>>, instrument_list: Rc<List<Instrument>>,
part_list: Rc<List<PartOrSection>>, part_list: Rc<List<PartOrSection>>,
id: u32, id: String,
composer: RefCell<Option<Person>>, composer: RefCell<Option<Person>>,
instruments: RefCell<Vec<Instrument>>, instruments: RefCell<Vec<Instrument>>,
structure: RefCell<Vec<PartOrSection>>, structure: RefCell<Vec<PartOrSection>>,
@ -91,7 +91,7 @@ impl WorkEditor {
(work.id, Some(work.composer), work.instruments, structure) (work.id, Some(work.composer), work.instruments, structure)
} }
None => (rand::random::<u32>().into(), None, Vec::new(), Vec::new()), None => (generate_id(), None, Vec::new(), Vec::new()),
}; };
let this = Rc::new(Self { let this = Rc::new(Self {
@ -137,7 +137,7 @@ impl WorkEditor {
} }
let work = Work { let work = Work {
id: this.id, id: this.id.clone(),
title: this.title_entry.get_text().to_string(), title: this.title_entry.get_text().to_string(),
composer: this.composer.borrow().clone().expect("Tried to create work without composer!"), composer: this.composer.borrow().clone().expect("Tried to create work without composer!"),
instruments: this.instruments.borrow().clone(), instruments: this.instruments.borrow().clone(),

View file

@ -80,7 +80,7 @@ impl WorkSelectorPersonScreen {
let context = glib::MainContext::default(); let context = glib::MainContext::default();
let clone = this.clone(); let clone = this.clone();
context.spawn_local(async move { context.spawn_local(async move {
let works = clone.backend.db().get_works(person.id).await.unwrap(); let works = clone.backend.db().get_works(&person.id).await.unwrap();
clone.work_list.show_items(works); clone.work_list.show_items(works);
clone.stack.set_visible_child_name("content"); clone.stack.set_visible_child_name("content");

View file

@ -116,7 +116,7 @@ impl EnsembleScreen {
let context = glib::MainContext::default(); let context = glib::MainContext::default();
let clone = result.clone(); let clone = result.clone();
context.spawn_local(async move { context.spawn_local(async move {
clone.backend.db().delete_ensemble(clone.ensemble.id).await.unwrap(); clone.backend.db().delete_ensemble(&clone.ensemble.id).await.unwrap();
}); });
})); }));
@ -126,7 +126,7 @@ impl EnsembleScreen {
let recordings = clone let recordings = clone
.backend .backend
.db() .db()
.get_recordings_for_ensemble(clone.ensemble.id) .get_recordings_for_ensemble(&clone.ensemble.id)
.await .await
.unwrap(); .unwrap();

View file

@ -145,14 +145,14 @@ impl PersonScreen {
})); }));
edit_action.connect_activate(clone!(@strong result => move |_, _| { edit_action.connect_activate(clone!(@strong result => move |_, _| {
PersonEditor::new(result.backend.clone(), &result.window, Some(result.person.clone()), |_| {}).show(); PersonEditor::new(result.backend.clone(), &result.window, Some(result.person.clone())).show();
})); }));
delete_action.connect_activate(clone!(@strong result => move |_, _| { delete_action.connect_activate(clone!(@strong result => move |_, _| {
let context = glib::MainContext::default(); let context = glib::MainContext::default();
let clone = result.clone(); let clone = result.clone();
context.spawn_local(async move { context.spawn_local(async move {
clone.backend.db().delete_person(clone.person.id).await.unwrap(); clone.backend.db().delete_person(&clone.person.id).await.unwrap();
}); });
})); }));
@ -162,13 +162,13 @@ impl PersonScreen {
let works = clone let works = clone
.backend .backend
.db() .db()
.get_works(clone.person.id as u32) .get_works(&clone.person.id)
.await .await
.unwrap(); .unwrap();
let recordings = clone let recordings = clone
.backend .backend
.db() .db()
.get_recordings_for_person(clone.person.id as u32) .get_recordings_for_person(&clone.person.id)
.await .await
.unwrap(); .unwrap();

View file

@ -118,7 +118,7 @@ impl RecordingScreen {
let context = glib::MainContext::default(); let context = glib::MainContext::default();
let clone = result.clone(); let clone = result.clone();
context.spawn_local(async move { context.spawn_local(async move {
clone.backend.db().delete_recording(clone.recording.id).await.unwrap(); clone.backend.db().delete_recording(&clone.recording.id).await.unwrap();
}); });
})); }));
@ -130,7 +130,7 @@ impl RecordingScreen {
let context = glib::MainContext::default(); let context = glib::MainContext::default();
let clone = result.clone(); let clone = result.clone();
context.spawn_local(async move { context.spawn_local(async move {
clone.backend.db().delete_tracks(clone.recording.id).await.unwrap(); clone.backend.db().delete_tracks(&clone.recording.id).await.unwrap();
}); });
})); }));
@ -140,7 +140,7 @@ impl RecordingScreen {
let tracks = clone let tracks = clone
.backend .backend
.db() .db()
.get_tracks(clone.recording.id) .get_tracks(&clone.recording.id)
.await .await
.unwrap(); .unwrap();

View file

@ -115,7 +115,7 @@ impl WorkScreen {
let context = glib::MainContext::default(); let context = glib::MainContext::default();
let clone = result.clone(); let clone = result.clone();
context.spawn_local(async move { context.spawn_local(async move {
clone.backend.db().delete_work(clone.work.id).await.unwrap(); clone.backend.db().delete_work(&clone.work.id).await.unwrap();
}); });
})); }));
@ -125,7 +125,7 @@ impl WorkScreen {
let recordings = clone let recordings = clone
.backend .backend
.db() .db()
.get_recordings_for_work(clone.work.id as u32) .get_recordings_for_work(&clone.work.id)
.await .await
.unwrap(); .unwrap();