Ensure consistency between in-memory and database state #146
@ -77,10 +77,6 @@ struct ArtistSortValue {
|
|||||||
|
|
||||||
#[derive(StructOpt, Debug)]
|
#[derive(StructOpt, Debug)]
|
||||||
enum MusicBrainzCommand {
|
enum MusicBrainzCommand {
|
||||||
#[structopt(about = "Add a MusicBrainz URL without overwriting the existing value")]
|
|
||||||
Add(MusicBrainzValue),
|
|
||||||
#[structopt(about = "Remove the MusicBrainz URL")]
|
|
||||||
Remove(MusicBrainzValue),
|
|
||||||
#[structopt(about = "Set the MusicBrainz URL overwriting any existing value")]
|
#[structopt(about = "Set the MusicBrainz URL overwriting any existing value")]
|
||||||
Set(MusicBrainzValue),
|
Set(MusicBrainzValue),
|
||||||
#[structopt(about = "Clear the MusicBrainz URL)")]
|
#[structopt(about = "Clear the MusicBrainz URL)")]
|
||||||
@ -129,10 +125,14 @@ impl ArtistCommand {
|
|||||||
fn handle(self, music_hoard: &mut MH) {
|
fn handle(self, music_hoard: &mut MH) {
|
||||||
match self {
|
match self {
|
||||||
ArtistCommand::Add(artist_value) => {
|
ArtistCommand::Add(artist_value) => {
|
||||||
music_hoard.add_artist(ArtistId::new(artist_value.artist));
|
music_hoard
|
||||||
|
.add_artist(ArtistId::new(artist_value.artist))
|
||||||
|
.expect("failed to add artist");
|
||||||
}
|
}
|
||||||
ArtistCommand::Remove(artist_value) => {
|
ArtistCommand::Remove(artist_value) => {
|
||||||
music_hoard.remove_artist(ArtistId::new(artist_value.artist));
|
music_hoard
|
||||||
|
.remove_artist(ArtistId::new(artist_value.artist))
|
||||||
|
.expect("failed to remove artist");
|
||||||
}
|
}
|
||||||
ArtistCommand::Sort(sort_command) => {
|
ArtistCommand::Sort(sort_command) => {
|
||||||
sort_command.handle(music_hoard);
|
sort_command.handle(music_hoard);
|
||||||
@ -166,18 +166,6 @@ impl SortCommand {
|
|||||||
impl MusicBrainzCommand {
|
impl MusicBrainzCommand {
|
||||||
fn handle(self, music_hoard: &mut MH) {
|
fn handle(self, music_hoard: &mut MH) {
|
||||||
match self {
|
match self {
|
||||||
MusicBrainzCommand::Add(musicbrainz_value) => music_hoard
|
|
||||||
.add_musicbrainz_url(
|
|
||||||
ArtistId::new(musicbrainz_value.artist),
|
|
||||||
musicbrainz_value.url,
|
|
||||||
)
|
|
||||||
.expect("failed to add MusicBrainz URL"),
|
|
||||||
MusicBrainzCommand::Remove(musicbrainz_value) => music_hoard
|
|
||||||
.remove_musicbrainz_url(
|
|
||||||
ArtistId::new(musicbrainz_value.artist),
|
|
||||||
musicbrainz_value.url,
|
|
||||||
)
|
|
||||||
.expect("failed to remove MusicBrainz URL"),
|
|
||||||
MusicBrainzCommand::Set(musicbrainz_value) => music_hoard
|
MusicBrainzCommand::Set(musicbrainz_value) => music_hoard
|
||||||
.set_musicbrainz_url(
|
.set_musicbrainz_url(
|
||||||
ArtistId::new(musicbrainz_value.artist),
|
ArtistId::new(musicbrainz_value.artist),
|
||||||
@ -227,14 +215,9 @@ fn main() {
|
|||||||
|
|
||||||
let db = JsonDatabase::new(JsonDatabaseFileBackend::new(&opt.database_file_path));
|
let db = JsonDatabase::new(JsonDatabaseFileBackend::new(&opt.database_file_path));
|
||||||
|
|
||||||
let mut music_hoard = MusicHoardBuilder::default().set_database(db).build();
|
let mut music_hoard = MusicHoardBuilder::default()
|
||||||
music_hoard
|
.set_database(db)
|
||||||
.load_from_database()
|
.build()
|
||||||
.expect("failed to load database");
|
.expect("failed to initialise MusicHoard");
|
||||||
|
|
||||||
opt.category.handle(&mut music_hoard);
|
opt.category.handle(&mut music_hoard);
|
||||||
|
|
||||||
music_hoard
|
|
||||||
.save_to_database()
|
|
||||||
.expect("failed to save database");
|
|
||||||
}
|
}
|
||||||
|
@ -208,14 +208,14 @@ impl<DB: IDatabase> MusicHoard<NoLibrary, DB> {
|
|||||||
library_cache: HashMap::new(),
|
library_cache: HashMap::new(),
|
||||||
pre_commit: vec![],
|
pre_commit: vec![],
|
||||||
};
|
};
|
||||||
mh.load_from_database()?;
|
mh.reload_database()?;
|
||||||
Ok(mh)
|
Ok(mh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<LIB, DB: IDatabase> MusicHoard<LIB, DB> {
|
impl<LIB, DB: IDatabase> MusicHoard<LIB, DB> {
|
||||||
/// Load the database and merge with the in-memory collection.
|
/// Load the database and merge with the in-memory collection.
|
||||||
pub fn load_from_database(&mut self) -> Result<(), Error> {
|
pub fn reload_database(&mut self) -> Result<(), Error> {
|
||||||
self.collection = self.database.load()?;
|
self.collection = self.database.load()?;
|
||||||
Self::sort_albums_and_tracks(self.collection.iter_mut());
|
Self::sort_albums_and_tracks(self.collection.iter_mut());
|
||||||
|
|
||||||
@ -370,7 +370,7 @@ impl<LIB: ILibrary, DB: IDatabase> MusicHoard<LIB, DB> {
|
|||||||
library_cache: HashMap::new(),
|
library_cache: HashMap::new(),
|
||||||
pre_commit: vec![],
|
pre_commit: vec![],
|
||||||
};
|
};
|
||||||
mh.load_from_database()?;
|
mh.reload_database()?;
|
||||||
Ok(mh)
|
Ok(mh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ mod tests {
|
|||||||
.set_database(NullDatabase)
|
.set_database(NullDatabase)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(mh.load_from_database().is_ok());
|
assert!(mh.reload_database().is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -113,6 +113,6 @@ mod tests {
|
|||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(mh.rescan_library().is_ok());
|
assert!(mh.rescan_library().is_ok());
|
||||||
assert!(mh.load_from_database().is_ok());
|
assert!(mh.reload_database().is_ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ struct DbOpt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn with<LIB: ILibrary, DB: IDatabase>(builder: MusicHoardBuilder<LIB, DB>) {
|
fn with<LIB: ILibrary, DB: IDatabase>(builder: MusicHoardBuilder<LIB, DB>) {
|
||||||
let music_hoard = builder.build();
|
let music_hoard = builder.build().expect("failed to initialise MusicHoard");
|
||||||
|
|
||||||
// Initialize the terminal user interface.
|
// Initialize the terminal user interface.
|
||||||
let backend = CrosstermBackend::new(io::stdout());
|
let backend = CrosstermBackend::new(io::stdout());
|
||||||
|
@ -36,14 +36,9 @@ impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppBrowse>> for AppPublic<
|
|||||||
impl<MH: IMusicHoard> IAppInteractBrowse for AppMachine<MH, AppBrowse> {
|
impl<MH: IMusicHoard> IAppInteractBrowse for AppMachine<MH, AppBrowse> {
|
||||||
type APP = App<MH>;
|
type APP = App<MH>;
|
||||||
|
|
||||||
fn save_and_quit(mut self) -> Self::APP {
|
fn quit(mut self) -> Self::APP {
|
||||||
match self.inner.music_hoard.save_to_database() {
|
self.inner.running = false;
|
||||||
Ok(_) => {
|
self.into()
|
||||||
self.inner.running = false;
|
|
||||||
self.into()
|
|
||||||
}
|
|
||||||
Err(err) => AppMachine::error(self.inner, err.to_string()).into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increment_category(mut self) -> Self::APP {
|
fn increment_category(mut self) -> Self::APP {
|
||||||
@ -104,37 +99,16 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn save_and_quit() {
|
fn quit() {
|
||||||
let mut music_hoard = music_hoard(vec![]);
|
let music_hoard = music_hoard(vec![]);
|
||||||
|
|
||||||
music_hoard
|
|
||||||
.expect_save_to_database()
|
|
||||||
.times(1)
|
|
||||||
.return_once(|| Ok(()));
|
|
||||||
|
|
||||||
let browse = AppMachine::browse(inner(music_hoard));
|
let browse = AppMachine::browse(inner(music_hoard));
|
||||||
|
|
||||||
let app = browse.save_and_quit();
|
let app = browse.quit();
|
||||||
assert!(!app.is_running());
|
assert!(!app.is_running());
|
||||||
app.unwrap_browse();
|
app.unwrap_browse();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn save_and_quit_error() {
|
|
||||||
let mut music_hoard = music_hoard(vec![]);
|
|
||||||
|
|
||||||
music_hoard
|
|
||||||
.expect_save_to_database()
|
|
||||||
.times(1)
|
|
||||||
.return_once(|| Err(musichoard::Error::DatabaseError(String::from("get rekt"))));
|
|
||||||
|
|
||||||
let browse = AppMachine::browse(inner(music_hoard));
|
|
||||||
|
|
||||||
let app = browse.save_and_quit();
|
|
||||||
assert!(app.is_running());
|
|
||||||
app.unwrap_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn increment_decrement() {
|
fn increment_decrement() {
|
||||||
let mut browse = AppMachine::browse(inner(music_hoard(COLLECTION.to_owned())));
|
let mut browse = AppMachine::browse(inner(music_hoard(COLLECTION.to_owned())));
|
||||||
|
@ -48,7 +48,6 @@ impl<MH: IMusicHoard> App<MH> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn init(music_hoard: &mut MH) -> Result<(), musichoard::Error> {
|
fn init(music_hoard: &mut MH) -> Result<(), musichoard::Error> {
|
||||||
music_hoard.load_from_database()?;
|
|
||||||
music_hoard.rescan_library()?;
|
music_hoard.rescan_library()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -196,10 +195,6 @@ mod tests {
|
|||||||
fn music_hoard_init(collection: Collection) -> MockIMusicHoard {
|
fn music_hoard_init(collection: Collection) -> MockIMusicHoard {
|
||||||
let mut music_hoard = music_hoard(collection);
|
let mut music_hoard = music_hoard(collection);
|
||||||
|
|
||||||
music_hoard
|
|
||||||
.expect_load_from_database()
|
|
||||||
.times(1)
|
|
||||||
.return_once(|| Ok(()));
|
|
||||||
music_hoard
|
music_hoard
|
||||||
.expect_rescan_library()
|
.expect_rescan_library()
|
||||||
.times(1)
|
.times(1)
|
||||||
@ -323,9 +318,9 @@ mod tests {
|
|||||||
let mut music_hoard = MockIMusicHoard::new();
|
let mut music_hoard = MockIMusicHoard::new();
|
||||||
|
|
||||||
music_hoard
|
music_hoard
|
||||||
.expect_load_from_database()
|
.expect_rescan_library()
|
||||||
.times(1)
|
.times(1)
|
||||||
.return_once(|| Err(musichoard::Error::DatabaseError(String::from("get rekt"))));
|
.return_once(|| Err(musichoard::Error::LibraryError(String::from("get rekt"))));
|
||||||
music_hoard.expect_get_collection().return_const(vec![]);
|
music_hoard.expect_get_collection().return_const(vec![]);
|
||||||
|
|
||||||
let app = App::new(music_hoard);
|
let app = App::new(music_hoard);
|
||||||
|
@ -49,7 +49,7 @@ impl<MH: IMusicHoard> IAppInteractReload for AppMachine<MH, AppReload> {
|
|||||||
self.inner.music_hoard.get_collection(),
|
self.inner.music_hoard.get_collection(),
|
||||||
&self.inner.selection,
|
&self.inner.selection,
|
||||||
);
|
);
|
||||||
let result = self.inner.music_hoard.load_from_database();
|
let result = self.inner.music_hoard.reload_database();
|
||||||
self.refresh(previous, result)
|
self.refresh(previous, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ mod tests {
|
|||||||
let mut music_hoard = music_hoard(vec![]);
|
let mut music_hoard = music_hoard(vec![]);
|
||||||
|
|
||||||
music_hoard
|
music_hoard
|
||||||
.expect_load_from_database()
|
.expect_reload_database()
|
||||||
.times(1)
|
.times(1)
|
||||||
.return_once(|| Ok(()));
|
.return_once(|| Ok(()));
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ mod tests {
|
|||||||
let mut music_hoard = music_hoard(vec![]);
|
let mut music_hoard = music_hoard(vec![]);
|
||||||
|
|
||||||
music_hoard
|
music_hoard
|
||||||
.expect_load_from_database()
|
.expect_reload_database()
|
||||||
.times(1)
|
.times(1)
|
||||||
.return_once(|| Err(musichoard::Error::DatabaseError(String::from("get rekt"))));
|
.return_once(|| Err(musichoard::Error::DatabaseError(String::from("get rekt"))));
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ pub trait IAppInteract {
|
|||||||
pub trait IAppInteractBrowse {
|
pub trait IAppInteractBrowse {
|
||||||
type APP: IAppInteract;
|
type APP: IAppInteract;
|
||||||
|
|
||||||
fn save_and_quit(self) -> Self::APP;
|
fn quit(self) -> Self::APP;
|
||||||
|
|
||||||
fn increment_category(self) -> Self::APP;
|
fn increment_category(self) -> Self::APP;
|
||||||
fn decrement_category(self) -> Self::APP;
|
fn decrement_category(self) -> Self::APP;
|
||||||
|
@ -83,7 +83,7 @@ impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
|||||||
fn handle_browse_key_event(app: <APP as IAppInteract>::BS, key_event: KeyEvent) -> APP {
|
fn handle_browse_key_event(app: <APP as IAppInteract>::BS, key_event: KeyEvent) -> APP {
|
||||||
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::Char('Q') => app.save_and_quit(),
|
KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => app.quit(),
|
||||||
// Category change.
|
// Category change.
|
||||||
KeyCode::Left => app.decrement_category(),
|
KeyCode::Left => app.decrement_category(),
|
||||||
KeyCode::Right => app.increment_category(),
|
KeyCode::Right => app.increment_category(),
|
||||||
|
@ -6,8 +6,7 @@ use mockall::automock;
|
|||||||
#[cfg_attr(test, automock)]
|
#[cfg_attr(test, automock)]
|
||||||
pub trait IMusicHoard {
|
pub trait IMusicHoard {
|
||||||
fn rescan_library(&mut self) -> Result<(), musichoard::Error>;
|
fn rescan_library(&mut self) -> Result<(), musichoard::Error>;
|
||||||
fn load_from_database(&mut self) -> Result<(), musichoard::Error>;
|
fn reload_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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,12 +16,8 @@ impl<LIB: ILibrary, DB: IDatabase> IMusicHoard for MusicHoard<LIB, DB> {
|
|||||||
MusicHoard::rescan_library(self)
|
MusicHoard::rescan_library(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_from_database(&mut self) -> Result<(), musichoard::Error> {
|
fn reload_database(&mut self) -> Result<(), musichoard::Error> {
|
||||||
MusicHoard::load_from_database(self)
|
MusicHoard::reload_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 {
|
||||||
|
@ -193,7 +193,7 @@ mod tests {
|
|||||||
fn music_hoard(collection: Collection) -> MockIMusicHoard {
|
fn music_hoard(collection: Collection) -> MockIMusicHoard {
|
||||||
let mut music_hoard = MockIMusicHoard::new();
|
let mut music_hoard = MockIMusicHoard::new();
|
||||||
|
|
||||||
music_hoard.expect_load_from_database().returning(|| Ok(()));
|
music_hoard.expect_reload_database().returning(|| Ok(()));
|
||||||
music_hoard.expect_rescan_library().returning(|| Ok(()));
|
music_hoard.expect_rescan_library().returning(|| Ok(()));
|
||||||
music_hoard.expect_get_collection().return_const(collection);
|
music_hoard.expect_get_collection().return_const(collection);
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ fn merge_library_then_database() {
|
|||||||
let mut music_hoard = MusicHoard::new(library, database).unwrap();
|
let mut music_hoard = MusicHoard::new(library, database).unwrap();
|
||||||
|
|
||||||
music_hoard.rescan_library().unwrap();
|
music_hoard.rescan_library().unwrap();
|
||||||
music_hoard.load_from_database().unwrap();
|
music_hoard.reload_database().unwrap();
|
||||||
|
|
||||||
assert_eq!(music_hoard.get_collection(), &*COLLECTION);
|
assert_eq!(music_hoard.get_collection(), &*COLLECTION);
|
||||||
}
|
}
|
||||||
@ -54,7 +54,7 @@ fn merge_database_then_library() {
|
|||||||
|
|
||||||
let mut music_hoard = MusicHoard::new(library, database).unwrap();
|
let mut music_hoard = MusicHoard::new(library, database).unwrap();
|
||||||
|
|
||||||
music_hoard.load_from_database().unwrap();
|
music_hoard.reload_database().unwrap();
|
||||||
music_hoard.rescan_library().unwrap();
|
music_hoard.rescan_library().unwrap();
|
||||||
|
|
||||||
assert_eq!(music_hoard.get_collection(), &*COLLECTION);
|
assert_eq!(music_hoard.get_collection(), &*COLLECTION);
|
||||||
|
Loading…
Reference in New Issue
Block a user