Add code for merging

This commit is contained in:
Wojciech Kozlowski 2023-05-19 17:27:43 +02:00
parent 970a7786e9
commit 6a90b6cf78

View File

@ -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<Artist>;
trait Merge {
fn merge(self, other: Self) -> Self;
}
struct MergeSorted<L, R>
where
L: Iterator<Item = R::Item>,
R: Iterator,
{
left: Peekable<L>,
right: Peekable<R>,
}
impl<L, R> MergeSorted<L, R>
where
L: Iterator<Item = R::Item>,
R: Iterator,
{
fn new(left: L, right: R) -> MergeSorted<L, R> {
MergeSorted {
left: left.peekable(),
right: right.peekable(),
}
}
}
impl<L, R> Iterator for MergeSorted<L, R>
where
L: Iterator<Item = R::Item>,
R: Iterator,
L::Item: Ord + Merge,
{
type Item = L::Item;
fn next(&mut self) -> Option<L::Item> {
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<LIB: ILibrary, DB: IDatabase> MusicHoard<LIB, DB> {
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<LIB: ILibrary, DB: IDatabase> MusicHoard<LIB, DB> {
}
}
fn merge(primary: Vec<Artist>, secondary: Vec<Artist>) -> Vec<Artist> {
MergeSorted::new(primary.into_iter(), secondary.into_iter()).collect()
}
fn items_to_artists(items: Vec<Item>) -> Vec<Artist> {
let mut artists: Vec<Artist> = vec![];
let mut album_ids = HashMap::<ArtistId, HashSet<AlbumId>>::new();