Integrate changes with binary
This commit is contained in:
parent
6a90b6cf78
commit
e2dd53845f
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
/codecov
|
/codecov
|
||||||
|
database.json
|
||||||
|
@ -6,13 +6,19 @@ use serde::Serialize;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use mockall::automock;
|
use mockall::automock;
|
||||||
|
|
||||||
use super::{Error, IDatabase};
|
use super::{IDatabase, ReadError, WriteError};
|
||||||
|
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
|
|
||||||
impl From<serde_json::Error> for Error {
|
impl From<serde_json::Error> for ReadError {
|
||||||
fn from(err: serde_json::Error) -> Error {
|
fn from(err: serde_json::Error) -> ReadError {
|
||||||
Error::SerDeError(err.to_string())
|
ReadError::SerDeError(err.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<serde_json::Error> for WriteError {
|
||||||
|
fn from(err: serde_json::Error) -> WriteError {
|
||||||
|
WriteError::SerDeError(err.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,13 +46,13 @@ impl<JDB: IJsonDatabaseBackend> JsonDatabase<JDB> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<JDB: IJsonDatabaseBackend> IDatabase for JsonDatabase<JDB> {
|
impl<JDB: IJsonDatabaseBackend> IDatabase for JsonDatabase<JDB> {
|
||||||
fn read<D: DeserializeOwned>(&self, collection: &mut D) -> Result<(), Error> {
|
fn read<D: DeserializeOwned>(&self, collection: &mut D) -> Result<(), ReadError> {
|
||||||
let serialized = self.backend.read()?;
|
let serialized = self.backend.read()?;
|
||||||
*collection = serde_json::from_str(&serialized)?;
|
*collection = serde_json::from_str(&serialized)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<S: Serialize>(&mut self, collection: &S) -> Result<(), Error> {
|
fn write<S: Serialize>(&mut self, collection: &S) -> Result<(), WriteError> {
|
||||||
let serialized = serde_json::to_string(&collection)?;
|
let serialized = serde_json::to_string(&collection)?;
|
||||||
self.backend.write(&serialized)?;
|
self.backend.write(&serialized)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -191,7 +197,7 @@ mod tests {
|
|||||||
let serde_err = serde_json::to_string(&object);
|
let serde_err = serde_json::to_string(&object);
|
||||||
assert!(serde_err.is_err());
|
assert!(serde_err.is_err());
|
||||||
|
|
||||||
let serde_err: Error = serde_err.unwrap_err().into();
|
let serde_err: WriteError = serde_err.unwrap_err().into();
|
||||||
assert!(!serde_err.to_string().is_empty());
|
assert!(!serde_err.to_string().is_empty());
|
||||||
assert!(!format!("{:?}", serde_err).is_empty());
|
assert!(!format!("{:?}", serde_err).is_empty());
|
||||||
}
|
}
|
||||||
|
@ -14,35 +14,61 @@ pub mod json;
|
|||||||
#[cfg_attr(test, automock)]
|
#[cfg_attr(test, automock)]
|
||||||
pub trait IDatabase {
|
pub trait IDatabase {
|
||||||
/// Read collection from the database.
|
/// Read collection from the database.
|
||||||
fn read<D: DeserializeOwned + 'static>(&self, collection: &mut D) -> Result<(), Error>;
|
fn read<D: DeserializeOwned + 'static>(&self, collection: &mut D) -> Result<(), ReadError>;
|
||||||
|
|
||||||
/// Write collection to the database.
|
/// Write collection to the database.
|
||||||
fn write<S: Serialize + 'static>(&mut self, collection: &S) -> Result<(), Error>;
|
fn write<S: Serialize + 'static>(&mut self, collection: &S) -> Result<(), WriteError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error type for database calls.
|
/// Error type for database calls.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum ReadError {
|
||||||
/// The database experienced an I/O error.
|
/// The database experienced an I/O read error.
|
||||||
IoError(String),
|
IoError(String),
|
||||||
/// The database experienced a (de)serialisation error.
|
/// The database experienced a deserialisation error.
|
||||||
SerDeError(String),
|
SerDeError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for ReadError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Self::IoError(ref s) => write!(f, "the database experienced an I/O error: {s}"),
|
Self::IoError(ref s) => write!(f, "the database experienced an I/O read error: {s}"),
|
||||||
Self::SerDeError(ref s) => {
|
Self::SerDeError(ref s) => {
|
||||||
write!(f, "the database experienced a (de)serialisation error: {s}")
|
write!(f, "the database experienced a deserialisation error: {s}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::io::Error> for Error {
|
impl From<std::io::Error> for ReadError {
|
||||||
fn from(err: std::io::Error) -> Error {
|
fn from(err: std::io::Error) -> ReadError {
|
||||||
Error::IoError(err.to_string())
|
ReadError::IoError(err.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error type for database calls.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum WriteError {
|
||||||
|
/// The database experienced an I/O write error.
|
||||||
|
IoError(String),
|
||||||
|
/// The database experienced a serialisation error.
|
||||||
|
SerDeError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for WriteError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Self::IoError(ref s) => write!(f, "the database experienced an I/O write error: {s}"),
|
||||||
|
Self::SerDeError(ref s) => {
|
||||||
|
write!(f, "the database experienced a serialisation error: {s}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for WriteError {
|
||||||
|
fn from(err: std::io::Error) -> WriteError {
|
||||||
|
WriteError::IoError(err.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,11 +76,15 @@ impl From<std::io::Error> for Error {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use super::Error;
|
use super::{ReadError, WriteError};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn errors() {
|
fn errors() {
|
||||||
let io_err: Error = io::Error::new(io::ErrorKind::Interrupted, "error").into();
|
let io_err: ReadError = io::Error::new(io::ErrorKind::Interrupted, "error").into();
|
||||||
|
assert!(!io_err.to_string().is_empty());
|
||||||
|
assert!(!format!("{:?}", io_err).is_empty());
|
||||||
|
|
||||||
|
let io_err: WriteError = io::Error::new(io::ErrorKind::Interrupted, "error").into();
|
||||||
assert!(!io_err.to_string().is_empty());
|
assert!(!io_err.to_string().is_empty());
|
||||||
assert!(!format!("{:?}", io_err).is_empty());
|
assert!(!format!("{:?}", io_err).is_empty());
|
||||||
}
|
}
|
||||||
|
20
src/lib.rs
20
src/lib.rs
@ -7,7 +7,8 @@ use std::{
|
|||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
fmt,
|
fmt,
|
||||||
iter::Peekable, mem,
|
iter::Peekable,
|
||||||
|
mem,
|
||||||
};
|
};
|
||||||
|
|
||||||
use database::IDatabase;
|
use database::IDatabase;
|
||||||
@ -212,8 +213,14 @@ impl From<library::Error> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<database::Error> for Error {
|
impl From<database::ReadError> for Error {
|
||||||
fn from(err: database::Error) -> Error {
|
fn from(err: database::ReadError) -> Error {
|
||||||
|
Error::DatabaseError(err.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<database::WriteError> for Error {
|
||||||
|
fn from(err: database::WriteError) -> Error {
|
||||||
Error::DatabaseError(err.to_string())
|
Error::DatabaseError(err.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -521,7 +528,7 @@ mod tests {
|
|||||||
let library = MockILibrary::new();
|
let library = MockILibrary::new();
|
||||||
let mut database = MockIDatabase::new();
|
let mut database = MockIDatabase::new();
|
||||||
|
|
||||||
let database_result = Err(database::Error::IoError(String::from("I/O error")));
|
let database_result = Err(database::WriteError::IoError(String::from("I/O error")));
|
||||||
|
|
||||||
database
|
database
|
||||||
.expect_write()
|
.expect_write()
|
||||||
@ -531,8 +538,9 @@ mod tests {
|
|||||||
let mut music_hoard = MusicHoard::new(library, database);
|
let mut music_hoard = MusicHoard::new(library, database);
|
||||||
|
|
||||||
let actual_err = music_hoard.save_to_database().unwrap_err();
|
let actual_err = music_hoard.save_to_database().unwrap_err();
|
||||||
let expected_err =
|
let expected_err = Error::DatabaseError(
|
||||||
Error::DatabaseError(database::Error::IoError(String::from("I/O error")).to_string());
|
database::WriteError::IoError(String::from("I/O error")).to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(actual_err, expected_err);
|
assert_eq!(actual_err, expected_err);
|
||||||
assert_eq!(actual_err.to_string(), expected_err.to_string());
|
assert_eq!(actual_err.to_string(), expected_err.to_string());
|
||||||
|
25
src/main.rs
25
src/main.rs
@ -1,6 +1,8 @@
|
|||||||
|
use std::fs::OpenOptions;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{ffi::OsString, io};
|
use std::{ffi::OsString, io};
|
||||||
|
|
||||||
|
use musichoard::Collection;
|
||||||
use ratatui::{backend::CrosstermBackend, Terminal};
|
use ratatui::{backend::CrosstermBackend, Terminal};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
@ -57,9 +59,28 @@ fn with<LIB: ILibrary, DB: IDatabase>(lib: LIB, db: DB) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Create the application.
|
|
||||||
let opt = Opt::from_args();
|
let opt = Opt::from_args();
|
||||||
|
|
||||||
|
// Create an empty database file if it does not exist.
|
||||||
|
match OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.open(&opt.database_file_path)
|
||||||
|
{
|
||||||
|
Ok(f) => {
|
||||||
|
drop(f);
|
||||||
|
JsonDatabase::new(JsonDatabaseFileBackend::new(&opt.database_file_path))
|
||||||
|
.write::<Collection>(&vec![])
|
||||||
|
.expect("failed to create empty database");
|
||||||
|
}
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
io::ErrorKind::AlreadyExists => {}
|
||||||
|
_ => panic!("failed to access database file"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the application.
|
||||||
|
let db_exec = JsonDatabaseFileBackend::new(&opt.database_file_path);
|
||||||
if let Some(uri) = opt.beets_ssh_uri {
|
if let Some(uri) = opt.beets_ssh_uri {
|
||||||
let uri = uri.into_string().expect("invalid SSH URI");
|
let uri = uri.into_string().expect("invalid SSH URI");
|
||||||
let beets_config_file_path = opt
|
let beets_config_file_path = opt
|
||||||
@ -70,11 +91,9 @@ fn main() {
|
|||||||
let lib_exec = BeetsLibrarySshExecutor::new(uri)
|
let lib_exec = BeetsLibrarySshExecutor::new(uri)
|
||||||
.expect("failed to initialise beets")
|
.expect("failed to initialise beets")
|
||||||
.config(beets_config_file_path);
|
.config(beets_config_file_path);
|
||||||
let db_exec = JsonDatabaseFileBackend::new(&opt.database_file_path);
|
|
||||||
with(BeetsLibrary::new(lib_exec), JsonDatabase::new(db_exec));
|
with(BeetsLibrary::new(lib_exec), JsonDatabase::new(db_exec));
|
||||||
} else {
|
} else {
|
||||||
let lib_exec = BeetsLibraryProcessExecutor::default().config(opt.beets_config_file_path);
|
let lib_exec = BeetsLibraryProcessExecutor::default().config(opt.beets_config_file_path);
|
||||||
let db_exec = JsonDatabaseFileBackend::new(&opt.database_file_path);
|
|
||||||
with(BeetsLibrary::new(lib_exec), JsonDatabase::new(db_exec));
|
with(BeetsLibrary::new(lib_exec), JsonDatabase::new(db_exec));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,14 @@ use crossterm::event::{KeyEvent, MouseEvent};
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
use super::ui::UiError;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum EventError {
|
pub enum EventError {
|
||||||
Send(Event),
|
Send(Event),
|
||||||
Recv,
|
Recv,
|
||||||
Io(std::io::Error),
|
Io(std::io::Error),
|
||||||
|
Ui(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for EventError {
|
impl fmt::Display for EventError {
|
||||||
@ -17,6 +20,9 @@ impl fmt::Display for EventError {
|
|||||||
Self::Io(ref e) => {
|
Self::Io(ref e) => {
|
||||||
write!(f, "an I/O error was triggered during event handling: {e}")
|
write!(f, "an I/O error was triggered during event handling: {e}")
|
||||||
}
|
}
|
||||||
|
Self::Ui(ref s) => {
|
||||||
|
write!(f, "the UI returned an error during event handling: {s}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,6 +39,12 @@ impl From<mpsc::RecvError> for EventError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<UiError> for EventError {
|
||||||
|
fn from(err: UiError) -> EventError {
|
||||||
|
EventError::Ui(err.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Key(KeyEvent),
|
Key(KeyEvent),
|
||||||
|
@ -14,7 +14,8 @@ pub trait IEventHandler<UI> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trait IEventHandlerPrivate<UI> {
|
trait IEventHandlerPrivate<UI> {
|
||||||
fn handle_key_event(ui: &mut UI, key_event: KeyEvent);
|
fn handle_key_event(ui: &mut UI, key_event: KeyEvent) -> Result<(), EventError>;
|
||||||
|
fn quit(ui: &mut UI) -> Result<(), EventError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventHandler {
|
pub struct EventHandler {
|
||||||
@ -31,7 +32,7 @@ impl EventHandler {
|
|||||||
impl<UI: IUi> IEventHandler<UI> for EventHandler {
|
impl<UI: IUi> IEventHandler<UI> for EventHandler {
|
||||||
fn handle_next_event(&self, ui: &mut UI) -> Result<(), EventError> {
|
fn handle_next_event(&self, ui: &mut UI) -> Result<(), EventError> {
|
||||||
match self.events.recv()? {
|
match self.events.recv()? {
|
||||||
Event::Key(key_event) => Self::handle_key_event(ui, key_event),
|
Event::Key(key_event) => Self::handle_key_event(ui, key_event)?,
|
||||||
Event::Mouse(_) => {}
|
Event::Mouse(_) => {}
|
||||||
Event::Resize(_, _) => {}
|
Event::Resize(_, _) => {}
|
||||||
};
|
};
|
||||||
@ -40,16 +41,16 @@ impl<UI: IUi> IEventHandler<UI> for EventHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<UI: IUi> IEventHandlerPrivate<UI> for EventHandler {
|
impl<UI: IUi> IEventHandlerPrivate<UI> for EventHandler {
|
||||||
fn handle_key_event(ui: &mut UI, key_event: KeyEvent) {
|
fn handle_key_event(ui: &mut UI, key_event: KeyEvent) -> Result<(), EventError> {
|
||||||
match key_event.code {
|
match key_event.code {
|
||||||
// Exit application on `ESC` or `q`.
|
// Exit application on `ESC` or `q`.
|
||||||
KeyCode::Esc | KeyCode::Char('q') => {
|
KeyCode::Esc | KeyCode::Char('q') => {
|
||||||
ui.quit();
|
Self::quit(ui)?;
|
||||||
}
|
}
|
||||||
// Exit application on `Ctrl-C`.
|
// Exit application on `Ctrl-C`.
|
||||||
KeyCode::Char('c') | KeyCode::Char('C') => {
|
KeyCode::Char('c') | KeyCode::Char('C') => {
|
||||||
if key_event.modifiers == KeyModifiers::CONTROL {
|
if key_event.modifiers == KeyModifiers::CONTROL {
|
||||||
ui.quit();
|
Self::quit(ui)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Category change.
|
// Category change.
|
||||||
@ -69,6 +70,14 @@ impl<UI: IUi> IEventHandlerPrivate<UI> for EventHandler {
|
|||||||
// Other keys.
|
// Other keys.
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quit(ui: &mut UI) -> Result<(), EventError> {
|
||||||
|
ui.quit();
|
||||||
|
ui.save()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// GRCOV_EXCL_STOP
|
// GRCOV_EXCL_STOP
|
||||||
|
@ -3,24 +3,26 @@ use musichoard::{database::IDatabase, library::ILibrary, Collection, MusicHoard}
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use mockall::automock;
|
use mockall::automock;
|
||||||
|
|
||||||
use super::Error;
|
|
||||||
|
|
||||||
#[cfg_attr(test, automock)]
|
#[cfg_attr(test, automock)]
|
||||||
pub trait IMusicHoard {
|
pub trait IMusicHoard {
|
||||||
fn rescan_library(&mut self) -> Result<(), Error>;
|
fn rescan_library(&mut self) -> Result<(), musichoard::Error>;
|
||||||
|
fn load_from_database(&mut self) -> Result<(), musichoard::Error>;
|
||||||
|
fn save_to_database(&mut self) -> Result<(), musichoard::Error>;
|
||||||
fn get_collection(&self) -> &Collection;
|
fn get_collection(&self) -> &Collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<musichoard::Error> for Error {
|
|
||||||
fn from(err: musichoard::Error) -> Error {
|
|
||||||
Error::Lib(err.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GRCOV_EXCL_START
|
// GRCOV_EXCL_START
|
||||||
impl<LIB: ILibrary, DB: IDatabase> IMusicHoard for MusicHoard<LIB, DB> {
|
impl<LIB: ILibrary, DB: IDatabase> IMusicHoard for MusicHoard<LIB, DB> {
|
||||||
fn rescan_library(&mut self) -> Result<(), Error> {
|
fn rescan_library(&mut self) -> Result<(), musichoard::Error> {
|
||||||
Ok(MusicHoard::rescan_library(self)?)
|
MusicHoard::rescan_library(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_from_database(&mut self) -> Result<(), musichoard::Error> {
|
||||||
|
MusicHoard::load_from_database(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_to_database(&mut self) -> Result<(), musichoard::Error> {
|
||||||
|
MusicHoard::save_to_database(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_collection(&self) -> &Collection {
|
fn get_collection(&self) -> &Collection {
|
||||||
|
@ -25,6 +25,12 @@ pub enum Error {
|
|||||||
ListenerPanic,
|
ListenerPanic,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<musichoard::Error> for Error {
|
||||||
|
fn from(err: musichoard::Error) -> Error {
|
||||||
|
Error::Lib(err.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
impl From<io::Error> for Error {
|
||||||
fn from(err: io::Error) -> Error {
|
fn from(err: io::Error) -> Error {
|
||||||
Error::Io(err.to_string())
|
Error::Io(err.to_string())
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
use musichoard::{Album, Artist, Collection, Format, Track};
|
use musichoard::{Album, Artist, Collection, Format, Track};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
backend::Backend,
|
backend::Backend,
|
||||||
@ -9,10 +11,31 @@ use ratatui::{
|
|||||||
|
|
||||||
use super::{lib::IMusicHoard, Error};
|
use super::{lib::IMusicHoard, Error};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum UiError {
|
||||||
|
Lib(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for UiError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Self::Lib(ref s) => write!(f, "the musichoard library returned an error: {s}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<musichoard::Error> for UiError {
|
||||||
|
fn from(err: musichoard::Error) -> UiError {
|
||||||
|
UiError::Lib(err.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait IUi {
|
pub trait IUi {
|
||||||
fn is_running(&self) -> bool;
|
fn is_running(&self) -> bool;
|
||||||
fn quit(&mut self);
|
fn quit(&mut self);
|
||||||
|
|
||||||
|
fn save(&mut self) -> Result<(), UiError>;
|
||||||
|
|
||||||
fn increment_category(&mut self);
|
fn increment_category(&mut self);
|
||||||
fn decrement_category(&mut self);
|
fn decrement_category(&mut self);
|
||||||
|
|
||||||
@ -440,6 +463,7 @@ impl<'a, 'b> TrackState<'a, 'b> {
|
|||||||
|
|
||||||
impl<MH: IMusicHoard> Ui<MH> {
|
impl<MH: IMusicHoard> Ui<MH> {
|
||||||
pub fn new(mut music_hoard: MH) -> Result<Self, Error> {
|
pub fn new(mut music_hoard: MH) -> Result<Self, Error> {
|
||||||
|
music_hoard.load_from_database()?;
|
||||||
music_hoard.rescan_library()?;
|
music_hoard.rescan_library()?;
|
||||||
let selection = Selection::new(Some(music_hoard.get_collection()));
|
let selection = Selection::new(Some(music_hoard.get_collection()));
|
||||||
Ok(Ui {
|
Ok(Ui {
|
||||||
@ -531,6 +555,11 @@ impl<MH: IMusicHoard> IUi for Ui<MH> {
|
|||||||
self.running = false;
|
self.running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn save(&mut self) -> Result<(), UiError> {
|
||||||
|
self.music_hoard.save_to_database()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn increment_category(&mut self) {
|
fn increment_category(&mut self) {
|
||||||
self.selection.increment_category();
|
self.selection.increment_category();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user