mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
database: Introduce single PersonOrEnsemble enum
This commit is contained in:
parent
15e5849730
commit
cec955eb9f
5 changed files with 118 additions and 143 deletions
|
|
@ -21,6 +21,9 @@ pub enum Error {
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ReceiveError(#[from] tokio::sync::oneshot::error::RecvError),
|
ReceiveError(#[from] tokio::sync::oneshot::error::RecvError),
|
||||||
|
|
||||||
|
#[error("{0}")]
|
||||||
|
Other(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return type for database methods.
|
/// Return type for database methods.
|
||||||
|
|
|
||||||
|
|
@ -5,84 +5,6 @@ use diesel::prelude::*;
|
||||||
use log::info;
|
use log::info;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Database table data for a recording.
|
|
||||||
#[derive(Insertable, Queryable, Debug, Clone)]
|
|
||||||
#[table_name = "recordings"]
|
|
||||||
struct RecordingRow {
|
|
||||||
pub id: String,
|
|
||||||
pub work: String,
|
|
||||||
pub comment: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Recording> for RecordingRow {
|
|
||||||
fn from(recording: Recording) -> Self {
|
|
||||||
RecordingRow {
|
|
||||||
id: recording.id,
|
|
||||||
work: recording.work.id,
|
|
||||||
comment: recording.comment,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Database table data for a performance.
|
|
||||||
#[derive(Insertable, Queryable, Debug, Clone)]
|
|
||||||
#[table_name = "performances"]
|
|
||||||
struct PerformanceRow {
|
|
||||||
pub id: i64,
|
|
||||||
pub recording: String,
|
|
||||||
pub person: Option<String>,
|
|
||||||
pub ensemble: Option<String>,
|
|
||||||
pub role: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// How a person or ensemble was involved in a recording.
|
|
||||||
// TODO: Replace person/ensemble with an enum.
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct Performance {
|
|
||||||
pub person: Option<Person>,
|
|
||||||
pub ensemble: Option<Ensemble>,
|
|
||||||
pub role: Option<Instrument>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Performance {
|
|
||||||
/// Get a string representation of the performance.
|
|
||||||
// TODO: Replace with impl Display.
|
|
||||||
pub fn get_title(&self) -> String {
|
|
||||||
let mut text = if self.is_person() {
|
|
||||||
self.unwrap_person().name_fl()
|
|
||||||
} else {
|
|
||||||
self.unwrap_ensemble().name
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.has_role() {
|
|
||||||
text = text + " (" + &self.unwrap_role().name + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
text
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_person(&self) -> bool {
|
|
||||||
self.person.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unwrap_person(&self) -> Person {
|
|
||||||
self.person.clone().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unwrap_ensemble(&self) -> Ensemble {
|
|
||||||
self.ensemble.clone().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn has_role(&self) -> bool {
|
|
||||||
self.role.clone().is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unwrap_role(&self) -> Instrument {
|
|
||||||
self.role.clone().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A specific recording of a work.
|
/// A specific recording of a work.
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
|
@ -117,6 +39,75 @@ impl Recording {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// How a person or ensemble was involved in a recording.
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Performance {
|
||||||
|
pub performer: PersonOrEnsemble,
|
||||||
|
pub role: Option<Instrument>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Performance {
|
||||||
|
/// Get a string representation of the performance.
|
||||||
|
// TODO: Replace with impl Display.
|
||||||
|
pub fn get_title(&self) -> String {
|
||||||
|
let performer_title = self.performer.get_title();
|
||||||
|
|
||||||
|
if let Some(role) = &self.role {
|
||||||
|
format!("{} ({})", performer_title, role.name)
|
||||||
|
} else {
|
||||||
|
performer_title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Either a person or an ensemble.
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub enum PersonOrEnsemble {
|
||||||
|
Person(Person),
|
||||||
|
Ensemble(Ensemble),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PersonOrEnsemble {
|
||||||
|
/// Get a short textual representation of the item.
|
||||||
|
pub fn get_title(&self) -> String {
|
||||||
|
match self {
|
||||||
|
PersonOrEnsemble::Person(person) => person.name_lf(),
|
||||||
|
PersonOrEnsemble::Ensemble(ensemble) => ensemble.name.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Database table data for a recording.
|
||||||
|
#[derive(Insertable, Queryable, Debug, Clone)]
|
||||||
|
#[table_name = "recordings"]
|
||||||
|
struct RecordingRow {
|
||||||
|
pub id: String,
|
||||||
|
pub work: String,
|
||||||
|
pub comment: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Recording> for RecordingRow {
|
||||||
|
fn from(recording: Recording) -> Self {
|
||||||
|
RecordingRow {
|
||||||
|
id: recording.id,
|
||||||
|
work: recording.work.id,
|
||||||
|
comment: recording.comment,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Database table data for a performance.
|
||||||
|
#[derive(Insertable, Queryable, Debug, Clone)]
|
||||||
|
#[table_name = "performances"]
|
||||||
|
struct PerformanceRow {
|
||||||
|
pub id: i64,
|
||||||
|
pub recording: String,
|
||||||
|
pub person: Option<String>,
|
||||||
|
pub ensemble: Option<String>,
|
||||||
|
pub role: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
/// Update an existing recording or insert a new one.
|
/// Update an existing recording or insert a new one.
|
||||||
// TODO: Think about whether to also insert the other items.
|
// TODO: Think about whether to also insert the other items.
|
||||||
|
|
@ -134,15 +125,16 @@ impl Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
for performance in &recording.performances {
|
for performance in &recording.performances {
|
||||||
if let Some(person) = &performance.person {
|
match &performance.performer {
|
||||||
if self.get_person(&person.id)?.is_none() {
|
PersonOrEnsemble::Person(person) => {
|
||||||
self.update_person(person.clone())?;
|
if self.get_person(&person.id)?.is_none() {
|
||||||
|
self.update_person(person.clone())?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
PersonOrEnsemble::Ensemble(ensemble) => {
|
||||||
|
if self.get_ensemble(&ensemble.id)?.is_none() {
|
||||||
if let Some(ensemble) = &performance.ensemble {
|
self.update_ensemble(ensemble.clone())?;
|
||||||
if self.get_ensemble(&ensemble.id)?.is_none() {
|
}
|
||||||
self.update_ensemble(ensemble.clone())?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,11 +153,16 @@ impl Database {
|
||||||
.execute(&self.connection)?;
|
.execute(&self.connection)?;
|
||||||
|
|
||||||
for performance in recording.performances {
|
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 {
|
let row = PerformanceRow {
|
||||||
id: rand::random(),
|
id: rand::random(),
|
||||||
recording: recording_id.to_string(),
|
recording: recording_id.to_string(),
|
||||||
person: performance.person.map(|person| person.id),
|
person,
|
||||||
ensemble: performance.ensemble.map(|ensemble| ensemble.id),
|
ensemble,
|
||||||
role: performance.role.map(|role| role.id),
|
role: performance.role.map(|role| role.id),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -217,19 +214,18 @@ impl Database {
|
||||||
|
|
||||||
for row in performance_rows {
|
for row in performance_rows {
|
||||||
performance_descriptions.push(Performance {
|
performance_descriptions.push(Performance {
|
||||||
person: match row.person {
|
performer: if let Some(id) = row.person {
|
||||||
Some(id) => Some(
|
PersonOrEnsemble::Person(
|
||||||
self.get_person(&id)?
|
self.get_person(&id)?
|
||||||
.ok_or(Error::MissingItem("person", id))?,
|
.ok_or(Error::MissingItem("person", id))?,
|
||||||
),
|
)
|
||||||
None => None,
|
} else if let Some(id) = row.ensemble {
|
||||||
},
|
PersonOrEnsemble::Ensemble(
|
||||||
ensemble: match row.ensemble {
|
|
||||||
Some(id) => Some(
|
|
||||||
self.get_ensemble(&id)?
|
self.get_ensemble(&id)?
|
||||||
.ok_or(Error::MissingItem("ensemble", id))?,
|
.ok_or(Error::MissingItem("ensemble", id))?,
|
||||||
),
|
)
|
||||||
None => None,
|
} else {
|
||||||
|
return Err(Error::Other("Performance without performer"));
|
||||||
},
|
},
|
||||||
role: match row.role {
|
role: match row.role {
|
||||||
Some(id) => Some(
|
Some(id) => Some(
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ use adw::prelude::*;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use musicus_backend::db::{Ensemble, Instrument, Performance, Person};
|
use log::error;
|
||||||
|
use musicus_backend::db::{Ensemble, Instrument, Performance, Person, PersonOrEnsemble};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
|
@ -85,8 +86,14 @@ impl Screen<Option<Performance>, Performance> for PerformanceEditor {
|
||||||
|
|
||||||
this.editor.set_save_cb(clone!(@weak this => move || {
|
this.editor.set_save_cb(clone!(@weak this => move || {
|
||||||
let performance = Performance {
|
let performance = Performance {
|
||||||
person: this.person.borrow().clone(),
|
performer: if let Some(person) = this.person.borrow().clone() {
|
||||||
ensemble: this.ensemble.borrow().clone(),
|
PersonOrEnsemble::Person(person)
|
||||||
|
} else if let Some(ensemble) = this.ensemble.borrow().clone() {
|
||||||
|
PersonOrEnsemble::Ensemble(ensemble)
|
||||||
|
} else {
|
||||||
|
error!("Tried to save performance without performer");
|
||||||
|
return;
|
||||||
|
},
|
||||||
role: this.role.borrow().clone(),
|
role: this.role.borrow().clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -133,13 +140,16 @@ impl Screen<Option<Performance>, Performance> for PerformanceEditor {
|
||||||
// Initialize
|
// Initialize
|
||||||
|
|
||||||
if let Some(performance) = performance {
|
if let Some(performance) = performance {
|
||||||
if let Some(person) = performance.person {
|
match performance.performer {
|
||||||
this.show_person(Some(&person));
|
PersonOrEnsemble::Person(person) => {
|
||||||
this.person.replace(Some(person));
|
this.show_person(Some(&person));
|
||||||
} else if let Some(ensemble) = performance.ensemble {
|
this.person.replace(Some(person));
|
||||||
this.show_ensemble(Some(&ensemble));
|
}
|
||||||
this.ensemble.replace(Some(ensemble));
|
PersonOrEnsemble::Ensemble(ensemble) => {
|
||||||
}
|
this.show_ensemble(Some(&ensemble));
|
||||||
|
this.ensemble.replace(Some(ensemble));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(role) = performance.role {
|
if let Some(role) = performance.role {
|
||||||
this.show_role(Some(&role));
|
this.show_role(Some(&role));
|
||||||
|
|
|
||||||
|
|
@ -9,27 +9,10 @@ use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk_macros::get_widget;
|
use gtk_macros::get_widget;
|
||||||
use musicus_backend::db::{Ensemble, Person};
|
use musicus_backend::db::PersonOrEnsemble;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// Either a person or an ensemble to be shown in the list.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum PersonOrEnsemble {
|
|
||||||
Person(Person),
|
|
||||||
Ensemble(Ensemble),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PersonOrEnsemble {
|
|
||||||
/// Get a short textual representation of the item.
|
|
||||||
pub fn get_title(&self) -> String {
|
|
||||||
match self {
|
|
||||||
PersonOrEnsemble::Person(person) => person.name_lf(),
|
|
||||||
PersonOrEnsemble::Ensemble(ensemble) => ensemble.name.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The main screen of the app, once it's set up and finished loading. The screen assumes that the
|
/// The main screen of the app, once it's set up and finished loading. The screen assumes that the
|
||||||
/// music library and the player are available and initialized.
|
/// music library and the player are available and initialized.
|
||||||
pub struct MainScreen {
|
pub struct MainScreen {
|
||||||
|
|
|
||||||
|
|
@ -5,26 +5,9 @@ use adw::prelude::*;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use musicus_backend::db::{Ensemble, Medium, Person};
|
use musicus_backend::db::{Medium, PersonOrEnsemble};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// Either a person or an ensemble to be shown in the list.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum PersonOrEnsemble {
|
|
||||||
Person(Person),
|
|
||||||
Ensemble(Ensemble),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PersonOrEnsemble {
|
|
||||||
/// Get a short textual representation of the item.
|
|
||||||
pub fn get_title(&self) -> String {
|
|
||||||
match self {
|
|
||||||
PersonOrEnsemble::Person(person) => person.name_lf(),
|
|
||||||
PersonOrEnsemble::Ensemble(ensemble) => ensemble.name.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A screen for selecting a medium.
|
/// A screen for selecting a medium.
|
||||||
pub struct MediumSelector {
|
pub struct MediumSelector {
|
||||||
handle: NavigationHandle<Medium>,
|
handle: NavigationHandle<Medium>,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue