Split into multiple crates

This commit is contained in:
Elias Projahn 2021-02-04 21:47:22 +01:00
parent d7fb996183
commit 5d06ec9faf
88 changed files with 501 additions and 528 deletions

View file

@ -1,50 +1,2 @@
[package] [workspace]
name = "musicus" members = ["crates/*"]
version = "0.1.0"
edition = "2018"
[dependencies]
anyhow = "1.0.33"
async-trait = "0.1.42"
diesel = { version = "1.4.5", features = ["sqlite"] }
diesel_migrations = "1.4.0"
discid = "0.4.4"
fragile = "1.0.0"
futures = "0.3.6"
futures-channel = "0.3.5"
gettext-rs = "0.5.0"
gtk-macros = "0.2.0"
gstreamer = "0.16.4"
gstreamer-player = "0.16.3"
isahc = "0.9.12"
once_cell = "1.5.2"
rand = "0.7.3"
secret-service = "2.0.1"
serde = { version = "1.0.117", features = ["derive"] }
serde_json = "1.0.59"
thiserror = "1.0.23"
uuid = { version = "0.8", features = ["v4"] }
[dependencies.gdk]
git = "https://github.com/gtk-rs/gtk4-rs/"
package = "gdk4"
[dependencies.gio]
git = "https://github.com/gtk-rs/gtk-rs/"
features = ["v2_64"]
[dependencies.glib]
git = "https://github.com/gtk-rs/gtk-rs/"
features = ["v2_64"]
[dependencies.gtk]
git = "https://github.com/gtk-rs/gtk4-rs"
package = "gtk4"
[dependencies.libadwaita]
git = "https://gitlab.gnome.org/bilelmoussaoui/libadwaita-rs"
package = "libadwaita"
[dependencies.pango]
git = "https://github.com/gtk-rs/gtk-rs/"
features = ["v1_44"]

41
crates/musicus/Cargo.toml Normal file
View file

@ -0,0 +1,41 @@
[package]
name = "musicus"
version = "0.1.0"
edition = "2018"
[dependencies]
anyhow = "1.0.33"
async-trait = "0.1.42"
discid = "0.4.4"
futures = "0.3.6"
futures-channel = "0.3.5"
gettext-rs = "0.5.0"
gstreamer = "0.16.4"
gtk-macros = "0.2.0"
musicus_backend = { version = "0.1.0", path = "../musicus_backend" }
once_cell = "1.5.2"
rand = "0.7.3"
[dependencies.gdk]
git = "https://github.com/gtk-rs/gtk4-rs/"
package = "gdk4"
[dependencies.gio]
git = "https://github.com/gtk-rs/gtk-rs/"
features = ["v2_64"]
[dependencies.glib]
git = "https://github.com/gtk-rs/gtk-rs/"
features = ["v2_64"]
[dependencies.gtk]
git = "https://github.com/gtk-rs/gtk4-rs"
package = "gtk4"
[dependencies.libadwaita]
git = "https://gitlab.gnome.org/bilelmoussaoui/libadwaita-rs"
package = "libadwaita"
[dependencies.pango]
git = "https://github.com/gtk-rs/gtk-rs/"
features = ["v1_44"]

View file

@ -0,0 +1,2 @@
pub static VERSION: &str = "0.1.0";
pub static LOCALEDIR: &str = "/app/share/locale";

View file

@ -1,12 +1,11 @@
use crate::backend::generate_id;
use crate::backend::{Backend, Ensemble};
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::{Editor, EntryRow, Section, UploadSection, Widget}; use crate::widgets::{Editor, EntryRow, Section, UploadSection, Widget};
use anyhow::Result; use anyhow::Result;
use gettextrs::gettext; use gettextrs::gettext;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use std::cell::RefCell; use musicus_backend::generate_id;
use musicus_backend::Ensemble;
use std::rc::Rc; use std::rc::Rc;
/// A dialog for creating or editing a ensemble. /// A dialog for creating or editing a ensemble.
@ -92,7 +91,7 @@ impl EnsembleEditor {
}; };
if self.upload.get_active() { if self.upload.get_active() {
self.handle.backend.post_ensemble(&ensemble).await?; self.handle.backend.cl().post_ensemble(&ensemble).await?;
} }
self.handle.backend.db().update_ensemble(ensemble.clone()).await?; self.handle.backend.db().update_ensemble(ensemble.clone()).await?;

View file

@ -1,12 +1,11 @@
use crate::backend::generate_id;
use crate::backend::{Backend, Instrument};
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::{Editor, EntryRow, Section, UploadSection, Widget}; use crate::widgets::{Editor, EntryRow, Section, UploadSection, Widget};
use anyhow::Result; use anyhow::Result;
use gettextrs::gettext; use gettextrs::gettext;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use std::cell::RefCell; use musicus_backend::generate_id;
use musicus_backend::Instrument;
use std::rc::Rc; use std::rc::Rc;
/// A dialog for creating or editing a instrument. /// A dialog for creating or editing a instrument.
@ -92,7 +91,7 @@ impl InstrumentEditor {
}; };
if self.upload.get_active() { if self.upload.get_active() {
self.handle.backend.post_instrument(&instrument).await?; self.handle.backend.cl().post_instrument(&instrument).await?;
} }
self.handle.backend.db().update_instrument(instrument.clone()).await?; self.handle.backend.db().update_instrument(instrument.clone()).await?;

View file

@ -1,4 +1,3 @@
use crate::backend::{Backend, Performance, Person, Ensemble, Instrument};
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::selectors::{EnsembleSelector, InstrumentSelector, PersonSelector}; use crate::selectors::{EnsembleSelector, InstrumentSelector, PersonSelector};
use crate::widgets::{Editor, Section, ButtonRow, Widget}; use crate::widgets::{Editor, Section, ButtonRow, Widget};
@ -6,6 +5,7 @@ use gettextrs::gettext;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use musicus_backend::{Performance, Person, Ensemble, Instrument};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;

View file

@ -1,12 +1,11 @@
use crate::backend::generate_id;
use crate::backend::{Backend, Person};
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::{Editor, EntryRow, Section, UploadSection, Widget}; use crate::widgets::{Editor, EntryRow, Section, UploadSection, Widget};
use anyhow::Result; use anyhow::Result;
use gettextrs::gettext; use gettextrs::gettext;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use std::cell::RefCell; use musicus_backend::generate_id;
use musicus_backend::Person;
use std::rc::Rc; use std::rc::Rc;
/// A dialog for creating or editing a person. /// A dialog for creating or editing a person.
@ -101,7 +100,7 @@ impl PersonEditor {
}; };
if self.upload.get_active() { if self.upload.get_active() {
self.handle.backend.post_person(&person).await?; self.handle.backend.cl().post_person(&person).await?;
} }
self.handle.backend.db().update_person(person.clone()).await?; self.handle.backend.db().update_person(person.clone()).await?;

View file

@ -1,7 +1,5 @@
use super::performance::PerformanceEditor; use super::performance::PerformanceEditor;
use crate::backend::generate_id; use crate::selectors::WorkSelector;
use crate::backend::{Backend, Performance, Person, Recording, Work};
use crate::selectors::{PersonSelector, WorkSelector};
use crate::widgets::{List, Widget}; use crate::widgets::{List, Widget};
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use anyhow::Result; use anyhow::Result;
@ -10,6 +8,8 @@ use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use gtk_macros::get_widget; use gtk_macros::get_widget;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use musicus_backend::generate_id;
use musicus_backend::{Performance, Recording, Work};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
@ -195,7 +195,7 @@ impl RecordingEditor {
let upload = self.upload_switch.get_active(); let upload = self.upload_switch.get_active();
if upload { if upload {
self.handle.backend.post_recording(&recording).await?; self.handle.backend.cl().post_recording(&recording).await?;
} }
self.handle.backend self.handle.backend

View file

@ -1,6 +1,4 @@
use super::work_part::WorkPartEditor; use super::work_part::WorkPartEditor;
use crate::backend::generate_id;
use crate::backend::{Instrument, Person, Work, WorkPart, WorkSection};
use super::work_section::WorkSectionEditor; use super::work_section::WorkSectionEditor;
use crate::selectors::{InstrumentSelector, PersonSelector}; use crate::selectors::{InstrumentSelector, PersonSelector};
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
@ -11,6 +9,8 @@ use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use gtk_macros::get_widget; use gtk_macros::get_widget;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use musicus_backend::generate_id;
use musicus_backend::{Instrument, Person, Work, WorkPart, WorkSection};
use std::cell::RefCell; use std::cell::RefCell;
use std::convert::TryInto; use std::convert::TryInto;
use std::rc::Rc; use std::rc::Rc;
@ -336,7 +336,7 @@ impl WorkEditor {
let upload = self.upload_switch.get_active(); let upload = self.upload_switch.get_active();
if upload { if upload {
self.handle.backend.post_work(&work).await?; self.handle.backend.cl().post_work(&work).await?;
} }
self.handle.backend self.handle.backend

View file

@ -1,4 +1,3 @@
use crate::backend::{Person, WorkPart};
use crate::selectors::PersonSelector; use crate::selectors::PersonSelector;
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget; use crate::widgets::Widget;
@ -7,6 +6,7 @@ use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use gtk_macros::get_widget; use gtk_macros::get_widget;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use musicus_backend::{Person, WorkPart};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;

View file

@ -1,10 +1,9 @@
use crate::backend::WorkSection;
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget; use crate::widgets::Widget;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use gtk_macros::get_widget; use gtk_macros::get_widget;
use std::cell::RefCell; use musicus_backend::WorkSection;
use std::rc::Rc; use std::rc::Rc;
/// A dialog for creating or editing a work section. /// A dialog for creating or editing a work section.

View file

@ -146,8 +146,8 @@ impl Source for DiscSource {
let (discid, tracks) = receiver.await??; let (discid, tracks) = receiver.await??;
self.discid.set(discid); self.discid.set(discid).unwrap();
self.tracks.set(tracks); self.tracks.set(tracks).unwrap();
Ok(()) Ok(())
} }

View file

@ -68,7 +68,7 @@ impl Source for FolderSource {
}); });
let tracks = receiver.await??; let tracks = receiver.await??;
self.tracks.set(tracks); self.tracks.set(tracks).unwrap();
Ok(()) Ok(())
} }

View file

@ -1,7 +1,5 @@
use super::source::Source; use super::source::Source;
use super::track_set_editor::{TrackSetData, TrackSetEditor}; use super::track_set_editor::{TrackSetData, TrackSetEditor};
use crate::backend::generate_id;
use crate::backend::{Backend, Medium, Track, TrackSet};
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::{List, Widget}; use crate::widgets::{List, Widget};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
@ -10,6 +8,8 @@ use glib::prelude::*;
use gtk::prelude::*; use gtk::prelude::*;
use gtk_macros::get_widget; use gtk_macros::get_widget;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use musicus_backend::generate_id;
use musicus_backend::{Medium, Track, TrackSet};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
@ -192,7 +192,7 @@ impl MediumEditor {
let upload = self.publish_switch.get_active(); let upload = self.publish_switch.get_active();
if upload { if upload {
self.handle.backend.post_medium(&medium).await?; self.handle.backend.cl().post_medium(&medium).await?;
} }
self.handle.backend self.handle.backend

View file

@ -2,14 +2,12 @@ use super::medium_editor::MediumEditor;
use super::disc_source::DiscSource; use super::disc_source::DiscSource;
use super::folder_source::FolderSource; use super::folder_source::FolderSource;
use super::source::Source; use super::source::Source;
use crate::backend::Backend;
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget; use crate::widgets::Widget;
use gettextrs::gettext; 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 std::cell::RefCell;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;

View file

@ -1,10 +1,10 @@
use crate::backend::Recording;
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget; use crate::widgets::Widget;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use gtk_macros::get_widget; use gtk_macros::get_widget;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use musicus_backend::Recording;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;

View file

@ -1,15 +1,15 @@
use super::source::Source; use super::source::Source;
use super::track_editor::TrackEditor; use super::track_editor::TrackEditor;
use super::track_selector::TrackSelector; use super::track_selector::TrackSelector;
use crate::backend::{Backend, Recording};
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::selectors::{PersonSelector, RecordingSelector}; use crate::selectors::RecordingSelector;
use crate::widgets::{List, Widget}; use crate::widgets::{List, Widget};
use gettextrs::gettext; 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 libadwaita::prelude::*; use libadwaita::prelude::*;
use musicus_backend::Recording;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;

View file

@ -1,11 +1,3 @@
// Required for database/schema.rs
#[macro_use]
extern crate diesel;
// Required for embed_migrations macro in database/database.rs
#[macro_use]
extern crate diesel_migrations;
use gio::prelude::*; use gio::prelude::*;
use glib::clone; use glib::clone;
use std::cell::RefCell; use std::cell::RefCell;
@ -14,7 +6,6 @@ use std::rc::Rc;
#[macro_use] #[macro_use]
mod macros; mod macros;
mod backend;
mod config; mod config;
mod editors; mod editors;
mod import; mod import;

View file

@ -0,0 +1,59 @@
prefix = get_option('prefix')
localedir = join_paths(prefix, get_option('localedir'))
global_conf = configuration_data()
global_conf.set_quoted('LOCALEDIR', localedir)
global_conf.set_quoted('VERSION', meson.project_version())
config_rs = configure_file(
input: 'config.rs.in',
output: 'config.rs',
configuration: global_conf
)
run_command(
'cp',
config_rs,
meson.current_source_dir(),
check: true
)
resource_conf = configuration_data()
resource_conf.set_quoted('RESOURCEFILE', resources.full_path())
resource_rs = configure_file(
input: 'resources.rs.in',
output: 'resources.rs',
configuration: resource_conf
)
run_command(
'cp',
resource_rs,
meson.current_source_dir(),
check: true
)
sources = files(
'config.rs',
'resources.rs',
)
cargo_script = find_program(join_paths(meson.source_root(), 'build-aux/cargo.sh'))
cargo_release = custom_target(
'cargo-build',
build_by_default: true,
input: sources,
build_always_stale: true,
depends: resources,
output: meson.project_name(),
console: true,
install: true,
install_dir: get_option('bindir'),
command: [
cargo_script,
meson.build_root(),
meson.source_root(),
'@OUTPUT@',
get_option('buildtype'),
meson.project_name(),
]
)

View file

@ -1,9 +1,9 @@
use crate::backend::Backend;
use crate::widgets::Widget; use crate::widgets::Widget;
use futures_channel::oneshot; use futures_channel::oneshot;
use futures_channel::oneshot::{Receiver, Sender}; use futures_channel::oneshot::{Receiver, Sender};
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use musicus_backend::Backend;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};

View file

@ -1,7 +1,7 @@
use crate::backend::Backend; use super::Navigator;
use super::{Navigator, Screen};
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use musicus_backend::Backend;
use std::rc::Rc; use std::rc::Rc;
/// A window hosting a navigator. /// A window hosting a navigator.
@ -35,9 +35,4 @@ impl NavigatorWindow {
self.window.set_modal(true); self.window.set_modal(true);
self.window.set_transient_for(Some(window)); self.window.set_transient_for(Some(window));
} }
/// Show the navigator window.
pub fn show(&self) {
self.window.show();
}
} }

View file

@ -1,12 +1,11 @@
use super::register::RegisterDialog; use super::register::RegisterDialog;
use crate::push; use crate::push;
use crate::backend::{Backend, LoginData};
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget; use crate::widgets::Widget;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use gtk_macros::get_widget; use gtk_macros::get_widget;
use std::cell::RefCell; use musicus_backend::LoginData;
use std::rc::Rc; use std::rc::Rc;
/// A dialog for entering login credentials. /// A dialog for entering login credentials.
@ -55,7 +54,7 @@ impl Screen<(), LoginData> for LoginDialog {
spawn!(@clone this, async move { spawn!(@clone this, async move {
this.handle.backend.set_login_data(data.clone()).await.unwrap(); this.handle.backend.set_login_data(data.clone()).await.unwrap();
if this.handle.backend.login().await.unwrap() { if this.handle.backend.cl().login().await.unwrap() {
this.handle.pop(Some(data)); this.handle.pop(Some(data));
} else { } else {
this.widget.set_visible_child_name("content"); this.widget.set_visible_child_name("content");

View file

@ -1,9 +1,9 @@
use crate::backend::Backend;
use crate::navigator::NavigatorWindow; use crate::navigator::NavigatorWindow;
use gettextrs::gettext; 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::Backend;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use std::rc::Rc; use std::rc::Rc;

View file

@ -1,10 +1,10 @@
use crate::backend::{Backend, LoginData, UserRegistration};
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget; use crate::widgets::Widget;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use gtk_macros::get_widget; use gtk_macros::get_widget;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use musicus_backend::{LoginData, UserRegistration};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
@ -85,7 +85,7 @@ impl Screen<(), LoginData> for RegisterDialog {
}; };
// TODO: Handle errors. // TODO: Handle errors.
if this.handle.backend.register(registration).await.unwrap() { if this.handle.backend.cl().register(registration).await.unwrap() {
let data = LoginData { let data = LoginData {
username, username,
password, password,
@ -102,7 +102,7 @@ impl Screen<(), LoginData> for RegisterDialog {
// Initialize // Initialize
spawn!(@clone this, async move { spawn!(@clone this, async move {
let captcha = this.handle.backend.get_captcha().await.unwrap(); let captcha = this.handle.backend.cl().get_captcha().await.unwrap();
this.captcha_row.set_title(Some(&captcha.question)); this.captcha_row.set_title(Some(&captcha.question));
this.captcha_id.replace(Some(captcha.id)); this.captcha_id.replace(Some(captcha.id));
this.widget.set_visible_child_name("content"); this.widget.set_visible_child_name("content");

View file

@ -1,7 +1,7 @@
use crate::backend::Backend;
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::Backend;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;

View file

@ -0,0 +1,9 @@
use anyhow::Result;
pub fn init() -> Result<()> {
let bytes = glib::Bytes::from(include_bytes!("/home/johrpan/.var/app/org.gnome.Builder/cache/gnome-builder/projects/musicus/builds/de.johrpan.musicus.json-flatpak-org.gnome.Platform-x86_64-master-error-handling/res/musicus.gresource").as_ref());
let resource = gio::Resource::from_data(&bytes)?;
gio::resources_register(&resource);
Ok(())
}

View file

@ -1,5 +1,4 @@
use super::RecordingScreen; use super::RecordingScreen;
use crate::backend::{Backend, Ensemble, Recording};
use crate::editors::EnsembleEditor; use crate::editors::EnsembleEditor;
use crate::navigator::{NavigatorWindow, NavigationHandle, Screen}; use crate::navigator::{NavigatorWindow, NavigationHandle, Screen};
use crate::widgets; use crate::widgets;
@ -8,6 +7,7 @@ use gettextrs::gettext;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use musicus_backend::{Ensemble, Recording};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;

View file

@ -1,5 +1,4 @@
use super::{WorkScreen, RecordingScreen}; use super::{WorkScreen, RecordingScreen};
use crate::backend::{Backend, Person, Recording, Work};
use crate::editors::PersonEditor; use crate::editors::PersonEditor;
use crate::navigator::{NavigatorWindow, NavigationHandle, Screen}; use crate::navigator::{NavigatorWindow, NavigationHandle, Screen};
use crate::widgets; use crate::widgets;
@ -8,6 +7,7 @@ use gettextrs::gettext;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use musicus_backend::{Person, Recording, Work};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;

View file

@ -1,10 +1,10 @@
use crate::backend::{Player, PlaylistItem};
use crate::widgets::*; use crate::widgets::*;
use gettextrs::gettext; 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 libadwaita::prelude::*; use libadwaita::prelude::*;
use musicus_backend::{Player, PlaylistItem};
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::rc::Rc; use std::rc::Rc;

View file

@ -1,4 +1,3 @@
use crate::backend::{Backend, PlaylistItem, Recording, TrackSet};
use crate::editors::RecordingEditor; use crate::editors::RecordingEditor;
use crate::navigator::{NavigatorWindow, NavigationHandle, Screen}; use crate::navigator::{NavigatorWindow, NavigationHandle, Screen};
use crate::widgets; use crate::widgets;
@ -7,6 +6,7 @@ use gettextrs::gettext;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use musicus_backend::{PlaylistItem, Recording, TrackSet};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;

View file

@ -1,5 +1,4 @@
use super::RecordingScreen; use super::RecordingScreen;
use crate::backend::{Backend, Work, Recording};
use crate::editors::WorkEditor; use crate::editors::WorkEditor;
use crate::navigator::{NavigatorWindow, NavigationHandle, Screen}; use crate::navigator::{NavigatorWindow, NavigationHandle, Screen};
use crate::widgets; use crate::widgets;
@ -8,6 +7,7 @@ use gettextrs::gettext;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use musicus_backend::{Work, Recording};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;

View file

@ -1,5 +1,4 @@
use super::selector::Selector; use super::selector::Selector;
use crate::backend::{Backend, Ensemble};
use crate::editors::EnsembleEditor; use crate::editors::EnsembleEditor;
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget; use crate::widgets::Widget;
@ -7,7 +6,7 @@ use gettextrs::gettext;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use std::cell::RefCell; use musicus_backend::Ensemble;
use std::rc::Rc; use std::rc::Rc;
/// A screen for selecting a ensemble. /// A screen for selecting a ensemble.
@ -45,7 +44,7 @@ impl Screen<(), Ensemble> for EnsembleSelector {
this.selector.set_load_online(clone!(@weak this => move || { this.selector.set_load_online(clone!(@weak this => move || {
let clone = this.clone(); let clone = this.clone();
async move { clone.handle.backend.get_ensembles().await } async move { Ok(clone.handle.backend.cl().get_ensembles().await?) }
})); }));
this.selector.set_load_local(clone!(@weak this => move || { this.selector.set_load_local(clone!(@weak this => move || {

View file

@ -1,5 +1,4 @@
use super::selector::Selector; use super::selector::Selector;
use crate::backend::{Backend, Instrument};
use crate::editors::InstrumentEditor; use crate::editors::InstrumentEditor;
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget; use crate::widgets::Widget;
@ -7,7 +6,7 @@ use gettextrs::gettext;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use std::cell::RefCell; use musicus_backend::Instrument;
use std::rc::Rc; use std::rc::Rc;
/// A screen for selecting a instrument. /// A screen for selecting a instrument.
@ -45,7 +44,7 @@ impl Screen<(), Instrument> for InstrumentSelector {
this.selector.set_load_online(clone!(@weak this => move || { this.selector.set_load_online(clone!(@weak this => move || {
let clone = this.clone(); let clone = this.clone();
async move { clone.handle.backend.get_instruments().await } async move { Ok(clone.handle.backend.cl().get_instruments().await?) }
})); }));
this.selector.set_load_local(clone!(@weak this => move || { this.selector.set_load_local(clone!(@weak this => move || {

View file

@ -1,5 +1,4 @@
use super::selector::Selector; use super::selector::Selector;
use crate::backend::{Backend, Person};
use crate::editors::PersonEditor; use crate::editors::PersonEditor;
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget; use crate::widgets::Widget;
@ -7,7 +6,7 @@ use gettextrs::gettext;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use std::cell::RefCell; use musicus_backend::Person;
use std::rc::Rc; use std::rc::Rc;
/// A screen for selecting a person. /// A screen for selecting a person.
@ -45,7 +44,7 @@ impl Screen<(), Person> for PersonSelector {
this.selector.set_load_online(clone!(@weak this => move || { this.selector.set_load_online(clone!(@weak this => move || {
let clone = this.clone(); let clone = this.clone();
async move { clone.handle.backend.get_persons().await } async move { Ok(clone.handle.backend.cl().get_persons().await?) }
})); }));
this.selector.set_load_local(clone!(@weak this => move || { this.selector.set_load_local(clone!(@weak this => move || {

View file

@ -1,5 +1,4 @@
use super::selector::Selector; use super::selector::Selector;
use crate::backend::{Backend, Person, Work, Recording};
use crate::editors::{PersonEditor, WorkEditor, RecordingEditor}; use crate::editors::{PersonEditor, WorkEditor, RecordingEditor};
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget; use crate::widgets::Widget;
@ -7,7 +6,7 @@ use gettextrs::gettext;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use std::cell::RefCell; use musicus_backend::{Person, Work, Recording};
use std::rc::Rc; use std::rc::Rc;
/// A screen for selecting a recording. /// A screen for selecting a recording.
@ -56,7 +55,7 @@ impl Screen<(), Recording> for RecordingSelector {
})); }));
this.selector.set_load_online(clone!(@weak this => move || { this.selector.set_load_online(clone!(@weak this => move || {
async move { this.handle.backend.get_persons().await } async move { Ok(this.handle.backend.cl().get_persons().await?) }
})); }));
this.selector.set_load_local(clone!(@weak this => move || { this.selector.set_load_local(clone!(@weak this => move || {
@ -134,7 +133,7 @@ impl Screen<Person, Work> for RecordingSelectorWorkScreen {
})); }));
this.selector.set_load_online(clone!(@weak this => move || { this.selector.set_load_online(clone!(@weak this => move || {
async move { this.handle.backend.get_works(&this.person.id).await } async move { Ok(this.handle.backend.cl().get_works(&this.person.id).await?) }
})); }));
this.selector.set_load_local(clone!(@weak this => move || { this.selector.set_load_local(clone!(@weak this => move || {
@ -199,7 +198,7 @@ impl Screen<Work, Recording> for RecordingSelectorRecordingScreen {
})); }));
this.selector.set_load_online(clone!(@weak this => move || { this.selector.set_load_online(clone!(@weak this => move || {
async move { this.handle.backend.get_recordings_for_work(&this.work.id).await } async move { Ok(this.handle.backend.cl().get_recordings_for_work(&this.work.id).await?) }
})); }));
this.selector.set_load_local(clone!(@weak this => move || { this.selector.set_load_local(clone!(@weak this => move || {

View file

@ -1,8 +1,8 @@
use crate::backend::Result;
use crate::widgets::List; use crate::widgets::List;
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::Result;
use std::cell::RefCell; use std::cell::RefCell;
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;

View file

@ -1,5 +1,4 @@
use super::selector::Selector; use super::selector::Selector;
use crate::backend::{Backend, Person, Work};
use crate::editors::{PersonEditor, WorkEditor}; use crate::editors::{PersonEditor, WorkEditor};
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget; use crate::widgets::Widget;
@ -7,7 +6,7 @@ use gettextrs::gettext;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use std::cell::RefCell; use musicus_backend::{Person, Work};
use std::rc::Rc; use std::rc::Rc;
/// A screen for selecting a work. /// A screen for selecting a work.
@ -50,7 +49,7 @@ impl Screen<(), Work> for WorkSelector {
})); }));
this.selector.set_load_online(clone!(@weak this => move || { this.selector.set_load_online(clone!(@weak this => move || {
async move { this.handle.backend.get_persons().await } async move { Ok(this.handle.backend.cl().get_persons().await?) }
})); }));
this.selector.set_load_local(clone!(@weak this => move || { this.selector.set_load_local(clone!(@weak this => move || {
@ -124,7 +123,7 @@ impl Screen<Person, Work> for WorkSelectorWorkScreen {
})); }));
this.selector.set_load_online(clone!(@weak this => move || { this.selector.set_load_online(clone!(@weak this => move || {
async move { this.handle.backend.get_works(&this.person.id).await } async move { Ok(this.handle.backend.cl().get_works(&this.person.id).await?) }
})); }));
this.selector.set_load_local(clone!(@weak this => move || { this.selector.set_load_local(clone!(@weak this => move || {

View file

@ -1,5 +1,4 @@
use super::Widget; use super::Widget;
use gio::prelude::*;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use gtk_macros::get_widget; use gtk_macros::get_widget;

View file

@ -68,7 +68,7 @@ mod indexed_list_model {
PROPERTIES.as_ref() PROPERTIES.as_ref()
} }
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) { fn set_property(&self, _obj: &Self::Type, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.get_name() { match pspec.get_name() {
"length" => { "length" => {
let length = value.get().unwrap().unwrap(); let length = value.get().unwrap().unwrap();
@ -78,7 +78,7 @@ mod indexed_list_model {
} }
} }
fn get_property(&self, _obj: &Self::Type, id: usize, pspec: &glib::ParamSpec) -> glib::Value { fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.get_name() { match pspec.get_name() {
"length" => self.length.get().to_value(), "length" => self.length.get().to_value(),
_ => unimplemented!(), _ => unimplemented!(),
@ -160,7 +160,7 @@ mod item_index {
PROPERTIES.as_ref() PROPERTIES.as_ref()
} }
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) { fn set_property(&self, _obj: &Self::Type, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.get_name() { match pspec.get_name() {
"value" => { "value" => {
let value = value.get().unwrap().unwrap(); let value = value.get().unwrap().unwrap();
@ -170,7 +170,7 @@ mod item_index {
} }
} }
fn get_property(&self, _obj: &Self::Type, id: usize, pspec: &glib::ParamSpec) -> glib::Value { fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.get_name() { match pspec.get_name() {
"value" => self.value.get().to_value(), "value" => self.value.get().to_value(),
_ => unimplemented!(), _ => unimplemented!(),

View file

@ -119,14 +119,6 @@ impl List {
self.widget.set_selection_mode(gtk::SelectionMode::Single); self.widget.set_selection_mode(gtk::SelectionMode::Single);
} }
/// Select an item by its index. If the index is out of range, nothing will happen.
pub fn select(&self, index: usize) {
let row = self.widget.get_row_at_index(index as i32);
if let Some(row) = row {
self.widget.select_row(Some(&row));
}
}
/// Refilter the list based on the filter callback. /// Refilter the list based on the filter callback.
pub fn invalidate_filter(&self) { pub fn invalidate_filter(&self) {
self.filter.changed(gtk::FilterChange::Different); self.filter.changed(gtk::FilterChange::Different);

View file

@ -1,7 +1,7 @@
use crate::backend::{Player, PlaylistItem};
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::{Player, PlaylistItem};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;

View file

@ -1,9 +1,8 @@
use super::*; use super::*;
use crate::backend::{Backend, Person, Ensemble};
use glib::clone; use glib::clone;
use gtk::prelude::*;
use gtk_macros::get_widget; use gtk_macros::get_widget;
use libadwaita::prelude::*; use libadwaita::prelude::*;
use musicus_backend::{Backend, Person, Ensemble};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;

View file

@ -1,7 +1,6 @@
use super::Section; use super::Section;
use gettextrs::gettext; use gettextrs::gettext;
use gtk::prelude::*;
use libadwaita::prelude::*; use libadwaita::prelude::*;
/// A section showing a switch to enable uploading an item. /// A section showing a switch to enable uploading an item.
@ -9,9 +8,6 @@ pub struct UploadSection {
/// The GTK widget of the wrapped section. /// The GTK widget of the wrapped section.
pub widget: gtk::Box, pub widget: gtk::Box,
/// The section itself.
section: Section,
/// The upload switch. /// The upload switch.
switch: gtk::Switch, switch: gtk::Switch,
} }
@ -41,7 +37,6 @@ impl UploadSection {
Self { Self {
widget: section.widget.clone(), widget: section.widget.clone(),
section,
switch, switch,
} }
} }

View file

@ -1,4 +1,3 @@
use crate::backend::*;
use crate::config; use crate::config;
use crate::import::SourceSelector; use crate::import::SourceSelector;
use crate::preferences::Preferences; use crate::preferences::Preferences;
@ -11,6 +10,7 @@ use gio::prelude::*;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use gtk_macros::{action, get_widget}; use gtk_macros::{action, get_widget};
use musicus_backend::{Backend, BackendState};
use std::rc::Rc; use std::rc::Rc;
pub struct Window { pub struct Window {
@ -202,12 +202,6 @@ impl Window {
self.window.present(); self.window.present();
} }
fn reload(&self) {
self.poe_list.clone().reload();
self.navigator.reset();
self.leaflet.set_visible_child(&self.sidebar_box);
}
fn show_about_dialog(&self) { fn show_about_dialog(&self) {
let dialog = gtk::AboutDialogBuilder::new() let dialog = gtk::AboutDialogBuilder::new()
.transient_for(&self.window) .transient_for(&self.window)

View file

@ -0,0 +1,17 @@
[package]
name = "musicus_backend"
version = "0.1.0"
edition = "2018"
[dependencies]
fragile = "1.0.0"
futures-channel = "0.3.5"
gio = "0.9.1"
glib = "0.10.3"
gstreamer = "0.16.4"
gstreamer-player = "0.16.3"
musicus_client = { version = "0.1.0", path = "../musicus_client" }
musicus_database = { version = "0.1.0", path = "../musicus_database" }
secret-service = "2.0.1"
thiserror = "1.0.23"

View file

@ -0,0 +1,28 @@
/// An error that can happened within the backend.
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
ClientError(#[from] musicus_client::Error),
#[error(transparent)]
DatabaseError(#[from] musicus_database::Error),
#[error("An error happened using the SecretService.")]
SecretServiceError(#[from] secret_service::Error),
#[error("An error happened in GLib.")]
GlibError(#[from] glib::BoolError),
#[error("A channel was canceled.")]
ChannelError(#[from] futures_channel::oneshot::Canceled),
#[error("An error happened while decoding to UTF-8.")]
Utf8Error(#[from] std::str::Utf8Error),
#[error("An error happened: {0}")]
Other(&'static str),
}
pub type Result<T> = std::result::Result<T, Error>;

View file

@ -1,17 +1,18 @@
use futures_channel::mpsc; use futures_channel::mpsc;
use gio::prelude::*;
use std::cell::RefCell; use std::cell::RefCell;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
pub mod client; pub use musicus_client::*;
pub use client::*; pub use musicus_database::*;
pub mod database;
pub use database::*;
pub mod error; pub mod error;
pub use error::*; pub use error::*;
// Override the identically named types from the other crates.
pub use error::{Error, Result};
pub mod library; pub mod library;
pub use library::*; pub use library::*;
@ -43,9 +44,7 @@ pub struct Backend {
music_library_path: RefCell<Option<PathBuf>>, music_library_path: RefCell<Option<PathBuf>>,
database: RefCell<Option<Rc<DbThread>>>, database: RefCell<Option<Rc<DbThread>>>,
player: RefCell<Option<Rc<Player>>>, player: RefCell<Option<Rc<Player>>>,
server_url: RefCell<Option<String>>, client: Client,
login_data: RefCell<Option<LoginData>>,
token: RefCell<Option<String>>,
} }
impl Backend { impl Backend {
@ -61,20 +60,55 @@ impl Backend {
music_library_path: RefCell::new(None), music_library_path: RefCell::new(None),
database: RefCell::new(None), database: RefCell::new(None),
player: RefCell::new(None), player: RefCell::new(None),
server_url: RefCell::new(None), client: Client::new(),
login_data: RefCell::new(None),
token: RefCell::new(None),
} }
} }
/// Initialize the backend updating the state accordingly. /// Initialize the backend updating the state accordingly.
pub async fn init(self: Rc<Backend>) -> Result<()> { pub async fn init(self: Rc<Backend>) -> Result<()> {
self.init_library().await?; self.init_library().await?;
self.init_client()?;
if let Some(url) = self.settings.get_string("server-url") {
if !url.is_empty() {
self.client.set_server_url(&url);
}
}
if let Some(data) = secure::load_login_data()? {
self.client.set_login_data(data);
}
Ok(()) Ok(())
} }
/// Set the URL of the Musicus server to connect to.
pub fn set_server_url(&self, url: &str) -> Result<()> {
self.settings.set_string("server-url", url)?;
self.client.set_server_url(url);
Ok(())
}
/// Get the currently set server URL.
pub fn get_server_url(&self) -> Option<String> {
self.client.get_server_url()
}
/// Set the user credentials to use.
pub async fn set_login_data(&self, data: LoginData) -> Result<()> {
secure::store_login_data(data.clone()).await?;
self.client.set_login_data(data);
Ok(())
}
pub fn cl(&self) -> &Client {
&self.client
}
/// Get the currently stored login credentials.
pub fn get_login_data(&self) -> Option<LoginData> {
self.client.get_login_data()
}
/// Set the current state and notify the user interface. /// Set the current state and notify the user interface.
fn set_state(&self, state: BackendState) { fn set_state(&self, state: BackendState) {
self.state_sender.borrow_mut().try_send(state).unwrap(); self.state_sender.borrow_mut().try_send(state).unwrap();

View file

@ -1,4 +1,5 @@
use crate::backend::{Backend, BackendState, DbThread, Player, Result}; use crate::{Backend, BackendState, Player, Result};
use musicus_database::DbThread;
use gio::prelude::*; use gio::prelude::*;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;

View file

@ -1,4 +1,5 @@
use crate::backend::{Error, Result, TrackSet}; use crate::{Error, Result};
use musicus_database::TrackSet;
use gstreamer_player::prelude::*; use gstreamer_player::prelude::*;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::path::PathBuf; use std::path::PathBuf;

View file

@ -1,4 +1,5 @@
use crate::backend::{LoginData, Result}; use crate::Result;
use musicus_client::LoginData;
use futures_channel::oneshot; use futures_channel::oneshot;
use secret_service::{Collection, EncryptionType, SecretService}; use secret_service::{Collection, EncryptionType, SecretService};
use std::collections::HashMap; use std::collections::HashMap;

View file

@ -0,0 +1,11 @@
[package]
name = "musicus_client"
version = "0.1.0"
edition = "2018"
[dependencies]
isahc = "0.9.12"
musicus_database = { version = "0.1.0", path = "../musicus_database" }
serde = { version = "1.0.117", features = ["derive"] }
serde_json = "1.0.59"
thiserror = "1.0.23"

View file

@ -1,6 +1,7 @@
use crate::backend::{Backend, Ensemble, Result}; use crate::{Client, Result};
use musicus_database::Ensemble;
impl Backend { impl Client {
/// Get all available ensembles from the server. /// Get all available ensembles from the server.
pub async fn get_ensembles(&self) -> Result<Vec<Ensemble>> { pub async fn get_ensembles(&self) -> Result<Vec<Ensemble>> {
let body = self.get("ensembles").await?; let body = self.get("ensembles").await?;

View file

@ -1,6 +1,6 @@
use isahc::http::StatusCode; use isahc::http::StatusCode;
/// An error that can happen within the backend. /// An error within the client.
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum Error { pub enum Error {
#[error("The users login credentials were wrong.")] #[error("The users login credentials were wrong.")]
@ -21,33 +21,14 @@ pub enum Error {
#[error("A networking error happened.")] #[error("A networking error happened.")]
HttpError(#[from] isahc::http::Error), HttpError(#[from] isahc::http::Error),
#[error(transparent)] #[error("An error happened when serializing/deserializing.")]
DatabaseError(#[from] crate::backend::DatabaseError), SerdeError(#[from] serde_json::Error),
#[error("An IO error happened.")] #[error("An IO error happened.")]
IoError(#[from] std::io::Error), IoError(#[from] std::io::Error),
#[error("An error happened using the SecretService.")]
SecretServiceError(#[from] secret_service::Error),
#[error("An error happened while serializing or deserializing.")]
SerdeError(#[from] serde_json::Error),
#[error("An error happened in GLib.")]
GlibError(#[from] glib::BoolError),
#[error("A channel was canceled.")]
ChannelError(#[from] futures_channel::oneshot::Canceled),
#[error("Error decoding to UTF8.")]
Utf8Error(#[from] std::str::Utf8Error),
#[error("An error happened: {0}")] #[error("An error happened: {0}")]
Other(&'static str), Other(&'static str),
// TODO: Remove this once anyhow has been dropped as a dependency.
#[error("An unkown error happened.")]
Unknown(#[from] anyhow::Error),
} }

View file

@ -1,6 +1,7 @@
use crate::backend::{Backend, Instrument, Result}; use crate::{Client, Result};
use musicus_database::Instrument;
impl Backend { impl Client {
/// Get all available instruments from the server. /// Get all available instruments from the server.
pub async fn get_instruments(&self) -> Result<Vec<Instrument>> { pub async fn get_instruments(&self) -> Result<Vec<Instrument>> {
let body = self.get("instruments").await?; let body = self.get("instruments").await?;

View file

@ -1,13 +1,15 @@
use crate::backend::{Backend, Error, Result, secure};
use gio::prelude::*;
use isahc::http::StatusCode; use isahc::http::StatusCode;
use isahc::prelude::*; use isahc::prelude::*;
use serde::Serialize; use serde::Serialize;
use std::time::Duration; use std::time::Duration;
use std::cell::RefCell;
pub mod ensembles; pub mod ensembles;
pub use ensembles::*; pub use ensembles::*;
pub mod error;
pub use error::*;
pub mod instruments; pub mod instruments;
pub use instruments::*; pub use instruments::*;
@ -34,37 +36,26 @@ pub struct LoginData {
pub password: String, pub password: String,
} }
impl Backend { /// A client for accessing the Wolfgang API.
/// Initialize the client. pub struct Client {
pub(super) fn init_client(&self) -> Result<()> { server_url: RefCell<Option<String>>,
if let Some(data) = secure::load_login_data()? { login_data: RefCell<Option<LoginData>>,
self.login_data.replace(Some(data)); token: RefCell<Option<String>>,
} }
if let Some(url) = self.settings.get_string("server-url") { impl Client {
if !url.is_empty() { /// Create a new client.
self.server_url.replace(Some(url.to_string())); pub fn new() -> Self {
} Self {
server_url: RefCell::new(None),
login_data: RefCell::new(None),
token: RefCell::new(None),
} }
Ok(())
} }
/// Set the URL of the Musicus server to connect to. /// Set the URL of the Musicus server to connect to.
pub fn set_server_url(&self, url: &str) -> Result<()> { pub fn set_server_url(&self, url: &str) {
self.settings.set_string("server-url", url)?; self.server_url.replace(Some(url.to_owned()));
self.server_url.replace(Some(url.to_string()));
Ok(())
}
/// Get the currently used login token.
pub fn get_token(&self) -> Option<String> {
self.token.borrow().clone()
}
/// Set the login token to use. This will be done automatically by the login method.
pub fn set_token(&self, token: &str) {
self.token.replace(Some(token.to_string()));
} }
/// Get the currently set server URL. /// Get the currently set server URL.
@ -72,9 +63,10 @@ impl Backend {
self.server_url.borrow().clone() self.server_url.borrow().clone()
} }
/// Require the server URL to be set. /// Set the user credentials to use.
fn server_url(&self) -> Result<String> { pub fn set_login_data(&self, data: LoginData) {
self.get_server_url().ok_or(Error::Other("The server URL is not available!")) self.login_data.replace(Some(data));
self.token.replace(None);
} }
/// Get the currently stored login credentials. /// Get the currently stored login credentials.
@ -82,18 +74,6 @@ impl Backend {
self.login_data.borrow().clone() self.login_data.borrow().clone()
} }
fn login_data(&self) -> Result<LoginData> {
self.get_login_data().ok_or(Error::Other("The login data is unset!"))
}
/// Set the user credentials to use.
pub async fn set_login_data(&self, data: LoginData) -> Result<()> {
secure::store_login_data(data.clone()).await?;
self.login_data.replace(Some(data));
self.token.replace(None);
Ok(())
}
/// Try to login a user with the provided credentials and return, wether the login suceeded. /// Try to login a user with the provided credentials and return, wether the login suceeded.
pub async fn login(&self) -> Result<bool> { pub async fn login(&self) -> Result<bool> {
let server_url = self.server_url()?; let server_url = self.server_url()?;
@ -109,7 +89,7 @@ impl Backend {
let success = match response.status() { let success = match response.status() {
StatusCode::OK => { StatusCode::OK => {
let token = response.text_async().await?; let token = response.text_async().await?;
self.set_token(&token); self.token.replace(Some(token));
true true
} }
StatusCode::UNAUTHORIZED => false, StatusCode::UNAUTHORIZED => false,
@ -136,30 +116,27 @@ impl Backend {
/// Make an authenticated post request to the server. /// Make an authenticated post request to the server.
async fn post(&self, url: &str, body: String) -> Result<String> { async fn post(&self, url: &str, body: String) -> Result<String> {
let body = match self.get_token() { let body = if self.token.borrow().is_some() {
Some(_) => { let mut response = self.post_priv(url, body.clone()).await?;
let mut response = self.post_priv(url, body.clone()).await?;
// Try one more time (maybe the token was expired) // Try one more time (maybe the token was expired)
if response.status() == StatusCode::UNAUTHORIZED { if response.status() == StatusCode::UNAUTHORIZED {
if self.login().await? { if self.login().await? {
response = self.post_priv(url, body).await?; response = self.post_priv(url, body).await?;
} else {
Err(Error::LoginFailed)?;
}
}
response.text_async().await?
}
None => {
let mut response = if self.login().await? {
self.post_priv(url, body).await?
} else { } else {
Err(Error::LoginFailed)? Err(Error::LoginFailed)?;
}; }
response.text_async().await?
} }
response.text_async().await?
} else {
let mut response = if self.login().await? {
self.post_priv(url, body).await?
} else {
Err(Error::LoginFailed)?
};
response.text_async().await?
}; };
Ok(body) Ok(body)
@ -168,7 +145,7 @@ impl Backend {
/// Post something to the server assuming there is a valid login token. /// Post something to the server assuming there is a valid login token.
async fn post_priv(&self, url: &str, body: String) -> Result<Response<Body>> { async fn post_priv(&self, url: &str, body: String) -> Result<Response<Body>> {
let server_url = self.server_url()?; let server_url = self.server_url()?;
let token = self.get_token().ok_or(Error::Other("No login token found!"))?; let token = self.token()?;
let response = Request::post(format!("{}/{}", server_url, url)) let response = Request::post(format!("{}/{}", server_url, url))
.timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(10))
@ -180,4 +157,19 @@ impl Backend {
Ok(response) Ok(response)
} }
/// Require the server URL to be set.
fn server_url(&self) -> Result<String> {
self.get_server_url().ok_or(Error::Other("The server URL is not available!"))
}
/// Require the login data to be set.
fn login_data(&self) -> Result<LoginData> {
self.get_login_data().ok_or(Error::Other("The login data is unset!"))
}
/// Require a login token to be set.
fn token(&self) -> Result<String> {
self.token.borrow().clone().ok_or(Error::Other("No login token found!"))
}
} }

View file

@ -1,6 +1,7 @@
use crate::backend::{Backend, Medium, Result}; use crate::{Client, Result};
use musicus_database::Medium;
impl Backend { impl Client {
/// Get all available mediums from the server, that contain the specified /// Get all available mediums from the server, that contain the specified
/// recording. /// recording.
pub async fn get_mediums_for_recording(&self, recording_id: &str) -> Result<Vec<Medium>> { pub async fn get_mediums_for_recording(&self, recording_id: &str) -> Result<Vec<Medium>> {

View file

@ -1,6 +1,7 @@
use crate::backend::{Backend, Person, Result}; use crate::{Client, Result};
use musicus_database::Person;
impl Backend { impl Client {
/// Get all available persons from the server. /// Get all available persons from the server.
pub async fn get_persons(&self) -> Result<Vec<Person>> { pub async fn get_persons(&self) -> Result<Vec<Person>> {
let body = self.get("persons").await?; let body = self.get("persons").await?;

View file

@ -1,6 +1,7 @@
use crate::backend::{Backend, Recording, Result}; use crate::{Client, Result};
use musicus_database::Recording;
impl Backend { impl Client {
/// Get all available recordings from the server. /// Get all available recordings from the server.
pub async fn get_recordings_for_work(&self, work_id: &str) -> Result<Vec<Recording>> { pub async fn get_recordings_for_work(&self, work_id: &str) -> Result<Vec<Recording>> {
let body = self.get(&format!("works/{}/recordings", work_id)).await?; let body = self.get(&format!("works/{}/recordings", work_id)).await?;

View file

@ -1,4 +1,4 @@
use crate::backend::{Backend, Result}; use crate::{Client, Result};
use isahc::http::StatusCode; use isahc::http::StatusCode;
use isahc::prelude::*; use isahc::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -23,7 +23,7 @@ pub struct UserRegistration {
pub answer: String, pub answer: String,
} }
impl Backend { impl Client {
/// Request a new captcha for registration. /// Request a new captcha for registration.
pub async fn get_captcha(&self) -> Result<Captcha> { pub async fn get_captcha(&self) -> Result<Captcha> {
let body = self.get("captcha").await?; let body = self.get("captcha").await?;
@ -36,7 +36,7 @@ impl Backend {
pub async fn register(&self, data: UserRegistration) -> Result<bool> { pub async fn register(&self, data: UserRegistration) -> Result<bool> {
let server_url = self.server_url()?; let server_url = self.server_url()?;
let mut response = Request::post(format!("{}/users", server_url)) let response = Request::post(format!("{}/users", server_url))
.timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(10))
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.body(serde_json::to_string(&data)?)? .body(serde_json::to_string(&data)?)?

View file

@ -1,6 +1,7 @@
use crate::backend::{Backend, Result, Work}; use crate::{Client, Result};
use musicus_database::Work;
impl Backend { impl Client {
/// Get all available works from the server. /// Get all available works from the server.
pub async fn get_works(&self, composer_id: &str) -> Result<Vec<Work>> { pub async fn get_works(&self, composer_id: &str) -> Result<Vec<Work>> {
let body = self.get(&format!("persons/{}/works", composer_id)).await?; let body = self.get(&format!("persons/{}/works", composer_id)).await?;

View file

@ -0,0 +1,15 @@
[package]
name = "musicus_database"
version = "0.1.0"
edition = "2018"
workspace = "../.."
[dependencies]
diesel = { version = "1.4.5", features = ["sqlite"] }
diesel_migrations = "1.4.0"
futures-channel = "0.3.5"
rand = "0.7.3"
serde = { version = "1.0.117", features = ["derive"] }
serde_json = "1.0.59"
thiserror = "1.0.23"
uuid = { version = "0.8", features = ["v4"] }

View file

@ -1,5 +1,5 @@
use super::schema::ensembles; use super::schema::ensembles;
use super::{Database, DatabaseResult}; use super::{Database, Result};
use diesel::prelude::*; use diesel::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -13,7 +13,7 @@ pub struct Ensemble {
impl Database { impl Database {
/// Update an existing ensemble or insert a new one. /// Update an existing ensemble or insert a new one.
pub fn update_ensemble(&self, ensemble: Ensemble) -> DatabaseResult<()> { pub fn update_ensemble(&self, ensemble: Ensemble) -> Result<()> {
self.defer_foreign_keys()?; self.defer_foreign_keys()?;
self.connection.transaction(|| { self.connection.transaction(|| {
@ -26,7 +26,7 @@ impl Database {
} }
/// Get an existing ensemble. /// Get an existing ensemble.
pub fn get_ensemble(&self, id: &str) -> DatabaseResult<Option<Ensemble>> { pub fn get_ensemble(&self, id: &str) -> Result<Option<Ensemble>> {
let ensemble = ensembles::table let ensemble = ensembles::table
.filter(ensembles::id.eq(id)) .filter(ensembles::id.eq(id))
.load::<Ensemble>(&self.connection)? .load::<Ensemble>(&self.connection)?
@ -37,14 +37,14 @@ impl Database {
} }
/// Delete an existing ensemble. /// Delete an existing ensemble.
pub fn delete_ensemble(&self, id: &str) -> DatabaseResult<()> { pub fn delete_ensemble(&self, id: &str) -> Result<()> {
diesel::delete(ensembles::table.filter(ensembles::id.eq(id))).execute(&self.connection)?; diesel::delete(ensembles::table.filter(ensembles::id.eq(id))).execute(&self.connection)?;
Ok(()) Ok(())
} }
/// Get all existing ensembles. /// Get all existing ensembles.
pub fn get_ensembles(&self) -> DatabaseResult<Vec<Ensemble>> { pub fn get_ensembles(&self) -> Result<Vec<Ensemble>> {
let ensembles = ensembles::table.load::<Ensemble>(&self.connection)?; let ensembles = ensembles::table.load::<Ensemble>(&self.connection)?;
Ok(ensembles) Ok(ensembles)

View file

@ -1,8 +1,6 @@
use thiserror::Error;
/// Error that happens within the database module. /// Error that happens within the database module.
#[derive(Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum DatabaseError { pub enum Error {
#[error(transparent)] #[error(transparent)]
ConnectionError(#[from] diesel::result::ConnectionError), ConnectionError(#[from] diesel::result::ConnectionError),
@ -23,4 +21,4 @@ pub enum DatabaseError {
} }
/// Return type for database methods. /// Return type for database methods.
pub type DatabaseResult<T> = Result<T, DatabaseError>; pub type Result<T> = std::result::Result<T, Error>;

View file

@ -1,5 +1,5 @@
use super::schema::instruments; use super::schema::instruments;
use super::{Database, DatabaseResult}; use super::{Database, Result};
use diesel::prelude::*; use diesel::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -13,7 +13,7 @@ pub struct Instrument {
impl Database { impl Database {
/// Update an existing instrument or insert a new one. /// Update an existing instrument or insert a new one.
pub fn update_instrument(&self, instrument: Instrument) -> DatabaseResult<()> { pub fn update_instrument(&self, instrument: Instrument) -> Result<()> {
self.defer_foreign_keys()?; self.defer_foreign_keys()?;
self.connection.transaction(|| { self.connection.transaction(|| {
@ -26,7 +26,7 @@ impl Database {
} }
/// Get an existing instrument. /// Get an existing instrument.
pub fn get_instrument(&self, id: &str) -> DatabaseResult<Option<Instrument>> { pub fn get_instrument(&self, id: &str) -> Result<Option<Instrument>> {
let instrument = instruments::table let instrument = instruments::table
.filter(instruments::id.eq(id)) .filter(instruments::id.eq(id))
.load::<Instrument>(&self.connection)? .load::<Instrument>(&self.connection)?
@ -37,7 +37,7 @@ impl Database {
} }
/// Delete an existing instrument. /// Delete an existing instrument.
pub fn delete_instrument(&self, id: &str) -> DatabaseResult<()> { pub fn delete_instrument(&self, id: &str) -> Result<()> {
diesel::delete(instruments::table.filter(instruments::id.eq(id))) diesel::delete(instruments::table.filter(instruments::id.eq(id)))
.execute(&self.connection)?; .execute(&self.connection)?;
@ -45,7 +45,7 @@ impl Database {
} }
/// Get all existing instruments. /// Get all existing instruments.
pub fn get_instruments(&self) -> DatabaseResult<Vec<Instrument>> { pub fn get_instruments(&self) -> Result<Vec<Instrument>> {
let instruments = instruments::table.load::<Instrument>(&self.connection)?; let instruments = instruments::table.load::<Instrument>(&self.connection)?;
Ok(instruments) Ok(instruments)

View file

@ -1,3 +1,11 @@
// Required for schema.rs
#[macro_use]
extern crate diesel;
// Required for embed_migrations macro in database.rs
#[macro_use]
extern crate diesel_migrations;
use diesel::prelude::*; use diesel::prelude::*;
pub mod ensembles; pub mod ensembles;
@ -44,7 +52,7 @@ pub struct Database {
impl Database { impl Database {
/// Create a new database interface and run migrations if necessary. /// Create a new database interface and run migrations if necessary.
pub fn new(file_name: &str) -> DatabaseResult<Database> { pub fn new(file_name: &str) -> Result<Database> {
let connection = SqliteConnection::establish(file_name)?; let connection = SqliteConnection::establish(file_name)?;
diesel::sql_query("PRAGMA foreign_keys = ON").execute(&connection)?; diesel::sql_query("PRAGMA foreign_keys = ON").execute(&connection)?;
@ -54,7 +62,7 @@ impl Database {
} }
/// Defer all foreign keys for the next transaction. /// Defer all foreign keys for the next transaction.
fn defer_foreign_keys(&self) -> DatabaseResult<()> { fn defer_foreign_keys(&self) -> Result<()> {
diesel::sql_query("PRAGMA defer_foreign_keys = ON").execute(&self.connection)?; diesel::sql_query("PRAGMA defer_foreign_keys = ON").execute(&self.connection)?;
Ok(()) Ok(())
} }

View file

@ -1,6 +1,6 @@
use super::generate_id; use super::generate_id;
use super::schema::{mediums, recordings, track_sets, tracks}; use super::schema::{mediums, recordings, track_sets, tracks};
use super::{Database, DatabaseError, Recording, DatabaseResult}; use super::{Database, Error, Recording, Result};
use diesel::prelude::*; use diesel::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -79,10 +79,10 @@ struct TrackRow {
impl Database { impl Database {
/// Update an existing medium or insert a new one. /// Update an existing medium or insert a new one.
pub fn update_medium(&self, medium: Medium) -> DatabaseResult<()> { pub fn update_medium(&self, medium: Medium) -> Result<()> {
self.defer_foreign_keys()?; self.defer_foreign_keys()?;
self.connection.transaction::<(), DatabaseError, _>(|| { self.connection.transaction::<(), Error, _>(|| {
let medium_id = &medium.id; let medium_id = &medium.id;
// This will also delete the track sets and tracks. // This will also delete the track sets and tracks.
@ -152,7 +152,7 @@ impl Database {
} }
/// Get an existing medium. /// Get an existing medium.
pub fn get_medium(&self, id: &str) -> DatabaseResult<Option<Medium>> { pub fn get_medium(&self, id: &str) -> Result<Option<Medium>> {
let row = mediums::table let row = mediums::table
.filter(mediums::id.eq(id)) .filter(mediums::id.eq(id))
.load::<MediumRow>(&self.connection)? .load::<MediumRow>(&self.connection)?
@ -169,13 +169,13 @@ impl Database {
/// Delete a medium and all of its tracks. This will fail, if the music /// Delete a medium and all of its tracks. This will fail, if the music
/// library contains audio files referencing any of those tracks. /// library contains audio files referencing any of those tracks.
pub fn delete_medium(&self, id: &str) -> DatabaseResult<()> { pub fn delete_medium(&self, id: &str) -> Result<()> {
diesel::delete(mediums::table.filter(mediums::id.eq(id))).execute(&self.connection)?; diesel::delete(mediums::table.filter(mediums::id.eq(id))).execute(&self.connection)?;
Ok(()) Ok(())
} }
/// Get all available track sets for a recording. /// Get all available track sets for a recording.
pub fn get_track_sets(&self, recording_id: &str) -> DatabaseResult<Vec<TrackSet>> { pub fn get_track_sets(&self, recording_id: &str) -> Result<Vec<TrackSet>> {
let mut track_sets: Vec<TrackSet> = Vec::new(); let mut track_sets: Vec<TrackSet> = Vec::new();
let rows = track_sets::table let rows = track_sets::table
@ -193,7 +193,7 @@ impl Database {
} }
/// Retrieve all available information on a medium from related tables. /// Retrieve all available information on a medium from related tables.
fn get_medium_data(&self, row: MediumRow) -> DatabaseResult<Medium> { fn get_medium_data(&self, row: MediumRow) -> Result<Medium> {
let track_set_rows = track_sets::table let track_set_rows = track_sets::table
.filter(track_sets::medium.eq(&row.id)) .filter(track_sets::medium.eq(&row.id))
.order_by(track_sets::index) .order_by(track_sets::index)
@ -217,12 +217,12 @@ impl Database {
} }
/// Convert a track set row from the database to an actual track set. /// Convert a track set row from the database to an actual track set.
fn get_track_set_from_row(&self, row: TrackSetRow) -> DatabaseResult<TrackSet> { fn get_track_set_from_row(&self, row: TrackSetRow) -> Result<TrackSet> {
let recording_id = row.recording; let recording_id = row.recording;
let recording = self let recording = self
.get_recording(&recording_id)? .get_recording(&recording_id)?
.ok_or(DatabaseError::Other(format!( .ok_or(Error::Other(format!(
"Failed to get recording ({}) for track set ({}).", "Failed to get recording ({}) for track set ({}).",
recording_id, recording_id,
row.id, row.id,
@ -241,12 +241,11 @@ impl Database {
.split(',') .split(',')
.map(|part_index| { .map(|part_index| {
str::parse(part_index) str::parse(part_index)
.or(Err(DatabaseError::Other(format!( .or(Err(Error::Other(
"Failed to parse part index from '{}'.", format!("Failed to parse part index from '{}'.", track_row.work_parts,
track_row.work_parts, ))))
)))?)
}) })
.collect::<DatabaseResult<Vec<usize>>>()?; .collect::<Result<Vec<usize>>>()?;
let track = Track { let track = Track {
work_parts, work_parts,

View file

@ -1,5 +1,5 @@
use super::schema::persons; use super::schema::persons;
use super::{Database, DatabaseResult}; use super::{Database, Result};
use diesel::prelude::*; use diesel::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -26,7 +26,7 @@ impl Person {
impl Database { impl Database {
/// Update an existing person or insert a new one. /// Update an existing person or insert a new one.
pub fn update_person(&self, person: Person) -> DatabaseResult<()> { pub fn update_person(&self, person: Person) -> Result<()> {
self.defer_foreign_keys()?; self.defer_foreign_keys()?;
self.connection.transaction(|| { self.connection.transaction(|| {
@ -39,7 +39,7 @@ impl Database {
} }
/// Get an existing person. /// Get an existing person.
pub fn get_person(&self, id: &str) -> DatabaseResult<Option<Person>> { pub fn get_person(&self, id: &str) -> Result<Option<Person>> {
let person = persons::table let person = persons::table
.filter(persons::id.eq(id)) .filter(persons::id.eq(id))
.load::<Person>(&self.connection)? .load::<Person>(&self.connection)?
@ -50,14 +50,14 @@ impl Database {
} }
/// Delete an existing person. /// Delete an existing person.
pub fn delete_person(&self, id: &str) -> DatabaseResult<()> { pub fn delete_person(&self, id: &str) -> Result<()> {
diesel::delete(persons::table.filter(persons::id.eq(id))).execute(&self.connection)?; diesel::delete(persons::table.filter(persons::id.eq(id))).execute(&self.connection)?;
Ok(()) Ok(())
} }
/// Get all existing persons. /// Get all existing persons.
pub fn get_persons(&self) -> DatabaseResult<Vec<Person>> { pub fn get_persons(&self) -> Result<Vec<Person>> {
let persons = persons::table.load::<Person>(&self.connection)?; let persons = persons::table.load::<Person>(&self.connection)?;
Ok(persons) Ok(persons)

View file

@ -1,6 +1,6 @@
use super::generate_id; use super::generate_id;
use super::schema::{ensembles, performances, persons, recordings}; use super::schema::{ensembles, performances, persons, recordings};
use super::{Database, Ensemble, DatabaseError, Instrument, Person, DatabaseResult, Work}; use super::{Database, Ensemble, Error, Instrument, Person, Result, Work};
use diesel::prelude::*; use diesel::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -119,9 +119,9 @@ impl Recording {
impl Database { impl Database {
/// Update an existing recording or insert a new one. /// Update an existing recording or insert a new one.
// TODO: Think about whether to also insert the other items. // TODO: Think about whether to also insert the other items.
pub fn update_recording(&self, recording: Recording) -> DatabaseResult<()> { pub fn update_recording(&self, recording: Recording) -> Result<()> {
self.defer_foreign_keys()?; self.defer_foreign_keys()?;
self.connection.transaction::<(), DatabaseError, _>(|| { self.connection.transaction::<(), Error, _>(|| {
let recording_id = &recording.id; let recording_id = &recording.id;
self.delete_recording(recording_id)?; self.delete_recording(recording_id)?;
@ -179,7 +179,7 @@ impl Database {
} }
/// Check whether the database contains a recording. /// Check whether the database contains a recording.
pub fn recording_exists(&self, id: &str) -> DatabaseResult<bool> { pub fn recording_exists(&self, id: &str) -> Result<bool> {
let exists = recordings::table let exists = recordings::table
.filter(recordings::id.eq(id)) .filter(recordings::id.eq(id))
.load::<RecordingRow>(&self.connection)? .load::<RecordingRow>(&self.connection)?
@ -190,7 +190,7 @@ impl Database {
} }
/// Get an existing recording. /// Get an existing recording.
pub fn get_recording(&self, id: &str) -> DatabaseResult<Option<Recording>> { pub fn get_recording(&self, id: &str) -> Result<Option<Recording>> {
let row = recordings::table let row = recordings::table
.filter(recordings::id.eq(id)) .filter(recordings::id.eq(id))
.load::<RecordingRow>(&self.connection)? .load::<RecordingRow>(&self.connection)?
@ -206,7 +206,7 @@ impl Database {
} }
/// Retrieve all available information on a recording from related tables. /// Retrieve all available information on a recording from related tables.
fn get_recording_data(&self, row: RecordingRow) -> DatabaseResult<Recording> { fn get_recording_data(&self, row: RecordingRow) -> Result<Recording> {
let mut performance_descriptions: Vec<Performance> = Vec::new(); let mut performance_descriptions: Vec<Performance> = Vec::new();
let performance_rows = performances::table let performance_rows = performances::table
@ -218,7 +218,7 @@ impl Database {
person: match row.person { person: match row.person {
Some(id) => Some( Some(id) => Some(
self.get_person(&id)? self.get_person(&id)?
.ok_or(DatabaseError::Other(format!( .ok_or(Error::Other(format!(
"Failed to get person ({}) for recording ({}).", "Failed to get person ({}) for recording ({}).",
id, id,
row.id, row.id,
@ -229,7 +229,7 @@ impl Database {
ensemble: match row.ensemble { ensemble: match row.ensemble {
Some(id) => Some( Some(id) => Some(
self.get_ensemble(&id)? self.get_ensemble(&id)?
.ok_or(DatabaseError::Other(format!( .ok_or(Error::Other(format!(
"Failed to get ensemble ({}) for recording ({}).", "Failed to get ensemble ({}) for recording ({}).",
id, id,
row.id, row.id,
@ -240,7 +240,7 @@ impl Database {
role: match row.role { role: match row.role {
Some(id) => Some( Some(id) => Some(
self.get_instrument(&id)? self.get_instrument(&id)?
.ok_or(DatabaseError::Other(format!( .ok_or(Error::Other(format!(
"Failed to get instrument ({}) for recording ({}).", "Failed to get instrument ({}) for recording ({}).",
id, id,
row.id, row.id,
@ -254,7 +254,7 @@ impl Database {
let work_id = &row.work; let work_id = &row.work;
let work = self let work = self
.get_work(work_id)? .get_work(work_id)?
.ok_or(DatabaseError::Other(format!( .ok_or(Error::Other(format!(
"Failed to get work ({}) for recording ({}).", "Failed to get work ({}) for recording ({}).",
work_id, work_id,
row.id, row.id,
@ -271,7 +271,7 @@ impl Database {
} }
/// Get all available information on all recordings where a person is performing. /// Get all available information on all recordings where a person is performing.
pub fn get_recordings_for_person(&self, person_id: &str) -> DatabaseResult<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
@ -289,7 +289,7 @@ impl Database {
} }
/// Get all available information on all recordings where an ensemble is performing. /// Get all available information on all recordings where an ensemble is performing.
pub fn get_recordings_for_ensemble(&self, ensemble_id: &str) -> DatabaseResult<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
@ -307,7 +307,7 @@ impl Database {
} }
/// Get allavailable information on all recordings of a work. /// Get allavailable information on all recordings of a work.
pub fn get_recordings_for_work(&self, work_id: &str) -> DatabaseResult<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
@ -323,7 +323,7 @@ impl Database {
/// Delete an existing recording. This will fail if there are still references to this /// Delete an existing recording. This will fail if there are still references to this
/// recording from other tables that are not directly part of the recording data. /// recording from other tables that are not directly part of the recording data.
pub fn delete_recording(&self, id: &str) -> DatabaseResult<()> { pub fn delete_recording(&self, id: &str) -> Result<()> {
diesel::delete(recordings::table.filter(recordings::id.eq(id))) diesel::delete(recordings::table.filter(recordings::id.eq(id)))
.execute(&self.connection)?; .execute(&self.connection)?;
Ok(()) Ok(())

View file

@ -6,31 +6,31 @@ use std::thread;
/// An action the database thread can perform. /// An action the database thread can perform.
pub enum Action { pub enum Action {
UpdatePerson(Person, Sender<DatabaseResult<()>>), UpdatePerson(Person, Sender<Result<()>>),
GetPerson(String, Sender<DatabaseResult<Option<Person>>>), GetPerson(String, Sender<Result<Option<Person>>>),
DeletePerson(String, Sender<DatabaseResult<()>>), DeletePerson(String, Sender<Result<()>>),
GetPersons(Sender<DatabaseResult<Vec<Person>>>), GetPersons(Sender<Result<Vec<Person>>>),
UpdateInstrument(Instrument, Sender<DatabaseResult<()>>), UpdateInstrument(Instrument, Sender<Result<()>>),
GetInstrument(String, Sender<DatabaseResult<Option<Instrument>>>), GetInstrument(String, Sender<Result<Option<Instrument>>>),
DeleteInstrument(String, Sender<DatabaseResult<()>>), DeleteInstrument(String, Sender<Result<()>>),
GetInstruments(Sender<DatabaseResult<Vec<Instrument>>>), GetInstruments(Sender<Result<Vec<Instrument>>>),
UpdateWork(Work, Sender<DatabaseResult<()>>), UpdateWork(Work, Sender<Result<()>>),
DeleteWork(String, Sender<DatabaseResult<()>>), DeleteWork(String, Sender<Result<()>>),
GetWorks(String, Sender<DatabaseResult<Vec<Work>>>), GetWorks(String, Sender<Result<Vec<Work>>>),
UpdateEnsemble(Ensemble, Sender<DatabaseResult<()>>), UpdateEnsemble(Ensemble, Sender<Result<()>>),
GetEnsemble(String, Sender<DatabaseResult<Option<Ensemble>>>), GetEnsemble(String, Sender<Result<Option<Ensemble>>>),
DeleteEnsemble(String, Sender<DatabaseResult<()>>), DeleteEnsemble(String, Sender<Result<()>>),
GetEnsembles(Sender<DatabaseResult<Vec<Ensemble>>>), GetEnsembles(Sender<Result<Vec<Ensemble>>>),
UpdateRecording(Recording, Sender<DatabaseResult<()>>), UpdateRecording(Recording, Sender<Result<()>>),
DeleteRecording(String, Sender<DatabaseResult<()>>), DeleteRecording(String, Sender<Result<()>>),
GetRecordingsForPerson(String, Sender<DatabaseResult<Vec<Recording>>>), GetRecordingsForPerson(String, Sender<Result<Vec<Recording>>>),
GetRecordingsForEnsemble(String, Sender<DatabaseResult<Vec<Recording>>>), GetRecordingsForEnsemble(String, Sender<Result<Vec<Recording>>>),
GetRecordingsForWork(String, Sender<DatabaseResult<Vec<Recording>>>), GetRecordingsForWork(String, Sender<Result<Vec<Recording>>>),
RecordingExists(String, Sender<DatabaseResult<bool>>), RecordingExists(String, Sender<Result<bool>>),
UpdateMedium(Medium, Sender<DatabaseResult<()>>), UpdateMedium(Medium, Sender<Result<()>>),
GetMedium(String, Sender<DatabaseResult<Option<Medium>>>), GetMedium(String, Sender<Result<Option<Medium>>>),
DeleteMedium(String, Sender<DatabaseResult<()>>), DeleteMedium(String, Sender<Result<()>>),
GetTrackSets(String, Sender<DatabaseResult<Vec<TrackSet>>>), GetTrackSets(String, Sender<Result<Vec<TrackSet>>>),
Stop(Sender<()>), Stop(Sender<()>),
} }
@ -43,7 +43,7 @@ pub struct DbThread {
impl DbThread { impl DbThread {
/// Create a new database connection in a background thread. /// Create a new database connection in a background thread.
pub async fn new(path: String) -> DatabaseResult<Self> { pub async fn new(path: String) -> Result<Self> {
let (action_sender, action_receiver) = mpsc::channel(); let (action_sender, action_receiver) = mpsc::channel();
let (ready_sender, ready_receiver) = oneshot::channel(); let (ready_sender, ready_receiver) = oneshot::channel();
@ -149,14 +149,14 @@ impl DbThread {
} }
/// Update an existing person or insert a new one. /// Update an existing person or insert a new one.
pub async fn update_person(&self, person: Person) -> DatabaseResult<()> { pub async fn update_person(&self, person: Person) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(UpdatePerson(person, sender))?; self.action_sender.send(UpdatePerson(person, sender))?;
receiver.await? receiver.await?
} }
/// Get an existing person. /// Get an existing person.
pub async fn get_person(&self, id: &str) -> DatabaseResult<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.to_string(), sender))?; self.action_sender.send(GetPerson(id.to_string(), sender))?;
receiver.await? receiver.await?
@ -164,7 +164,7 @@ impl DbThread {
/// Delete an existing person. This will fail, if there are still other items referencing /// Delete an existing person. This will fail, if there are still other items referencing
/// this person. /// this person.
pub async fn delete_person(&self, id: &str) -> DatabaseResult<()> { pub async fn delete_person(&self, id: &str) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender self.action_sender
.send(DeletePerson(id.to_string(), sender))?; .send(DeletePerson(id.to_string(), sender))?;
@ -172,14 +172,14 @@ impl DbThread {
} }
/// Get all existing persons. /// Get all existing persons.
pub async fn get_persons(&self) -> DatabaseResult<Vec<Person>> { pub async fn get_persons(&self) -> Result<Vec<Person>> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetPersons(sender))?; self.action_sender.send(GetPersons(sender))?;
receiver.await? receiver.await?
} }
/// Update an existing instrument or insert a new one. /// Update an existing instrument or insert a new one.
pub async fn update_instrument(&self, instrument: Instrument) -> DatabaseResult<()> { pub async fn update_instrument(&self, instrument: Instrument) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender self.action_sender
.send(UpdateInstrument(instrument, sender))?; .send(UpdateInstrument(instrument, sender))?;
@ -187,7 +187,7 @@ impl DbThread {
} }
/// Get an existing instrument. /// Get an existing instrument.
pub async fn get_instrument(&self, id: &str) -> DatabaseResult<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 self.action_sender
.send(GetInstrument(id.to_string(), sender))?; .send(GetInstrument(id.to_string(), sender))?;
@ -196,7 +196,7 @@ impl DbThread {
/// Delete an existing instrument. This will fail, if there are still other items referencing /// Delete an existing instrument. This will fail, if there are still other items referencing
/// this instrument. /// this instrument.
pub async fn delete_instrument(&self, id: &str) -> DatabaseResult<()> { pub async fn delete_instrument(&self, id: &str) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender self.action_sender
.send(DeleteInstrument(id.to_string(), sender))?; .send(DeleteInstrument(id.to_string(), sender))?;
@ -204,14 +204,14 @@ impl DbThread {
} }
/// Get all existing instruments. /// Get all existing instruments.
pub async fn get_instruments(&self) -> DatabaseResult<Vec<Instrument>> { pub async fn get_instruments(&self) -> Result<Vec<Instrument>> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetInstruments(sender))?; self.action_sender.send(GetInstruments(sender))?;
receiver.await? receiver.await?
} }
/// Update an existing work or insert a new one. /// Update an existing work or insert a new one.
pub async fn update_work(&self, work: Work) -> DatabaseResult<()> { pub async fn update_work(&self, work: Work) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(UpdateWork(work, sender))?; self.action_sender.send(UpdateWork(work, sender))?;
receiver.await? receiver.await?
@ -219,7 +219,7 @@ impl DbThread {
/// Delete an existing work. This will fail, if there are still other items referencing /// Delete an existing work. This will fail, if there are still other items referencing
/// this work. /// this work.
pub async fn delete_work(&self, id: &str) -> DatabaseResult<()> { pub async fn delete_work(&self, id: &str) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender self.action_sender
.send(DeleteWork(id.to_string(), sender))?; .send(DeleteWork(id.to_string(), sender))?;
@ -227,7 +227,7 @@ impl DbThread {
} }
/// Get information on all existing works by a composer. /// Get information on all existing works by a composer.
pub async fn get_works(&self, person_id: &str) -> DatabaseResult<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 self.action_sender
.send(GetWorks(person_id.to_string(), sender))?; .send(GetWorks(person_id.to_string(), sender))?;
@ -235,14 +235,14 @@ impl DbThread {
} }
/// Update an existing ensemble or insert a new one. /// Update an existing ensemble or insert a new one.
pub async fn update_ensemble(&self, ensemble: Ensemble) -> DatabaseResult<()> { pub async fn update_ensemble(&self, ensemble: Ensemble) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(UpdateEnsemble(ensemble, sender))?; self.action_sender.send(UpdateEnsemble(ensemble, sender))?;
receiver.await? receiver.await?
} }
/// Get an existing ensemble. /// Get an existing ensemble.
pub async fn get_ensemble(&self, id: &str) -> DatabaseResult<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 self.action_sender
.send(GetEnsemble(id.to_string(), sender))?; .send(GetEnsemble(id.to_string(), sender))?;
@ -251,7 +251,7 @@ impl DbThread {
/// Delete an existing ensemble. This will fail, if there are still other items referencing /// Delete an existing ensemble. This will fail, if there are still other items referencing
/// this ensemble. /// this ensemble.
pub async fn delete_ensemble(&self, id: &str) -> DatabaseResult<()> { pub async fn delete_ensemble(&self, id: &str) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender self.action_sender
.send(DeleteEnsemble(id.to_string(), sender))?; .send(DeleteEnsemble(id.to_string(), sender))?;
@ -259,14 +259,14 @@ impl DbThread {
} }
/// Get all existing ensembles. /// Get all existing ensembles.
pub async fn get_ensembles(&self) -> DatabaseResult<Vec<Ensemble>> { pub async fn get_ensembles(&self) -> Result<Vec<Ensemble>> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetEnsembles(sender))?; self.action_sender.send(GetEnsembles(sender))?;
receiver.await? receiver.await?
} }
/// Update an existing recording or insert a new one. /// Update an existing recording or insert a new one.
pub async fn update_recording(&self, recording: Recording) -> DatabaseResult<()> { pub async fn update_recording(&self, recording: Recording) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender self.action_sender
.send(UpdateRecording(recording, sender))?; .send(UpdateRecording(recording, sender))?;
@ -274,7 +274,7 @@ impl DbThread {
} }
/// Delete an existing recording. /// Delete an existing recording.
pub async fn delete_recording(&self, id: &str) -> DatabaseResult<()> { pub async fn delete_recording(&self, id: &str) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender self.action_sender
.send(DeleteRecording(id.to_string(), sender))?; .send(DeleteRecording(id.to_string(), sender))?;
@ -282,7 +282,7 @@ impl DbThread {
} }
/// Get information on all recordings in which a person performs. /// Get information on all recordings in which a person performs.
pub async fn get_recordings_for_person(&self, person_id: &str) -> DatabaseResult<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.to_string(), sender))?; .send(GetRecordingsForPerson(person_id.to_string(), sender))?;
@ -290,7 +290,7 @@ impl DbThread {
} }
/// Get information on all recordings in which an ensemble performs. /// Get information on all recordings in which an ensemble performs.
pub async fn get_recordings_for_ensemble(&self, ensemble_id: &str) -> DatabaseResult<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.to_string(), sender))?; .send(GetRecordingsForEnsemble(ensemble_id.to_string(), sender))?;
@ -298,7 +298,7 @@ impl DbThread {
} }
/// Get information on all recordings of a work. /// Get information on all recordings of a work.
pub async fn get_recordings_for_work(&self, work_id: &str) -> DatabaseResult<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.to_string(), sender))?; .send(GetRecordingsForWork(work_id.to_string(), sender))?;
@ -306,7 +306,7 @@ impl DbThread {
} }
/// Check whether a recording exists within the database. /// Check whether a recording exists within the database.
pub async fn recording_exists(&self, id: &str) -> DatabaseResult<bool> { pub async fn recording_exists(&self, id: &str) -> Result<bool> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender self.action_sender
.send(RecordingExists(id.to_string(), sender))?; .send(RecordingExists(id.to_string(), sender))?;
@ -314,7 +314,7 @@ impl DbThread {
} }
/// Update an existing medium or insert a new one. /// Update an existing medium or insert a new one.
pub async fn update_medium(&self, medium: Medium) -> DatabaseResult<()> { pub async fn update_medium(&self, medium: Medium) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(UpdateMedium(medium, sender))?; self.action_sender.send(UpdateMedium(medium, sender))?;
receiver.await? receiver.await?
@ -322,7 +322,7 @@ impl DbThread {
/// Delete an existing medium. This will fail, if there are still other /// Delete an existing medium. This will fail, if there are still other
/// items referencing this medium. /// items referencing this medium.
pub async fn delete_medium(&self, id: &str) -> DatabaseResult<()> { pub async fn delete_medium(&self, id: &str) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender self.action_sender
@ -332,21 +332,21 @@ impl DbThread {
} }
/// Get an existing medium. /// Get an existing medium.
pub async fn get_medium(&self, id: &str) -> DatabaseResult<Option<Medium>> { pub async fn get_medium(&self, id: &str) -> Result<Option<Medium>> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetMedium(id.to_owned(), sender))?; self.action_sender.send(GetMedium(id.to_owned(), sender))?;
receiver.await? receiver.await?
} }
/// Get all track sets for a recording. /// Get all track sets for a recording.
pub async fn get_track_sets(&self, recording_id: &str) -> DatabaseResult<Vec<TrackSet>> { pub async fn get_track_sets(&self, recording_id: &str) -> Result<Vec<TrackSet>> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(GetTrackSets(recording_id.to_owned(), sender))?; self.action_sender.send(GetTrackSets(recording_id.to_owned(), sender))?;
receiver.await? receiver.await?
} }
/// Stop the database thread. Any future access to the database will fail. /// Stop the database thread. Any future access to the database will fail.
pub async fn stop(&self) -> DatabaseResult<()> { pub async fn stop(&self) -> Result<()> {
let (sender, receiver) = oneshot::channel(); let (sender, receiver) = oneshot::channel();
self.action_sender.send(Stop(sender))?; self.action_sender.send(Stop(sender))?;
Ok(receiver.await?) Ok(receiver.await?)

View file

@ -1,10 +1,9 @@
use super::generate_id; use super::generate_id;
use super::schema::{instrumentations, work_parts, work_sections, works}; use super::schema::{instrumentations, work_parts, work_sections, works};
use super::{Database, DatabaseError, Instrument, Person, DatabaseResult}; use super::{Database, Error, Instrument, Person, Result};
use diesel::prelude::*; use diesel::prelude::*;
use diesel::{Insertable, Queryable}; use diesel::{Insertable, Queryable};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::convert::TryInto;
/// Table row data for a work. /// Table row data for a work.
#[derive(Insertable, Queryable, Debug, Clone)] #[derive(Insertable, Queryable, Debug, Clone)]
@ -105,10 +104,10 @@ impl Work {
impl Database { impl Database {
/// Update an existing work or insert a new one. /// Update an existing work or insert a new one.
// TODO: Think about also inserting related items. // TODO: Think about also inserting related items.
pub fn update_work(&self, work: Work) -> DatabaseResult<()> { pub fn update_work(&self, work: Work) -> Result<()> {
self.defer_foreign_keys()?; self.defer_foreign_keys()?;
self.connection.transaction::<(), DatabaseError, _>(|| { self.connection.transaction::<(), Error, _>(|| {
let work_id = &work.id; let work_id = &work.id;
self.delete_work(work_id)?; self.delete_work(work_id)?;
@ -194,7 +193,7 @@ impl Database {
} }
/// Get an existing work. /// Get an existing work.
pub fn get_work(&self, id: &str) -> DatabaseResult<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)) .filter(works::id.eq(id))
.load::<WorkRow>(&self.connection)? .load::<WorkRow>(&self.connection)?
@ -210,7 +209,7 @@ impl Database {
} }
/// Retrieve all available information on a work from related tables. /// Retrieve all available information on a work from related tables.
fn get_work_data(&self, row: WorkRow) -> DatabaseResult<Work> { fn get_work_data(&self, row: WorkRow) -> Result<Work> {
let mut instruments: Vec<Instrument> = Vec::new(); let mut instruments: Vec<Instrument> = Vec::new();
let instrumentations = instrumentations::table let instrumentations = instrumentations::table
@ -221,7 +220,7 @@ impl Database {
let id = &instrumentation.instrument; let id = &instrumentation.instrument;
instruments.push( instruments.push(
self.get_instrument(id)? self.get_instrument(id)?
.ok_or(DatabaseError::Other(format!( .ok_or(Error::Other(format!(
"Failed to get instrument ({}) for work ({}).", "Failed to get instrument ({}) for work ({}).",
id, id,
row.id, row.id,
@ -241,7 +240,7 @@ impl Database {
composer: match part_row.composer { composer: match part_row.composer {
Some(composer) => Some( Some(composer) => Some(
self.get_person(&composer)? self.get_person(&composer)?
.ok_or(DatabaseError::Other(format!( .ok_or(Error::Other(format!(
"Failed to get person ({}) for work ({}).", "Failed to get person ({}) for work ({}).",
composer, composer,
row.id, row.id,
@ -268,7 +267,7 @@ impl Database {
let person_id = &row.composer; let person_id = &row.composer;
let person = self let person = self
.get_person(person_id)? .get_person(person_id)?
.ok_or(DatabaseError::Other(format!( .ok_or(Error::Other(format!(
"Failed to get person ({}) for work ({}).", "Failed to get person ({}) for work ({}).",
person_id, person_id,
row.id, row.id,
@ -286,13 +285,13 @@ impl Database {
/// Delete an existing work. This will fail if there are still other tables that relate to /// Delete an existing work. This will fail if there are still other tables that relate to
/// this work except for the things that are part of the information on the work it /// this work except for the things that are part of the information on the work it
pub fn delete_work(&self, id: &str) -> DatabaseResult<()> { pub fn delete_work(&self, id: &str) -> Result<()> {
diesel::delete(works::table.filter(works::id.eq(id))).execute(&self.connection)?; diesel::delete(works::table.filter(works::id.eq(id))).execute(&self.connection)?;
Ok(()) Ok(())
} }
/// Get all existing works by a composer and related information from other tables. /// Get all existing works by a composer and related information from other tables.
pub fn get_works(&self, composer_id: &str) -> DatabaseResult<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

View file

@ -20,6 +20,6 @@ i18n = import('i18n')
subdir('data') subdir('data')
subdir('po') subdir('po')
subdir('res') subdir('res')
subdir('src') subdir('crates/musicus/src')
meson.add_install_script('build-aux/postinstall.py') meson.add_install_script('build-aux/postinstall.py')

View file

@ -1,134 +0,0 @@
prefix = get_option('prefix')
localedir = join_paths(prefix, get_option('localedir'))
global_conf = configuration_data()
global_conf.set_quoted('LOCALEDIR', localedir)
global_conf.set_quoted('VERSION', meson.project_version())
config_rs = configure_file(
input: 'config.rs.in',
output: 'config.rs',
configuration: global_conf
)
run_command(
'cp',
config_rs,
meson.current_source_dir(),
check: true
)
resource_conf = configuration_data()
resource_conf.set_quoted('RESOURCEFILE', resources.full_path())
resource_rs = configure_file(
input: 'resources.rs.in',
output: 'resources.rs',
configuration: resource_conf
)
run_command(
'cp',
resource_rs,
meson.current_source_dir(),
check: true
)
sources = files(
'backend/client/ensembles.rs',
'backend/client/instruments.rs',
'backend/client/mediums.rs',
'backend/client/mod.rs',
'backend/client/persons.rs',
'backend/client/recordings.rs',
'backend/client/register.rs',
'backend/client/works.rs',
'backend/database/ensembles.rs',
'backend/database/error.rs',
'backend/database/instruments.rs',
'backend/database/medium.rs',
'backend/database/mod.rs',
'backend/database/persons.rs',
'backend/database/recordings.rs',
'backend/database/schema.rs',
'backend/database/thread.rs',
'backend/database/works.rs',
'backend/error.rs',
'backend/library.rs',
'backend/mod.rs',
'backend/player.rs',
'backend/secure.rs',
'editors/ensemble.rs',
'editors/instrument.rs',
'editors/mod.rs',
'editors/performance.rs',
'editors/person.rs',
'editors/recording.rs',
'editors/work.rs',
'editors/work_part.rs',
'editors/work_section.rs',
'import/disc_source.rs',
'import/folder_source.rs',
'import/medium_editor.rs',
'import/mod.rs',
'import/source.rs',
'import/source_selector.rs',
'import/track_editor.rs',
'import/track_selector.rs',
'import/track_set_editor.rs',
'navigator/mod.rs',
'navigator/window.rs',
'preferences/login.rs',
'preferences/mod.rs',
'preferences/register.rs',
'preferences/server.rs',
'screens/ensemble.rs',
'screens/mod.rs',
'screens/person.rs',
'screens/player_screen.rs',
'screens/recording.rs',
'screens/work.rs',
'selectors/ensemble.rs',
'selectors/instrument.rs',
'selectors/mod.rs',
'selectors/person.rs',
'selectors/recording.rs',
'selectors/selector.rs',
'selectors/work.rs',
'widgets/button_row.rs',
'widgets/editor.rs',
'widgets/entry_row.rs',
'widgets/upload_section.rs',
'widgets/indexed_list_model.rs',
'widgets/list.rs',
'widgets/mod.rs',
'widgets/player_bar.rs',
'widgets/poe_list.rs',
'widgets/screen.rs',
'widgets/section.rs',
'config.rs',
'config.rs.in',
'macros.rs',
'main.rs',
'resources.rs',
'resources.rs.in',
'window.rs',
)
cargo_script = find_program(join_paths(meson.source_root(), 'build-aux/cargo.sh'))
cargo_release = custom_target(
'cargo-build',
build_by_default: true,
input: sources,
depends: resources,
output: meson.project_name(),
console: true,
install: true,
install_dir: get_option('bindir'),
command: [
cargo_script,
meson.build_root(),
meson.source_root(),
'@OUTPUT@',
get_option('buildtype'),
meson.project_name(),
]
)