Ensure consistency between in-memory and database state #146
@ -17,10 +17,11 @@ use crate::core::{
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MusicHoard<LIB, DB> {
|
pub struct MusicHoard<LIB, DB> {
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
|
pre_commit: Collection,
|
||||||
library: LIB,
|
library: LIB,
|
||||||
database: DB,
|
database: DB,
|
||||||
library_cache: HashMap<ArtistId, Artist>,
|
library_cache: HashMap<ArtistId, Artist>,
|
||||||
pre_commit: Collection,
|
database_cache: Collection,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Phantom type for when a library implementation is not needed.
|
/// Phantom type for when a library implementation is not needed.
|
||||||
@ -43,10 +44,11 @@ impl MusicHoard<NoLibrary, NoDatabase> {
|
|||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
MusicHoard {
|
MusicHoard {
|
||||||
collection: vec![],
|
collection: vec![],
|
||||||
|
pre_commit: vec![],
|
||||||
library: NoLibrary,
|
library: NoLibrary,
|
||||||
database: NoDatabase,
|
database: NoDatabase,
|
||||||
library_cache: HashMap::new(),
|
library_cache: HashMap::new(),
|
||||||
pre_commit: vec![],
|
database_cache: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,14 +74,14 @@ impl<LIB, DB> MusicHoard<LIB, DB> {
|
|||||||
|
|
||||||
fn merge_collections(&mut self) {
|
fn merge_collections(&mut self) {
|
||||||
let mut primary = self.library_cache.clone();
|
let mut primary = self.library_cache.clone();
|
||||||
for secondary_artist in self.collection.drain(..) {
|
for secondary_artist in self.database_cache.iter().cloned() {
|
||||||
if let Some(ref mut primary_artist) = primary.get_mut(&secondary_artist.id) {
|
if let Some(ref mut primary_artist) = primary.get_mut(&secondary_artist.id) {
|
||||||
primary_artist.merge_in_place(secondary_artist);
|
primary_artist.merge_in_place(secondary_artist);
|
||||||
} else {
|
} else {
|
||||||
primary.insert(secondary_artist.id.clone(), secondary_artist);
|
primary.insert(secondary_artist.id.clone(), secondary_artist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.collection.extend(primary.into_values());
|
self.collection = primary.into_values().collect();
|
||||||
Self::sort_artists(&mut self.collection);
|
Self::sort_artists(&mut self.collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,10 +180,11 @@ impl<LIB: ILibrary> MusicHoard<LIB, NoDatabase> {
|
|||||||
pub fn library(library: LIB) -> Self {
|
pub fn library(library: LIB) -> Self {
|
||||||
MusicHoard {
|
MusicHoard {
|
||||||
collection: vec![],
|
collection: vec![],
|
||||||
|
pre_commit: vec![],
|
||||||
library,
|
library,
|
||||||
database: NoDatabase,
|
database: NoDatabase,
|
||||||
library_cache: HashMap::new(),
|
library_cache: HashMap::new(),
|
||||||
pre_commit: vec![],
|
database_cache: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,10 +206,11 @@ impl<DB: IDatabase> MusicHoard<NoLibrary, DB> {
|
|||||||
pub fn database(database: DB) -> Result<Self, Error> {
|
pub fn database(database: DB) -> Result<Self, Error> {
|
||||||
let mut mh = MusicHoard {
|
let mut mh = MusicHoard {
|
||||||
collection: vec![],
|
collection: vec![],
|
||||||
|
pre_commit: vec![],
|
||||||
library: NoLibrary,
|
library: NoLibrary,
|
||||||
database,
|
database,
|
||||||
library_cache: HashMap::new(),
|
library_cache: HashMap::new(),
|
||||||
pre_commit: vec![],
|
database_cache: vec![],
|
||||||
};
|
};
|
||||||
mh.reload_database()?;
|
mh.reload_database()?;
|
||||||
Ok(mh)
|
Ok(mh)
|
||||||
@ -216,8 +220,8 @@ impl<DB: IDatabase> MusicHoard<NoLibrary, DB> {
|
|||||||
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 reload_database(&mut self) -> Result<(), Error> {
|
pub fn reload_database(&mut self) -> Result<(), Error> {
|
||||||
self.collection = self.database.load()?;
|
self.database_cache = self.database.load()?;
|
||||||
Self::sort_albums_and_tracks(self.collection.iter_mut());
|
Self::sort_albums_and_tracks(self.database_cache.iter_mut());
|
||||||
|
|
||||||
self.merge_collections();
|
self.merge_collections();
|
||||||
self.pre_commit = self.collection.clone();
|
self.pre_commit = self.collection.clone();
|
||||||
@ -367,10 +371,11 @@ impl<LIB: ILibrary, DB: IDatabase> MusicHoard<LIB, DB> {
|
|||||||
pub fn new(library: LIB, database: DB) -> Result<Self, Error> {
|
pub fn new(library: LIB, database: DB) -> Result<Self, Error> {
|
||||||
let mut mh = MusicHoard {
|
let mut mh = MusicHoard {
|
||||||
collection: vec![],
|
collection: vec![],
|
||||||
|
pre_commit: vec![],
|
||||||
library,
|
library,
|
||||||
database,
|
database,
|
||||||
library_cache: HashMap::new(),
|
library_cache: HashMap::new(),
|
||||||
pre_commit: vec![],
|
database_cache: vec![],
|
||||||
};
|
};
|
||||||
mh.reload_database()?;
|
mh.reload_database()?;
|
||||||
Ok(mh)
|
Ok(mh)
|
||||||
@ -657,12 +662,12 @@ mod tests {
|
|||||||
expected.sort_unstable();
|
expected.sort_unstable();
|
||||||
|
|
||||||
let mut mh = MusicHoard {
|
let mut mh = MusicHoard {
|
||||||
collection: right.clone(),
|
|
||||||
library_cache: left
|
library_cache: left
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| (a.id.clone(), a))
|
.map(|a| (a.id.clone(), a))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
database_cache: right.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -671,12 +676,12 @@ mod tests {
|
|||||||
|
|
||||||
// The merge is completely non-overlapping so it should be commutative.
|
// The merge is completely non-overlapping so it should be commutative.
|
||||||
let mut mh = MusicHoard {
|
let mut mh = MusicHoard {
|
||||||
collection: left.clone(),
|
|
||||||
library_cache: right
|
library_cache: right
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| (a.id.clone(), a))
|
.map(|a| (a.id.clone(), a))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
database_cache: left.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -695,12 +700,12 @@ mod tests {
|
|||||||
expected.sort_unstable();
|
expected.sort_unstable();
|
||||||
|
|
||||||
let mut mh = MusicHoard {
|
let mut mh = MusicHoard {
|
||||||
collection: right.clone(),
|
|
||||||
library_cache: left
|
library_cache: left
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| (a.id.clone(), a))
|
.map(|a| (a.id.clone(), a))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
database_cache: right.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -709,12 +714,12 @@ mod tests {
|
|||||||
|
|
||||||
// The merge does not overwrite any data so it should be commutative.
|
// The merge does not overwrite any data so it should be commutative.
|
||||||
let mut mh = MusicHoard {
|
let mut mh = MusicHoard {
|
||||||
collection: left.clone(),
|
|
||||||
library_cache: right
|
library_cache: right
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| (a.id.clone(), a))
|
.map(|a| (a.id.clone(), a))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
database_cache: left.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -746,12 +751,12 @@ mod tests {
|
|||||||
expected.rotate_right(1);
|
expected.rotate_right(1);
|
||||||
|
|
||||||
let mut mh = MusicHoard {
|
let mut mh = MusicHoard {
|
||||||
collection: right.clone(),
|
|
||||||
library_cache: left
|
library_cache: left
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| (a.id.clone(), a))
|
.map(|a| (a.id.clone(), a))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
database_cache: right.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -760,12 +765,12 @@ mod tests {
|
|||||||
|
|
||||||
// The merge overwrites the sort data, but no data is erased so it should be commutative.
|
// The merge overwrites the sort data, but no data is erased so it should be commutative.
|
||||||
let mut mh = MusicHoard {
|
let mut mh = MusicHoard {
|
||||||
collection: left.clone(),
|
|
||||||
library_cache: right
|
library_cache: right
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| (a.id.clone(), a))
|
.map(|a| (a.id.clone(), a))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
database_cache: left.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -792,6 +797,52 @@ mod tests {
|
|||||||
assert_eq!(music_hoard.get_collection(), &*LIBRARY_COLLECTION);
|
assert_eq!(music_hoard.get_collection(), &*LIBRARY_COLLECTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rescan_library_changed() {
|
||||||
|
let mut library = MockILibrary::new();
|
||||||
|
let mut seq = Sequence::new();
|
||||||
|
|
||||||
|
let library_input = Query::new();
|
||||||
|
let library_result = Ok(LIBRARY_ITEMS.to_owned());
|
||||||
|
|
||||||
|
library
|
||||||
|
.expect_list()
|
||||||
|
.with(predicate::eq(library_input))
|
||||||
|
.times(1)
|
||||||
|
.in_sequence(&mut seq)
|
||||||
|
.return_once(|_| library_result);
|
||||||
|
|
||||||
|
let library_input = Query::new();
|
||||||
|
let library_result = Ok(LIBRARY_ITEMS
|
||||||
|
.iter()
|
||||||
|
.filter(|item| item.album_title != "album_title a.a")
|
||||||
|
.cloned()
|
||||||
|
.collect());
|
||||||
|
|
||||||
|
library
|
||||||
|
.expect_list()
|
||||||
|
.with(predicate::eq(library_input))
|
||||||
|
.times(1)
|
||||||
|
.in_sequence(&mut seq)
|
||||||
|
.return_once(|_| library_result);
|
||||||
|
|
||||||
|
let mut music_hoard = MusicHoard::library(library);
|
||||||
|
|
||||||
|
music_hoard.rescan_library().unwrap();
|
||||||
|
assert!(music_hoard.get_collection()[0]
|
||||||
|
.albums
|
||||||
|
.iter()
|
||||||
|
.find(|album| album.id.title == "album_title a.a")
|
||||||
|
.is_some());
|
||||||
|
|
||||||
|
music_hoard.rescan_library().unwrap();
|
||||||
|
assert!(music_hoard.get_collection()[0]
|
||||||
|
.albums
|
||||||
|
.iter()
|
||||||
|
.find(|album| album.id.title == "album_title a.a")
|
||||||
|
.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rescan_library_unordered() {
|
fn rescan_library_unordered() {
|
||||||
let mut library = MockILibrary::new();
|
let mut library = MockILibrary::new();
|
||||||
|
Loading…
Reference in New Issue
Block a user