Compare commits

..

No commits in common. "5abac79414cd62e1a899cfa3dd8e1c4d3c35a6d0" and "f7e215eedfe0de641dc874390d3706046b221e0b" have entirely different histories.

12 changed files with 109 additions and 129 deletions

View File

@ -2,10 +2,12 @@
pub mod album; pub mod album;
pub mod artist; pub mod artist;
pub mod merge;
pub mod musicbrainz; pub mod musicbrainz;
pub mod track; pub mod track;
mod merge;
pub use merge::MergeCollections;
use std::fmt::{self, Display}; use std::fmt::{self, Display};
/// The [`Collection`] alias type for convenience. /// The [`Collection`] alias type for convenience.

View File

@ -2,8 +2,7 @@ use crate::core::{
collection::{ collection::{
album::{Album, AlbumId}, album::{Album, AlbumId},
artist::{Artist, ArtistId}, artist::{Artist, ArtistId},
merge::MergeCollections, Collection, MergeCollections,
Collection,
}, },
musichoard::{Error, MusicHoard}, musichoard::{Error, MusicHoard},
}; };

View File

@ -1,7 +1,5 @@
use std::mem;
use crate::{ use crate::{
collection::{album::AlbumInfo, artist::ArtistInfo, merge::Merge}, collection::{album::AlbumInfo, artist::ArtistInfo},
core::{ core::{
collection::{ collection::{
album::{Album, AlbumId, AlbumSeq}, album::{Album, AlbumId, AlbumSeq},
@ -26,7 +24,7 @@ pub trait IMusicHoardDatabase {
) -> Result<(), Error>; ) -> Result<(), Error>;
fn clear_artist_sort<Id: AsRef<ArtistId>>(&mut self, artist_id: Id) -> Result<(), Error>; fn clear_artist_sort<Id: AsRef<ArtistId>>(&mut self, artist_id: Id) -> Result<(), Error>;
fn merge_artist_info<Id: AsRef<ArtistId>>( fn set_artist_info<Id: AsRef<ArtistId>>(
&mut self, &mut self,
artist_id: Id, artist_id: Id,
info: ArtistInfo, info: ArtistInfo,
@ -68,7 +66,7 @@ pub trait IMusicHoardDatabase {
artist_id: ArtistIdRef, artist_id: ArtistIdRef,
album_id: AlbumIdRef, album_id: AlbumIdRef,
) -> Result<(), Error>; ) -> Result<(), Error>;
fn merge_album_info<Id: AsRef<ArtistId>, AlbumIdRef: AsRef<AlbumId>>( fn set_album_info<Id: AsRef<ArtistId>, AlbumIdRef: AsRef<AlbumId>>(
&mut self, &mut self,
artist_id: Id, artist_id: Id,
album_id: AlbumIdRef, album_id: AlbumIdRef,
@ -134,15 +132,12 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
) )
} }
fn merge_artist_info<Id: AsRef<ArtistId>>( fn set_artist_info<Id: AsRef<ArtistId>>(
&mut self, &mut self,
artist_id: Id, artist_id: Id,
mut info: ArtistInfo, info: ArtistInfo,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.update_artist(artist_id.as_ref(), |artist| { self.update_artist(artist_id.as_ref(), |artist| artist.meta.info = info)
mem::swap(&mut artist.meta.info, &mut info);
artist.meta.info.merge_in_place(info);
})
} }
fn clear_artist_info<Id: AsRef<ArtistId>>(&mut self, artist_id: Id) -> Result<(), Error> { fn clear_artist_info<Id: AsRef<ArtistId>>(&mut self, artist_id: Id) -> Result<(), Error> {
@ -221,15 +216,14 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
) )
} }
fn merge_album_info<Id: AsRef<ArtistId>, AlbumIdRef: AsRef<AlbumId>>( fn set_album_info<Id: AsRef<ArtistId>, AlbumIdRef: AsRef<AlbumId>>(
&mut self, &mut self,
artist_id: Id, artist_id: Id,
album_id: AlbumIdRef, album_id: AlbumIdRef,
mut info: AlbumInfo, info: AlbumInfo,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.update_album(artist_id.as_ref(), album_id.as_ref(), |album| { self.update_album(artist_id.as_ref(), album_id.as_ref(), |album| {
mem::swap(&mut album.meta.info, &mut info); album.meta.info = info
album.meta.info.merge_in_place(info);
}) })
} }
@ -463,7 +457,7 @@ mod tests {
let artist_id = ArtistId::new("an artist"); let artist_id = ArtistId::new("an artist");
let actual_err = music_hoard let actual_err = music_hoard
.merge_artist_info(&artist_id, ArtistInfo::default()) .set_artist_info(&artist_id, ArtistInfo::default())
.unwrap_err(); .unwrap_err();
let expected_err = let expected_err =
Error::CollectionError(format!("artist '{artist_id}' is not in the collection")); Error::CollectionError(format!("artist '{artist_id}' is not in the collection"));
@ -490,13 +484,13 @@ mod tests {
// Setting a URL on an artist not in the collection is an error. // Setting a URL on an artist not in the collection is an error.
assert!(music_hoard assert!(music_hoard
.merge_artist_info(&artist_id_2, info.clone()) .set_artist_info(&artist_id_2, info.clone())
.is_err()); .is_err());
assert_eq!(music_hoard.collection[0].meta.info.musicbrainz, expected); assert_eq!(music_hoard.collection[0].meta.info.musicbrainz, expected);
// Setting a URL on an artist. // Setting a URL on an artist.
assert!(music_hoard assert!(music_hoard
.merge_artist_info(&artist_id, info.clone()) .set_artist_info(&artist_id, info.clone())
.is_ok()); .is_ok());
expected.replace(MbArtistRef::from_uuid_str(MBID).unwrap()); expected.replace(MbArtistRef::from_uuid_str(MBID).unwrap());
assert_eq!(music_hoard.collection[0].meta.info.musicbrainz, expected); assert_eq!(music_hoard.collection[0].meta.info.musicbrainz, expected);
@ -685,14 +679,14 @@ mod tests {
// Seting info on an album not belonging to the artist is an error. // Seting info on an album not belonging to the artist is an error.
assert!(music_hoard assert!(music_hoard
.merge_album_info(&artist_id, &album_id_2, info.clone()) .set_album_info(&artist_id, &album_id_2, info.clone())
.is_err()); .is_err());
let meta = &music_hoard.collection[0].albums[0].meta; let meta = &music_hoard.collection[0].albums[0].meta;
assert_eq!(meta.info, AlbumInfo::default()); assert_eq!(meta.info, AlbumInfo::default());
// Set info. // Set info.
assert!(music_hoard assert!(music_hoard
.merge_album_info(&artist_id, &album_id, info.clone()) .set_album_info(&artist_id, &album_id, info.clone())
.is_ok()); .is_ok());
let meta = &music_hoard.collection[0].albums[0].meta; let meta = &music_hoard.collection[0].albums[0].meta;
assert_eq!(meta.info, info); assert_eq!(meta.info, info);

View File

@ -5,7 +5,7 @@ use std::{
}; };
use musichoard::collection::{ use musichoard::collection::{
album::{Album, AlbumId}, album::{Album, AlbumMeta},
artist::{Artist, ArtistId, ArtistMeta}, artist::{Artist, ArtistId, ArtistMeta},
musicbrainz::{IMusicBrainzRef, MbArtistRef, MbRefOption, Mbid}, musicbrainz::{IMusicBrainzRef, MbArtistRef, MbRefOption, Mbid},
}; };
@ -151,13 +151,13 @@ impl AppMachine<FetchState> {
inner: AppInner, inner: AppInner,
fetch: FetchState, fetch: FetchState,
artist_id: &ArtistId, artist_id: &ArtistId,
album_id: &AlbumId, album: &AlbumMeta,
mbid: Mbid, mbid: Mbid,
) -> App { ) -> App {
let f = |mb: &dyn IMbJobSender, rs, album, mbid| { let f = |mb: &dyn IMbJobSender, rs, album, mbid| {
Self::submit_lookup_release_group_job(mb, rs, artist_id, album, mbid) Self::submit_lookup_release_group_job(mb, rs, artist_id, album, mbid)
}; };
Self::app_lookup(f, inner, fetch, album_id, mbid) Self::app_lookup(f, inner, fetch, album, mbid)
} }
fn app_lookup<F, Meta>( fn app_lookup<F, Meta>(
@ -243,12 +243,12 @@ impl AppMachine<FetchState> {
musicbrainz: &dyn IMbJobSender, musicbrainz: &dyn IMbJobSender,
result_sender: ResultSender, result_sender: ResultSender,
artist_id: &ArtistId, artist_id: &ArtistId,
album_id: &AlbumId, album: &AlbumMeta,
mbid: Mbid, mbid: Mbid,
) -> Result<(), DaemonError> { ) -> Result<(), DaemonError> {
let requests = VecDeque::from([MbParams::lookup_release_group( let requests = VecDeque::from([MbParams::lookup_release_group(
artist_id.clone(), artist_id.clone(),
album_id.clone(), album.clone(),
mbid, mbid,
)]); )]);
musicbrainz.submit_foreground_job(result_sender, requests) musicbrainz.submit_foreground_job(result_sender, requests)
@ -468,11 +468,11 @@ mod tests {
fn lookup_album_expectation( fn lookup_album_expectation(
job_sender: &mut MockIMbJobSender, job_sender: &mut MockIMbJobSender,
artist_id: &ArtistId, artist_id: &ArtistId,
album_id: &AlbumId, album: &AlbumMeta,
) { ) {
let requests = VecDeque::from([MbParams::lookup_release_group( let requests = VecDeque::from([MbParams::lookup_release_group(
artist_id.clone(), artist_id.clone(),
album_id.clone(), album.clone(),
mbid(), mbid(),
)]); )]);
job_sender job_sender
@ -487,8 +487,8 @@ mod tests {
let mut mb_job_sender = MockIMbJobSender::new(); let mut mb_job_sender = MockIMbJobSender::new();
let artist_id = COLLECTION[1].meta.id.clone(); let artist_id = COLLECTION[1].meta.id.clone();
let album_id = COLLECTION[1].albums[0].meta.id.clone(); let album = COLLECTION[1].albums[0].meta.clone();
lookup_album_expectation(&mut mb_job_sender, &artist_id, &album_id); lookup_album_expectation(&mut mb_job_sender, &artist_id, &album);
let music_hoard = music_hoard(COLLECTION.to_owned()); let music_hoard = music_hoard(COLLECTION.to_owned());
let inner = AppInner::new(music_hoard, mb_job_sender); let inner = AppInner::new(music_hoard, mb_job_sender);
@ -496,7 +496,7 @@ mod tests {
let (_fetch_tx, fetch_rx) = mpsc::channel(); let (_fetch_tx, fetch_rx) = mpsc::channel();
let fetch = FetchState::new(fetch_rx); let fetch = FetchState::new(fetch_rx);
AppMachine::app_lookup_album(inner, fetch, &artist_id, &album_id, mbid()); AppMachine::app_lookup_album(inner, fetch, &artist_id, &album, mbid());
} }
fn search_artist_expectation(job_sender: &mut MockIMbJobSender, artist: &ArtistMeta) { fn search_artist_expectation(job_sender: &mut MockIMbJobSender, artist: &ArtistMeta) {

View File

@ -24,7 +24,7 @@ impl GetInfoMeta for AlbumMeta {
trait GetInfo { trait GetInfo {
type InfoType; type InfoType;
fn get_info(&self) -> InfoOption<Self::InfoType>; fn into_info(&self, info: Self::InfoType) -> InfoOption<Self::InfoType>;
} }
enum InfoOption<T> { enum InfoOption<T> {
@ -35,8 +35,7 @@ enum InfoOption<T> {
impl GetInfo for MatchOption<ArtistMeta> { impl GetInfo for MatchOption<ArtistMeta> {
type InfoType = ArtistInfo; type InfoType = ArtistInfo;
fn get_info(&self) -> InfoOption<Self::InfoType> { fn into_info(&self, mut info: Self::InfoType) -> InfoOption<Self::InfoType> {
let mut info = ArtistInfo::default();
match self { match self {
MatchOption::Some(option) => info.musicbrainz = option.entity.info.musicbrainz.clone(), MatchOption::Some(option) => info.musicbrainz = option.entity.info.musicbrainz.clone(),
MatchOption::CannotHaveMbid => info.musicbrainz = MbRefOption::CannotHaveMbid, MatchOption::CannotHaveMbid => info.musicbrainz = MbRefOption::CannotHaveMbid,
@ -49,8 +48,7 @@ impl GetInfo for MatchOption<ArtistMeta> {
impl GetInfo for MatchOption<AlbumMeta> { impl GetInfo for MatchOption<AlbumMeta> {
type InfoType = AlbumInfo; type InfoType = AlbumInfo;
fn get_info(&self) -> InfoOption<Self::InfoType> { fn into_info(&self, mut info: Self::InfoType) -> InfoOption<Self::InfoType> {
let mut info = AlbumInfo::default();
match self { match self {
MatchOption::Some(option) => info = option.entity.info.clone(), MatchOption::Some(option) => info = option.entity.info.clone(),
MatchOption::CannotHaveMbid => info.musicbrainz = MbRefOption::CannotHaveMbid, MatchOption::CannotHaveMbid => info.musicbrainz = MbRefOption::CannotHaveMbid,
@ -62,7 +60,7 @@ impl GetInfo for MatchOption<AlbumMeta> {
trait ExtractInfo { trait ExtractInfo {
type InfoType; type InfoType;
fn extract_info(&self, index: usize) -> InfoOption<Self::InfoType>; fn extract_info(&self, index: usize, info: Self::InfoType) -> InfoOption<Self::InfoType>;
} }
impl<T: GetInfoMeta> ExtractInfo for Vec<MatchOption<T>> impl<T: GetInfoMeta> ExtractInfo for Vec<MatchOption<T>>
@ -72,8 +70,8 @@ where
{ {
type InfoType = T::InfoType; type InfoType = T::InfoType;
fn extract_info(&self, index: usize) -> InfoOption<Self::InfoType> { fn extract_info(&self, index: usize, info: Self::InfoType) -> InfoOption<Self::InfoType> {
self.get(index).unwrap().get_info() self.get(index).unwrap().into_info(info)
} }
} }
@ -227,16 +225,22 @@ impl IAppInteractMatch for AppMachine<MatchState> {
let mh = &mut self.inner.music_hoard; let mh = &mut self.inner.music_hoard;
let result = match self.state.current { let result = match self.state.current {
EntityMatches::Artist(ref mut matches) => match matches.list.extract_info(index) { EntityMatches::Artist(ref mut matches) => {
InfoOption::Info(info) => mh.merge_artist_info(&matches.matching.id, info), let info: ArtistInfo = matches.matching.info.clone();
InfoOption::NeedInput => return self.get_input(), match matches.list.extract_info(index, info) {
}, InfoOption::Info(info) => mh.set_artist_info(&matches.matching.id, info),
EntityMatches::Album(ref mut matches) => match matches.list.extract_info(index) { InfoOption::NeedInput => return self.get_input(),
InfoOption::Info(info) => {
mh.merge_album_info(&matches.artist, &matches.matching, info)
} }
InfoOption::NeedInput => return self.get_input(), }
}, EntityMatches::Album(ref mut matches) => {
let info: AlbumInfo = matches.matching.info.clone();
match matches.list.extract_info(index, info) {
InfoOption::Info(info) => {
mh.set_album_info(&matches.artist, &matches.matching.id, info)
}
InfoOption::NeedInput => return self.get_input(),
}
}
}; };
if let Err(err) = result { if let Err(err) = result {
@ -314,13 +318,9 @@ mod tests {
EntityMatches::artist_lookup(artist, lookup) EntityMatches::artist_lookup(artist, lookup)
} }
fn album_id() -> AlbumId { fn album_meta() -> AlbumMeta {
AlbumId::new("Album")
}
fn album_meta(id: AlbumId) -> AlbumMeta {
AlbumMeta::new( AlbumMeta::new(
id, AlbumId::new("Album"),
AlbumDate::new(Some(1990), Some(5), None), AlbumDate::new(Some(1990), Some(5), None),
AlbumInfo::new( AlbumInfo::new(
MbRefOption::Some(mbid().into()), MbRefOption::Some(mbid().into()),
@ -332,27 +332,25 @@ mod tests {
fn album_match() -> EntityMatches { fn album_match() -> EntityMatches {
let artist_id = ArtistId::new("Artist"); let artist_id = ArtistId::new("Artist");
let album_id = album_id(); let album = album_meta();
let album_meta = album_meta(album_id.clone());
let album_1 = album_meta.clone(); let album_1 = album.clone();
let album_match_1 = Entity::with_score(album_1, 100); let album_match_1 = Entity::with_score(album_1, 100);
let mut album_2 = album_meta.clone(); let mut album_2 = album.clone();
album_2.id.title.push_str(" extra title part"); album_2.id.title.push_str(" extra title part");
album_2.info.secondary_types.pop(); album_2.info.secondary_types.pop();
let album_match_2 = Entity::with_score(album_2, 100); let album_match_2 = Entity::with_score(album_2, 100);
let list = vec![album_match_1.clone(), album_match_2.clone()]; let list = vec![album_match_1.clone(), album_match_2.clone()];
EntityMatches::album_search(artist_id, album_id, list) EntityMatches::album_search(artist_id, album, list)
} }
fn album_lookup() -> EntityMatches { fn album_lookup() -> EntityMatches {
let artist_id = ArtistId::new("Artist"); let artist_id = ArtistId::new("Artist");
let album_id = album_id(); let album = album_meta();
let album_meta = album_meta(album_id.clone()); let lookup = Entity::new(album.clone());
let lookup = Entity::new(album_meta.clone()); EntityMatches::album_lookup(artist_id, album, lookup)
EntityMatches::album_lookup(artist_id, album_id, lookup)
} }
fn fetch_state() -> FetchState { fn fetch_state() -> FetchState {
@ -395,10 +393,10 @@ mod tests {
match matches_info { match matches_info {
EntityMatches::Album(_) => { EntityMatches::Album(_) => {
let album_id = AlbumId::new("Album"); let album_id = AlbumId::new("Album");
let mut info = album_meta(album_id.clone()).info; let mut info = album_meta().info;
info.musicbrainz = MbRefOption::CannotHaveMbid; info.musicbrainz = MbRefOption::CannotHaveMbid;
music_hoard music_hoard
.expect_merge_album_info() .expect_set_album_info()
.with(eq(artist_id.clone()), eq(album_id.clone()), eq(info)) .with(eq(artist_id.clone()), eq(album_id.clone()), eq(info))
.times(1) .times(1)
.return_once(|_, _, _| Ok(())); .return_once(|_, _, _| Ok(()));
@ -407,7 +405,7 @@ mod tests {
let mut info = artist_meta().info; let mut info = artist_meta().info;
info.musicbrainz = MbRefOption::CannotHaveMbid; info.musicbrainz = MbRefOption::CannotHaveMbid;
music_hoard music_hoard
.expect_merge_artist_info() .expect_set_artist_info()
.with(eq(artist_id.clone()), eq(info)) .with(eq(artist_id.clone()), eq(info))
.times(1) .times(1)
.return_once(|_, _| Ok(())); .return_once(|_, _| Ok(()));
@ -491,7 +489,7 @@ mod tests {
EntityMatches::Artist(_) => { EntityMatches::Artist(_) => {
let meta = artist_meta(); let meta = artist_meta();
music_hoard music_hoard
.expect_merge_artist_info() .expect_set_artist_info()
.with(eq(meta.id), eq(meta.info)) .with(eq(meta.id), eq(meta.info))
.times(1) .times(1)
.return_once(|_, _| Ok(())); .return_once(|_, _| Ok(()));
@ -513,9 +511,9 @@ mod tests {
match matches_info { match matches_info {
EntityMatches::Artist(_) => panic!(), EntityMatches::Artist(_) => panic!(),
EntityMatches::Album(matches) => { EntityMatches::Album(matches) => {
let meta = album_meta(album_id()); let meta = album_meta();
music_hoard music_hoard
.expect_merge_album_info() .expect_set_album_info()
.with(eq(matches.artist), eq(meta.id), eq(meta.info)) .with(eq(matches.artist), eq(meta.id), eq(meta.info))
.times(1) .times(1)
.return_once(|_, _, _| Ok(())); .return_once(|_, _, _| Ok(()));
@ -537,7 +535,7 @@ mod tests {
match matches_info { match matches_info {
EntityMatches::Album(_) => panic!(), EntityMatches::Album(_) => panic!(),
EntityMatches::Artist(_) => { EntityMatches::Artist(_) => {
music_hoard.expect_merge_artist_info().return_once(|_, _| { music_hoard.expect_set_artist_info().return_once(|_, _| {
Err(musichoard::Error::DatabaseError(String::from("error"))) Err(musichoard::Error::DatabaseError(String::from("error")))
}); });
} }
@ -624,7 +622,7 @@ mod tests {
let album = AlbumMeta::new("Album", 1990, AlbumInfo::default()); let album = AlbumMeta::new("Album", 1990, AlbumInfo::default());
let requests = VecDeque::from([MbParams::lookup_release_group( let requests = VecDeque::from([MbParams::lookup_release_group(
artist_id.clone(), artist_id.clone(),
album.id.clone(), album.clone(),
mbid(), mbid(),
)]); )]);
mb_job_sender mb_job_sender
@ -634,7 +632,7 @@ mod tests {
let matches_vec: Vec<Entity<AlbumMeta>> = vec![]; let matches_vec: Vec<Entity<AlbumMeta>> = vec![];
let album_match = let album_match =
EntityMatches::album_search(artist_id.clone(), album.id.clone(), matches_vec); EntityMatches::album_search(artist_id.clone(), album.clone(), matches_vec);
let matches = AppMachine::match_state( let matches = AppMachine::match_state(
inner_with_mb(music_hoard(vec![]), mb_job_sender), inner_with_mb(music_hoard(vec![]), mb_job_sender),
match_state(album_match), match_state(album_match),

View File

@ -6,7 +6,7 @@ use ratatui::widgets::ListState;
pub use selection::{Category, Selection}; pub use selection::{Category, Selection};
use musichoard::collection::{ use musichoard::collection::{
album::{AlbumId, AlbumMeta}, album::AlbumMeta,
artist::{ArtistId, ArtistMeta}, artist::{ArtistId, ArtistMeta},
Collection, Collection,
}; };
@ -235,7 +235,7 @@ pub struct ArtistMatches {
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct AlbumMatches { pub struct AlbumMatches {
pub artist: ArtistId, pub artist: ArtistId,
pub matching: AlbumId, pub matching: AlbumMeta,
pub list: Vec<MatchOption<AlbumMeta>>, pub list: Vec<MatchOption<AlbumMeta>>,
} }
@ -256,7 +256,7 @@ impl EntityMatches {
pub fn album_search<M: Into<MatchOption<AlbumMeta>>>( pub fn album_search<M: Into<MatchOption<AlbumMeta>>>(
artist: ArtistId, artist: ArtistId,
matching: AlbumId, matching: AlbumMeta,
list: Vec<M>, list: Vec<M>,
) -> Self { ) -> Self {
let list = list.into_iter().map(Into::into).collect(); let list = list.into_iter().map(Into::into).collect();
@ -274,7 +274,7 @@ impl EntityMatches {
pub fn album_lookup<M: Into<MatchOption<AlbumMeta>>>( pub fn album_lookup<M: Into<MatchOption<AlbumMeta>>>(
artist: ArtistId, artist: ArtistId,
matching: AlbumId, matching: AlbumMeta,
item: M, item: M,
) -> Self { ) -> Self {
let list = vec![item.into()]; let list = vec![item.into()];

View File

@ -231,7 +231,7 @@ impl JobInstance {
if let Some(params) = self.requests.front() { if let Some(params) = self.requests.front() {
let result_sender = &mut self.result_sender; let result_sender = &mut self.result_sender;
let paging = &mut self.paging; let paging = &mut self.paging;
Self::execute(musicbrainz, result_sender, event_sender, params, paging)?; Self::execute(musicbrainz, result_sender, event_sender, &params, paging)?;
}; };
if self.paging.is_none() { if self.paging.is_none() {
@ -263,7 +263,7 @@ impl JobInstance {
.map(|rv| EntityMatches::artist_lookup(p.artist.clone(), rv)), .map(|rv| EntityMatches::artist_lookup(p.artist.clone(), rv)),
LookupParams::ReleaseGroup(p) => { LookupParams::ReleaseGroup(p) => {
musicbrainz.lookup_release_group(&p.mbid).map(|rv| { musicbrainz.lookup_release_group(&p.mbid).map(|rv| {
EntityMatches::album_lookup(p.artist_id.clone(), p.album_id.clone(), rv) EntityMatches::album_lookup(p.artist_id.clone(), p.album.clone(), rv)
}) })
} }
} }
@ -275,7 +275,7 @@ impl JobInstance {
SearchParams::ReleaseGroup(p) => musicbrainz SearchParams::ReleaseGroup(p) => musicbrainz
.search_release_group(&p.artist_mbid, &p.album) .search_release_group(&p.artist_mbid, &p.album)
.map(|rv| { .map(|rv| {
EntityMatches::album_search(p.artist_id.clone(), p.album.id.clone(), rv) EntityMatches::album_search(p.artist_id.clone(), p.album.clone(), rv)
}), }),
} }
.map(MbReturn::Match), .map(MbReturn::Match),
@ -428,9 +428,9 @@ mod tests {
fn lookup_release_group_requests() -> VecDeque<MbParams> { fn lookup_release_group_requests() -> VecDeque<MbParams> {
let artist_id = COLLECTION[1].meta.id.clone(); let artist_id = COLLECTION[1].meta.id.clone();
let album_id = COLLECTION[1].albums[0].meta.id.clone(); let album = COLLECTION[1].albums[0].meta.clone();
let mbid = mbid(); let mbid = mbid();
VecDeque::from([MbParams::lookup_release_group(artist_id, album_id, mbid)]) VecDeque::from([MbParams::lookup_release_group(artist_id, album, mbid)])
} }
fn search_artist_requests() -> VecDeque<MbParams> { fn search_artist_requests() -> VecDeque<MbParams> {
@ -630,9 +630,8 @@ mod tests {
fn execute_lookup_release_group() { fn execute_lookup_release_group() {
let mut musicbrainz = musicbrainz(); let mut musicbrainz = musicbrainz();
let mbid = mbid(); let mbid = mbid();
let album_id = COLLECTION[1].albums[0].meta.id.clone(); let album = COLLECTION[1].albums[0].meta.clone();
let album_meta = COLLECTION[1].albums[0].meta.clone(); let lookup = Entity::new(album.clone());
let lookup = Entity::new(album_meta.clone());
lookup_release_group_expectation(&mut musicbrainz, &mbid, &lookup); lookup_release_group_expectation(&mut musicbrainz, &mbid, &lookup);
let mut event_sender = event_sender(); let mut event_sender = event_sender();
@ -657,7 +656,7 @@ mod tests {
assert_eq!( assert_eq!(
result, result,
Ok(MbReturn::Match(EntityMatches::album_lookup( Ok(MbReturn::Match(EntityMatches::album_lookup(
artist_id, album_id, lookup artist_id, album, lookup
))) )))
); );
} }
@ -761,7 +760,7 @@ mod tests {
result, result,
Ok(MbReturn::Match(EntityMatches::album_search( Ok(MbReturn::Match(EntityMatches::album_search(
artist_id.clone(), artist_id.clone(),
album_1.id, album_1,
matches_1 matches_1
))) )))
); );
@ -771,7 +770,7 @@ mod tests {
result, result,
Ok(MbReturn::Match(EntityMatches::album_search( Ok(MbReturn::Match(EntityMatches::album_search(
artist_id.clone(), artist_id.clone(),
album_4.id, album_4,
matches_4 matches_4
))) )))
); );

View File

@ -1,7 +1,7 @@
use std::{collections::VecDeque, fmt, sync::mpsc}; use std::{collections::VecDeque, fmt, sync::mpsc};
use musichoard::collection::{ use musichoard::collection::{
album::{AlbumId, AlbumMeta}, album::AlbumMeta,
artist::{ArtistId, ArtistMeta}, artist::{ArtistId, ArtistMeta},
musicbrainz::Mbid, musicbrainz::Mbid,
}; };
@ -59,7 +59,6 @@ pub trait IMbJobSender {
pub enum MbParams { pub enum MbParams {
Lookup(LookupParams), Lookup(LookupParams),
Search(SearchParams), Search(SearchParams),
#[allow(dead_code)] // TODO: remove with completion of #160
Browse(BrowseParams), Browse(BrowseParams),
} }
@ -78,7 +77,7 @@ pub struct LookupArtistParams {
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct LookupReleaseGroupParams { pub struct LookupReleaseGroupParams {
pub artist_id: ArtistId, pub artist_id: ArtistId,
pub album_id: AlbumId, pub album: AlbumMeta,
pub mbid: Mbid, pub mbid: Mbid,
} }
@ -102,7 +101,6 @@ pub struct SearchReleaseGroupParams {
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum BrowseParams { pub enum BrowseParams {
#[allow(dead_code)] // TODO: remove with completion of #160
ReleaseGroup(BrowseReleaseGroupParams), ReleaseGroup(BrowseReleaseGroupParams),
} }
@ -116,10 +114,10 @@ impl MbParams {
MbParams::Lookup(LookupParams::Artist(LookupArtistParams { artist, mbid })) MbParams::Lookup(LookupParams::Artist(LookupArtistParams { artist, mbid }))
} }
pub fn lookup_release_group(artist_id: ArtistId, album_id: AlbumId, mbid: Mbid) -> Self { pub fn lookup_release_group(artist_id: ArtistId, album: AlbumMeta, mbid: Mbid) -> Self {
MbParams::Lookup(LookupParams::ReleaseGroup(LookupReleaseGroupParams { MbParams::Lookup(LookupParams::ReleaseGroup(LookupReleaseGroupParams {
artist_id, artist_id,
album_id, album,
mbid, mbid,
})) }))
} }

View File

@ -20,12 +20,9 @@ pub trait IMusicHoard {
fn reload_database(&mut self) -> Result<(), musichoard::Error>; fn reload_database(&mut self) -> Result<(), musichoard::Error>;
fn get_collection(&self) -> &Collection; fn get_collection(&self) -> &Collection;
fn merge_artist_info( fn set_artist_info(&mut self, id: &ArtistId, info: ArtistInfo)
&mut self, -> Result<(), musichoard::Error>;
id: &ArtistId, fn set_album_info(
info: ArtistInfo,
) -> Result<(), musichoard::Error>;
fn merge_album_info(
&mut self, &mut self,
artist_id: &ArtistId, artist_id: &ArtistId,
album_id: &AlbumId, album_id: &AlbumId,
@ -47,21 +44,21 @@ impl<Database: IDatabase, Library: ILibrary> IMusicHoard for MusicHoard<Database
<Self as IMusicHoardBase>::get_collection(self) <Self as IMusicHoardBase>::get_collection(self)
} }
fn merge_artist_info( fn set_artist_info(
&mut self, &mut self,
id: &ArtistId, id: &ArtistId,
info: ArtistInfo, info: ArtistInfo,
) -> Result<(), musichoard::Error> { ) -> Result<(), musichoard::Error> {
<Self as IMusicHoardDatabase>::merge_artist_info(self, id, info) <Self as IMusicHoardDatabase>::set_artist_info(self, id, info)
} }
fn merge_album_info( fn set_album_info(
&mut self, &mut self,
artist_id: &ArtistId, artist_id: &ArtistId,
album_id: &AlbumId, album_id: &AlbumId,
info: AlbumInfo, info: AlbumInfo,
) -> Result<(), musichoard::Error> { ) -> Result<(), musichoard::Error> {
<Self as IMusicHoardDatabase>::merge_album_info(self, artist_id, album_id, info) <Self as IMusicHoardDatabase>::set_album_info(self, artist_id, album_id, info)
} }
} }
// GRCOV_EXCL_STOP // GRCOV_EXCL_STOP

View File

@ -1,7 +1,5 @@
use musichoard::collection::{ use musichoard::collection::{
album::{ album::{AlbumDate, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq, AlbumStatus},
AlbumDate, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq, AlbumStatus,
},
artist::ArtistMeta, artist::ArtistMeta,
musicbrainz::{IMusicBrainzRef, MbRefOption}, musicbrainz::{IMusicBrainzRef, MbRefOption},
track::{TrackFormat, TrackQuality}, track::{TrackFormat, TrackQuality},
@ -113,8 +111,12 @@ impl UiDisplay {
format!("Matching artist: {}", &artist.id.name) format!("Matching artist: {}", &artist.id.name)
} }
pub fn display_album_matching(album: &AlbumId) -> String { pub fn display_album_matching(album: &AlbumMeta) -> String {
format!("Matching album: {}", &album.title) format!(
"Matching album: {} | {}",
UiDisplay::display_album_date(&album.date),
&album.id.title
)
} }
pub fn display_matching_info(info: &EntityMatches) -> String { pub fn display_matching_info(info: &EntityMatches) -> String {

View File

@ -1,7 +1,4 @@
use musichoard::collection::{ use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta};
album::{AlbumId, AlbumMeta},
artist::ArtistMeta,
};
use ratatui::widgets::{List, ListItem}; use ratatui::widgets::{List, ListItem};
use crate::tui::{ use crate::tui::{
@ -40,7 +37,7 @@ impl<'a, 'b> MatchOverlay<'a, 'b> {
} }
fn albums( fn albums(
matching: &AlbumId, matching: &AlbumMeta,
matches: &'a [MatchOption<AlbumMeta>], matches: &'a [MatchOption<AlbumMeta>],
state: &'b mut WidgetState, state: &'b mut WidgetState,
) -> Self { ) -> Self {

View File

@ -356,13 +356,9 @@ mod tests {
ArtistId::new("Artist") ArtistId::new("Artist")
} }
fn album_id() -> AlbumId { fn album_meta() -> AlbumMeta {
AlbumId::new("An Album")
}
fn album_meta(id: AlbumId) -> AlbumMeta {
AlbumMeta::new( AlbumMeta::new(
id, AlbumId::new("An Album"),
AlbumDate::new(Some(1990), Some(5), None), AlbumDate::new(Some(1990), Some(5), None),
AlbumInfo::new( AlbumInfo::new(
MbRefOption::None, MbRefOption::None,
@ -374,12 +370,11 @@ mod tests {
fn album_matches() -> EntityMatches { fn album_matches() -> EntityMatches {
let artist_id = album_artist_id(); let artist_id = album_artist_id();
let album_id = album_id(); let album = album_meta();
let album_meta = album_meta(album_id.clone()); let album_match = Entity::with_score(album.clone(), 80);
let album_match = Entity::with_score(album_meta.clone(), 80);
let list = vec![album_match.clone(), album_match.clone()]; let list = vec![album_match.clone(), album_match.clone()];
let mut info = EntityMatches::album_search(artist_id, album_id, list); let mut info = EntityMatches::album_search(artist_id, album, list);
info.push_cannot_have_mbid(); info.push_cannot_have_mbid();
info.push_manual_input_mbid(); info.push_manual_input_mbid();
info info
@ -387,11 +382,10 @@ mod tests {
fn album_lookup() -> EntityMatches { fn album_lookup() -> EntityMatches {
let artist_id = album_artist_id(); let artist_id = album_artist_id();
let album_id = album_id(); let album = album_meta();
let album_meta = album_meta(album_id.clone()); let album_lookup = Entity::new(album.clone());
let album_lookup = Entity::new(album_meta.clone());
let mut info = EntityMatches::album_lookup(artist_id, album_id, album_lookup); let mut info = EntityMatches::album_lookup(artist_id, album, album_lookup);
info.push_cannot_have_mbid(); info.push_cannot_have_mbid();
info.push_manual_input_mbid(); info.push_manual_input_mbid();
info info