diff --git a/src/lib.rs b/src/lib.rs index b3d070d..bd7a147 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,8 +4,10 @@ pub mod database; pub mod library; use std::{ + cmp::Ordering, collections::{HashMap, HashSet}, fmt, + iter::Peekable, mem, }; use database::IDatabase; @@ -57,6 +59,13 @@ impl Ord for Track { } } +impl Merge for Track { + fn merge(self, other: Self) -> Self { + assert_eq!(self.id, other.id); + self + } +} + /// The album identifier. #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, PartialOrd, Ord, Eq, Hash)] pub struct AlbumId { @@ -83,6 +92,14 @@ impl Ord for Album { } } +impl Merge for Album { + fn merge(mut self, other: Self) -> Self { + assert_eq!(self.id, other.id); + self.tracks = MergeSorted::new(self.tracks.into_iter(), other.tracks.into_iter()).collect(); + self + } +} + /// The artist identifier. #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ArtistId { @@ -108,9 +125,67 @@ impl Ord for Artist { } } +impl Merge for Artist { + fn merge(mut self, other: Self) -> Self { + assert_eq!(self.id, other.id); + self.albums = MergeSorted::new(self.albums.into_iter(), other.albums.into_iter()).collect(); + self + } +} + /// The collection type. Currently, a collection is a list of artists. pub type Collection = Vec; +trait Merge { + fn merge(self, other: Self) -> Self; +} + +struct MergeSorted +where + L: Iterator, + R: Iterator, +{ + left: Peekable, + right: Peekable, +} + +impl MergeSorted +where + L: Iterator, + R: Iterator, +{ + fn new(left: L, right: R) -> MergeSorted { + MergeSorted { + left: left.peekable(), + right: right.peekable(), + } + } +} + +impl Iterator for MergeSorted +where + L: Iterator, + R: Iterator, + L::Item: Ord + Merge, +{ + type Item = L::Item; + + fn next(&mut self) -> Option { + let which = match (self.left.peek(), self.right.peek()) { + (Some(l), Some(r)) => l.cmp(r), + (Some(_), None) => Ordering::Less, + (None, Some(_)) => Ordering::Greater, + (None, None) => return None, + }; + + match which { + Ordering::Less => self.left.next(), + Ordering::Equal => Some(self.left.next().unwrap().merge(self.right.next().unwrap())), + Ordering::Greater => self.right.next(), + } + } +} + /// Error type for `musichoard`. #[derive(Debug, PartialEq, Eq)] pub enum Error { @@ -163,8 +238,23 @@ impl MusicHoard { pub fn rescan_library(&mut self) -> Result<(), Error> { let items = self.library.list(&Query::new())?; - self.collection = Self::items_to_artists(items); - Self::sort(&mut self.collection); + let mut library = Self::items_to_artists(items); + Self::sort(&mut library); + + let collection = mem::replace(&mut self.collection, vec![]); + self.collection = Self::merge(library, collection); + + Ok(()) + } + + pub fn load_from_database(&mut self) -> Result<(), Error> { + let mut database: Collection = vec![]; + self.database.read(&mut database)?; + Self::sort(&mut database); + + let collection = mem::replace(&mut self.collection, vec![]); + self.collection = Self::merge(collection, database); + Ok(()) } @@ -187,6 +277,10 @@ impl MusicHoard { } } + fn merge(primary: Vec, secondary: Vec) -> Vec { + MergeSorted::new(primary.into_iter(), secondary.into_iter()).collect() + } + fn items_to_artists(items: Vec) -> Vec { let mut artists: Vec = vec![]; let mut album_ids = HashMap::>::new();