diff --git a/examples/musicbrainz_api/browse.rs b/examples/musicbrainz_api/browse.rs index af8d968..20dcc5c 100644 --- a/examples/musicbrainz_api/browse.rs +++ b/examples/musicbrainz_api/browse.rs @@ -66,14 +66,10 @@ fn main() { println!("{rg:?}\n"); } - let offset = response.page.release_group_offset; let count = response.release_groups.len(); response_counts.push(count); - let total = response.page.release_group_count; - println!("Release group offset : {offset}"); println!("Release groups in this response: {count}"); - println!("Release groups in total : {total}"); match response.page.next_page_offset(count) { NextPage::Offset(next_offset) => paging.with_offset(next_offset), diff --git a/src/external/musicbrainz/api/browse.rs b/src/external/musicbrainz/api/browse.rs index 9d1a21d..dc63ed8 100644 --- a/src/external/musicbrainz/api/browse.rs +++ b/src/external/musicbrainz/api/browse.rs @@ -16,8 +16,8 @@ use crate::{ #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)] #[serde(rename_all(deserialize = "kebab-case"))] pub struct BrowseReleaseGroupPage { - pub release_group_offset: usize, - pub release_group_count: usize, + release_group_offset: usize, + release_group_count: usize, } impl BrowseReleaseGroupPage { diff --git a/src/external/musicbrainz/api/mod.rs b/src/external/musicbrainz/api/mod.rs index 2f61f75..9f374da 100644 --- a/src/external/musicbrainz/api/mod.rs +++ b/src/external/musicbrainz/api/mod.rs @@ -63,8 +63,8 @@ impl MusicBrainzClient { #[derive(Default)] pub struct PageSettings { - pub limit: Option, - pub offset: Option, + limit: Option, + offset: Option, } impl PageSettings { diff --git a/src/external/musicbrainz/api/search/mod.rs b/src/external/musicbrainz/api/search/mod.rs index 6fcb4cc..f76c010 100644 --- a/src/external/musicbrainz/api/search/mod.rs +++ b/src/external/musicbrainz/api/search/mod.rs @@ -28,8 +28,8 @@ use super::NextPage; #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)] #[serde(rename_all(deserialize = "kebab-case"))] pub struct SearchPage { - pub offset: usize, - pub count: usize, + offset: usize, + count: usize, } impl SearchPage { diff --git a/src/tui/app/machine/fetch_state.rs b/src/tui/app/machine/fetch_state.rs index c8a15b3..94aaefc 100644 --- a/src/tui/app/machine/fetch_state.rs +++ b/src/tui/app/machine/fetch_state.rs @@ -290,11 +290,7 @@ mod tests { machine::tests::{inner, music_hoard}, Delta, IApp, IAppAccess, IAppInteractBrowse, MatchOption, MatchStateInfo, }, - lib::interface::musicbrainz::{ - self, - api::{Lookup, Match}, - daemon::MockIMbJobSender, - }, + lib::interface::musicbrainz::{self, api::Match, daemon::MockIMbJobSender}, testmod::COLLECTION, }; @@ -320,7 +316,7 @@ mod tests { assert_eq!(fetch.try_recv(), Err(TryRecvError::Empty)); - let lookup = Lookup::new(artist.clone()); + let lookup = Match::item(artist.clone()); let lookup_result = MatchStateInfo::artist_lookup(artist.clone(), lookup); lookup_tx.send(Ok(lookup_result.clone())).unwrap(); @@ -605,7 +601,7 @@ mod tests { let (tx, rx) = mpsc::channel::(); let artist = COLLECTION[3].meta.clone(); - let artist_match = Match::new(80, COLLECTION[2].meta.clone()); + let artist_match = Match::with_score(COLLECTION[2].meta.clone(), 80); let artist_match_info = MatchStateInfo::artist_search(artist.clone(), vec![artist_match.clone()]); let fetch_result = Ok(artist_match_info); diff --git a/src/tui/app/machine/match_state.rs b/src/tui/app/machine/match_state.rs index 19e0b3c..844c862 100644 --- a/src/tui/app/machine/match_state.rs +++ b/src/tui/app/machine/match_state.rs @@ -6,13 +6,10 @@ use musichoard::collection::{ musicbrainz::{MbRefOption, Mbid}, }; -use crate::tui::{ - app::{ - machine::{fetch_state::FetchState, input::Input, App, AppInner, AppMachine}, - AlbumMatches, AppPublicState, AppState, ArtistMatches, Delta, IAppInteractMatch, - ListOption, MatchOption, MatchStateInfo, MatchStatePublic, WidgetState, - }, - lib::interface::musicbrainz::api::{Lookup, Match}, +use crate::tui::app::{ + machine::{fetch_state::FetchState, input::Input, App, AppInner, AppMachine}, + AlbumMatches, AppPublicState, AppState, ArtistMatches, Delta, IAppInteractMatch, MatchOption, + MatchStateInfo, MatchStatePublic, WidgetState, }; trait GetInfoMeta { @@ -27,7 +24,7 @@ impl GetInfoMeta for AlbumMeta { trait GetInfo { type InfoType; - fn into_info(self, info: Self::InfoType) -> InfoOption; + fn into_info(&self, info: Self::InfoType) -> InfoOption; } enum InfoOption { @@ -35,86 +32,46 @@ enum InfoOption { NeedInput, } -macro_rules! impl_match_option_artist_into_info { - ($holder:ident) => { - impl GetInfo for MatchOption<$holder> { - type InfoType = ArtistInfo; +impl GetInfo for MatchOption { + type InfoType = ArtistInfo; - fn into_info(self, mut info: Self::InfoType) -> InfoOption { - match self { - MatchOption::Some(option) => info.musicbrainz = option.item.info.musicbrainz, - MatchOption::CannotHaveMbid => info.musicbrainz = MbRefOption::CannotHaveMbid, - MatchOption::ManualInputMbid => return InfoOption::NeedInput, - } - InfoOption::Info(info) - } + fn into_info(&self, mut info: Self::InfoType) -> InfoOption { + match self { + MatchOption::Some(option) => info.musicbrainz = option.item.info.musicbrainz.clone(), + MatchOption::CannotHaveMbid => info.musicbrainz = MbRefOption::CannotHaveMbid, + MatchOption::ManualInputMbid => return InfoOption::NeedInput, } - }; + InfoOption::Info(info) + } } -impl_match_option_artist_into_info!(Match); -impl_match_option_artist_into_info!(Lookup); +impl GetInfo for MatchOption { + type InfoType = AlbumInfo; -macro_rules! impl_match_option_album_into_info { - ($holder:ident) => { - impl GetInfo for MatchOption<$holder> { - type InfoType = AlbumInfo; - - fn into_info(self, mut info: Self::InfoType) -> InfoOption { - match self { - MatchOption::Some(option) => info = option.item.info, - MatchOption::CannotHaveMbid => info.musicbrainz = MbRefOption::CannotHaveMbid, - MatchOption::ManualInputMbid => return InfoOption::NeedInput, - } - InfoOption::Info(info) - } - } - }; -} - -impl_match_option_album_into_info!(Match); -impl_match_option_album_into_info!(Lookup); - -impl ListOption { - fn len(&self) -> usize { + fn into_info(&self, mut info: Self::InfoType) -> InfoOption { match self { - ListOption::Lookup(list) => list.len(), - ListOption::Search(list) => list.len(), - } - } - - fn push_cannot_have_mbid(&mut self) { - match self { - ListOption::Lookup(list) => list.push(MatchOption::CannotHaveMbid), - ListOption::Search(list) => list.push(MatchOption::CannotHaveMbid), - } - } - - fn push_manual_input_mbid(&mut self) { - match self { - ListOption::Lookup(list) => list.push(MatchOption::ManualInputMbid), - ListOption::Search(list) => list.push(MatchOption::ManualInputMbid), + MatchOption::Some(option) => info = option.item.info.clone(), + MatchOption::CannotHaveMbid => info.musicbrainz = MbRefOption::CannotHaveMbid, + MatchOption::ManualInputMbid => return InfoOption::NeedInput, } + InfoOption::Info(info) } } trait ExtractInfo { type InfoType; - fn extract_info(&mut self, index: usize, info: Self::InfoType) -> InfoOption; + fn extract_info(&self, index: usize, info: Self::InfoType) -> InfoOption; } -impl ExtractInfo for ListOption +impl ExtractInfo for Vec> where - MatchOption>: GetInfo, - MatchOption>: GetInfo, + MatchOption: GetInfo, + MatchOption: GetInfo, { type InfoType = T::InfoType; - fn extract_info(&mut self, index: usize, info: Self::InfoType) -> InfoOption { - match self { - ListOption::Lookup(ref mut list) => list.swap_remove(index).into_info(info), - ListOption::Search(ref mut list) => list.swap_remove(index).into_info(info), - } + fn extract_info(&self, index: usize, info: Self::InfoType) -> InfoOption { + self.get(index).unwrap().into_info(info) } } @@ -124,11 +81,11 @@ impl ArtistMatches { } fn push_cannot_have_mbid(&mut self) { - self.list.push_cannot_have_mbid(); + self.list.push(MatchOption::CannotHaveMbid); } fn push_manual_input_mbid(&mut self) { - self.list.push_manual_input_mbid(); + self.list.push(MatchOption::ManualInputMbid); } } @@ -138,11 +95,11 @@ impl AlbumMatches { } fn push_cannot_have_mbid(&mut self) { - self.list.push_cannot_have_mbid(); + self.list.push(MatchOption::CannotHaveMbid); } fn push_manual_input_mbid(&mut self) { - self.list.push_manual_input_mbid(); + self.list.push(MatchOption::ManualInputMbid); } } @@ -314,7 +271,7 @@ mod tests { IApp, IAppAccess, IAppInput, }, lib::interface::musicbrainz::{ - api::{Lookup, Match}, + api::Match, daemon::{MbParams, MockIMbJobSender}, }, }; @@ -322,18 +279,9 @@ mod tests { use super::*; impl Match { - pub fn new(score: u8, item: T) -> Self { + pub fn with_score(item: T, score: u8) -> Self { Match { - score, - item, - disambiguation: None, - } - } - } - - impl Lookup { - pub fn new(item: T) -> Self { - Lookup { + score: Some(score), item, disambiguation: None, } @@ -354,10 +302,10 @@ mod tests { let artist = artist_meta(); let artist_1 = artist.clone(); - let artist_match_1 = Match::new(100, artist_1); + let artist_match_1 = Match::with_score(artist_1, 100); let artist_2 = artist.clone(); - let mut artist_match_2 = Match::new(100, artist_2); + let mut artist_match_2 = Match::with_score(artist_2, 100); artist_match_2.disambiguation = Some(String::from("some disambiguation")); let list = vec![artist_match_1.clone(), artist_match_2.clone()]; @@ -366,7 +314,7 @@ mod tests { fn artist_lookup() -> MatchStateInfo { let artist = artist_meta(); - let lookup = Lookup::new(artist.clone()); + let lookup = Match::item(artist.clone()); MatchStateInfo::artist_lookup(artist, lookup) } @@ -387,12 +335,12 @@ mod tests { let album = album_meta(); let album_1 = album.clone(); - let album_match_1 = Match::new(100, album_1); + let album_match_1 = Match::with_score(album_1, 100); let mut album_2 = album.clone(); album_2.id.title.push_str(" extra title part"); album_2.info.secondary_types.pop(); - let album_match_2 = Match::new(100, album_2); + let album_match_2 = Match::with_score(album_2, 100); let list = vec![album_match_1.clone(), album_match_2.clone()]; MatchStateInfo::album_search(artist_id, album, list) @@ -401,7 +349,7 @@ mod tests { fn album_lookup() -> MatchStateInfo { let artist_id = ArtistId::new("Artist"); let album = album_meta(); - let lookup = Lookup::new(album.clone()); + let lookup = Match::item(album.clone()); MatchStateInfo::album_lookup(artist_id, album, lookup) } diff --git a/src/tui/app/machine/mod.rs b/src/tui/app/machine/mod.rs index 9873c25..6747d01 100644 --- a/src/tui/app/machine/mod.rs +++ b/src/tui/app/machine/mod.rs @@ -226,10 +226,7 @@ mod tests { use crate::tui::{ app::{AppState, IApp, IAppInput, IAppInteractBrowse, InputEvent, MatchStateInfo}, - lib::{ - interface::musicbrainz::{api::Lookup, daemon::MockIMbJobSender}, - MockIMusicHoard, - }, + lib::{interface::musicbrainz::{api::Match, daemon::MockIMbJobSender}, MockIMusicHoard}, }; use super::*; @@ -520,7 +517,7 @@ mod tests { let (_, rx) = mpsc::channel(); let fetch = FetchState::new(rx); let artist = ArtistMeta::new(ArtistId::new("Artist")); - let info = MatchStateInfo::artist_lookup(artist.clone(), Lookup::new(artist.clone())); + let info = MatchStateInfo::artist_lookup(artist.clone(), Match::item(artist.clone())); app = AppMachine::match_state(app.unwrap_browse().inner, MatchState::new(info, fetch)).into(); diff --git a/src/tui/app/mod.rs b/src/tui/app/mod.rs index e7bd921..637c8df 100644 --- a/src/tui/app/mod.rs +++ b/src/tui/app/mod.rs @@ -37,8 +37,6 @@ macro_rules! IAppState { } use IAppState; -use super::lib::interface::musicbrainz::api::Lookup; - pub trait IApp { type BrowseState: IAppBase + IAppInteractBrowse; type InfoState: IAppBase + IAppInteractInfo; @@ -217,19 +215,13 @@ pub type InputPublic<'app> = &'app tui_input::Input; #[derive(Clone, Debug, PartialEq, Eq)] pub enum MatchOption { - Some(T), + Some(Match), CannotHaveMbid, ManualInputMbid, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum ListOption { - Search(Vec>>), - Lookup(Vec>>), -} - -impl From for MatchOption { - fn from(value: T) -> Self { +impl From> for MatchOption { + fn from(value: Match) -> Self { MatchOption::Some(value) } } @@ -237,14 +229,14 @@ impl From for MatchOption { #[derive(Clone, Debug, PartialEq, Eq)] pub struct ArtistMatches { pub matching: ArtistMeta, - pub list: ListOption, + pub list: Vec>, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct AlbumMatches { pub artist: ArtistId, pub matching: AlbumMeta, - pub list: ListOption, + pub list: Vec>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -254,20 +246,20 @@ pub enum MatchStateInfo { } impl MatchStateInfo { - pub fn artist_search>>>( + pub fn artist_search>>( matching: ArtistMeta, list: Vec, ) -> Self { - let list = ListOption::Search(list.into_iter().map(Into::into).collect()); + let list = list.into_iter().map(Into::into).collect(); MatchStateInfo::Artist(ArtistMatches { matching, list }) } - pub fn album_search>>>( + pub fn album_search>>( artist: ArtistId, matching: AlbumMeta, list: Vec, ) -> Self { - let list = ListOption::Search(list.into_iter().map(Into::into).collect()); + let list = list.into_iter().map(Into::into).collect(); MatchStateInfo::Album(AlbumMatches { artist, matching, @@ -275,20 +267,17 @@ impl MatchStateInfo { }) } - pub fn artist_lookup>>>( - matching: ArtistMeta, - item: M, - ) -> Self { - let list = ListOption::Lookup(vec![item.into()]); + pub fn artist_lookup>>(matching: ArtistMeta, item: M) -> Self { + let list = vec![item.into()]; MatchStateInfo::Artist(ArtistMatches { matching, list }) } - pub fn album_lookup>>>( + pub fn album_lookup>>( artist: ArtistId, matching: AlbumMeta, item: M, ) -> Self { - let list = ListOption::Lookup(vec![item.into()]); + let list = vec![item.into()]; MatchStateInfo::Album(AlbumMatches { artist, matching, diff --git a/src/tui/lib/external/musicbrainz/api/mod.rs b/src/tui/lib/external/musicbrainz/api/mod.rs index 35156c5..6724289 100644 --- a/src/tui/lib/external/musicbrainz/api/mod.rs +++ b/src/tui/lib/external/musicbrainz/api/mod.rs @@ -10,6 +10,7 @@ use musichoard::{ }, external::musicbrainz::{ api::{ + browse::{BrowseReleaseGroupRequest, BrowseReleaseGroupResponse}, lookup::{ LookupArtistRequest, LookupArtistResponse, LookupReleaseGroupRequest, LookupReleaseGroupResponse, @@ -18,13 +19,13 @@ use musichoard::{ SearchArtistRequest, SearchArtistResponseArtist, SearchReleaseGroupRequest, SearchReleaseGroupResponseReleaseGroup, }, - MusicBrainzClient, PageSettings, + MbArtistMeta, MbReleaseGroupMeta, MusicBrainzClient, PageSettings, }, IMusicBrainzHttp, }, }; -use crate::tui::lib::interface::musicbrainz::api::{Error, IMusicBrainz, Lookup, Match}; +use crate::tui::lib::interface::musicbrainz::api::{Error, IMusicBrainz, Match, Paged}; // GRCOV_EXCL_START pub struct MusicBrainz { @@ -38,7 +39,7 @@ impl MusicBrainz { } impl IMusicBrainz for MusicBrainz { - fn lookup_artist(&mut self, mbid: &Mbid) -> Result, Error> { + fn lookup_artist(&mut self, mbid: &Mbid) -> Result, Error> { let request = LookupArtistRequest::new(mbid); let mb_response = self.client.lookup_artist(&request)?; @@ -46,7 +47,7 @@ impl IMusicBrainz for MusicBrainz { Ok(from_lookup_artist_response(mb_response)) } - fn lookup_release_group(&mut self, mbid: &Mbid) -> Result, Error> { + fn lookup_release_group(&mut self, mbid: &Mbid) -> Result, Error> { let request = LookupReleaseGroupRequest::new(mbid); let mb_response = self.client.lookup_release_group(&request)?; @@ -92,52 +93,75 @@ impl IMusicBrainz for MusicBrainz { .map(from_search_release_group_response_release_group) .collect()) } -} -fn from_lookup_artist_response(entity: LookupArtistResponse) -> Lookup { - let sort = Some(entity.meta.sort_name).filter(|s| s != &entity.meta.name); - Lookup { - item: ArtistMeta { - id: entity.meta.name.into(), - sort, - info: ArtistInfo { - musicbrainz: MbRefOption::Some(entity.meta.id.into()), - properties: HashMap::new(), - }, - }, - disambiguation: entity.meta.disambiguation, + fn browse_release_group( + &mut self, + artist: &Mbid, + paging: &mut PageSettings, + ) -> Result>>, Error> { + let request = BrowseReleaseGroupRequest::artist(artist); + + let mb_response = self.client.browse_release_group(&request, paging)?; + + let page_count = mb_response.release_groups.len(); + let next = mb_response.page.next_page_offset(page_count); + let item = from_browse_release_group_response(mb_response); + + Ok(Paged { item, next }) } } -fn from_lookup_release_group_response(entity: LookupReleaseGroupResponse) -> Lookup { - Lookup { - item: AlbumMeta { - id: entity.meta.title.into(), - date: entity.meta.first_release_date, - seq: AlbumSeq::default(), - info: AlbumInfo { - musicbrainz: MbRefOption::Some(entity.meta.id.into()), - primary_type: entity.meta.primary_type, - secondary_types: entity.meta.secondary_types.unwrap_or_default(), +fn from_mb_artist_meta(meta: MbArtistMeta) -> (ArtistMeta, Option) { + let sort = Some(meta.sort_name).filter(|s| s != &meta.name); + ( + ArtistMeta { + id: meta.name.into(), + sort, + info: ArtistInfo { + musicbrainz: MbRefOption::Some(meta.id.into()), + properties: HashMap::new(), }, }, + meta.disambiguation, + ) +} + +fn from_mb_release_group_meta(meta: MbReleaseGroupMeta) -> AlbumMeta { + AlbumMeta { + id: meta.title.into(), + date: meta.first_release_date, + seq: AlbumSeq::default(), + info: AlbumInfo { + musicbrainz: MbRefOption::Some(meta.id.into()), + primary_type: meta.primary_type, + secondary_types: meta.secondary_types.unwrap_or_default(), + }, + } +} + +fn from_lookup_artist_response(entity: LookupArtistResponse) -> Match { + let (item, disambiguation) = from_mb_artist_meta(entity.meta); + Match { + score: None, + item, + disambiguation, + } +} + +fn from_lookup_release_group_response(entity: LookupReleaseGroupResponse) -> Match { + Match { + score: None, + item: from_mb_release_group_meta(entity.meta), disambiguation: None, } } fn from_search_artist_response_artist(entity: SearchArtistResponseArtist) -> Match { - let sort = Some(entity.meta.sort_name).filter(|s| s != &entity.meta.name); + let (item, disambiguation) = from_mb_artist_meta(entity.meta); Match { - score: entity.score, - item: ArtistMeta { - id: entity.meta.name.into(), - sort, - info: ArtistInfo { - musicbrainz: MbRefOption::Some(entity.meta.id.into()), - properties: HashMap::new(), - }, - }, - disambiguation: entity.meta.disambiguation, + score: Some(entity.score), + item, + disambiguation, } } @@ -145,18 +169,17 @@ fn from_search_release_group_response_release_group( entity: SearchReleaseGroupResponseReleaseGroup, ) -> Match { Match { - score: entity.score, - item: AlbumMeta { - id: entity.meta.title.into(), - date: entity.meta.first_release_date, - seq: AlbumSeq::default(), - info: AlbumInfo { - musicbrainz: MbRefOption::Some(entity.meta.id.into()), - primary_type: entity.meta.primary_type, - secondary_types: entity.meta.secondary_types.unwrap_or_default(), - }, - }, + score: Some(entity.score), + item: from_mb_release_group_meta(entity.meta), disambiguation: None, } } + +fn from_browse_release_group_response( + entity: BrowseReleaseGroupResponse, +) -> Vec> { + let rgs = entity.release_groups.into_iter(); + let metas = rgs.map(from_mb_release_group_meta); + metas.map(|meta| Match::item(meta)).collect() +} // GRCOV_EXCL_STOP diff --git a/src/tui/lib/external/musicbrainz/daemon/mod.rs b/src/tui/lib/external/musicbrainz/daemon/mod.rs index dc75fd2..b793322 100644 --- a/src/tui/lib/external/musicbrainz/daemon/mod.rs +++ b/src/tui/lib/external/musicbrainz/daemon/mod.rs @@ -321,7 +321,7 @@ mod tests { use crate::tui::{ event::{Event, EventError, MockIFetchCompleteEventSender}, - lib::interface::musicbrainz::api::{Lookup, Match, MockIMusicBrainz}, + lib::interface::musicbrainz::api::{Match, MockIMusicBrainz}, testmod::COLLECTION, }; @@ -411,8 +411,8 @@ mod tests { fn search_artist_expectations() -> (ArtistMeta, Vec>) { let artist = COLLECTION[3].meta.clone(); - let artist_match_1 = Match::new(100, artist.clone()); - let artist_match_2 = Match::new(50, artist.clone()); + let artist_match_1 = Match::with_score(artist.clone(), 100); + let artist_match_2 = Match::with_score(artist.clone(), 50); let matches = vec![artist_match_1.clone(), artist_match_2.clone()]; (artist, matches) @@ -445,8 +445,8 @@ mod tests { let album_1 = COLLECTION[1].albums[0].meta.clone(); let album_4 = COLLECTION[1].albums[3].meta.clone(); - let album_match_1_1 = Match::new(100, album_1.clone()); - let album_match_1_2 = Match::new(50, album_4.clone()); + let album_match_1_1 = Match::with_score(album_1.clone(), 100); + let album_match_1_2 = Match::with_score(album_4.clone(), 50); let matches_1 = vec![album_match_1_1.clone(), album_match_1_2.clone()]; (album_1, matches_1) @@ -456,8 +456,8 @@ mod tests { let album_1 = COLLECTION[1].albums[0].meta.clone(); let album_4 = COLLECTION[1].albums[3].meta.clone(); - let album_match_4_1 = Match::new(100, album_4.clone()); - let album_match_4_2 = Match::new(30, album_1.clone()); + let album_match_4_1 = Match::with_score(album_4.clone(), 100); + let album_match_4_2 = Match::with_score(album_1.clone(), 30); let matches_4 = vec![album_match_4_1.clone(), album_match_4_2.clone()]; (album_4, matches_4) @@ -539,7 +539,7 @@ mod tests { fn lookup_artist_expectation( musicbrainz: &mut MockIMusicBrainz, mbid: &Mbid, - lookup: &Lookup, + lookup: &Match, ) { let result = Ok(lookup.clone()); musicbrainz @@ -554,7 +554,7 @@ mod tests { let mut musicbrainz = musicbrainz(); let mbid = mbid(); let artist = COLLECTION[3].meta.clone(); - let lookup = Lookup::new(artist.clone()); + let lookup = Match::item(artist.clone()); lookup_artist_expectation(&mut musicbrainz, &mbid, &lookup); let mut event_sender = event_sender(); @@ -581,7 +581,7 @@ mod tests { fn lookup_release_group_expectation( musicbrainz: &mut MockIMusicBrainz, mbid: &Mbid, - lookup: &Lookup, + lookup: &Match, ) { let result = Ok(lookup.clone()); musicbrainz @@ -596,7 +596,7 @@ mod tests { let mut musicbrainz = musicbrainz(); let mbid = mbid(); let album = COLLECTION[1].albums[0].meta.clone(); - let lookup = Lookup::new(album.clone()); + let lookup = Match::item(album.clone()); lookup_release_group_expectation(&mut musicbrainz, &mbid, &lookup); let mut event_sender = event_sender(); diff --git a/src/tui/lib/interface/musicbrainz/api/mod.rs b/src/tui/lib/interface/musicbrainz/api/mod.rs index 62917c0..2cc618b 100644 --- a/src/tui/lib/interface/musicbrainz/api/mod.rs +++ b/src/tui/lib/interface/musicbrainz/api/mod.rs @@ -3,32 +3,48 @@ #[cfg(test)] use mockall::automock; -use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta, musicbrainz::Mbid}; +use musichoard::{ + collection::{album::AlbumMeta, artist::ArtistMeta, musicbrainz::Mbid}, + external::musicbrainz::api::{NextPage, PageSettings}, +}; -/// Trait for interacting with the MusicBrainz API. #[cfg_attr(test, automock)] pub trait IMusicBrainz { - fn lookup_artist(&mut self, mbid: &Mbid) -> Result, Error>; - fn lookup_release_group(&mut self, mbid: &Mbid) -> Result, Error>; + fn lookup_artist(&mut self, mbid: &Mbid) -> Result, Error>; + fn lookup_release_group(&mut self, mbid: &Mbid) -> Result, Error>; fn search_artist(&mut self, artist: &ArtistMeta) -> Result>, Error>; fn search_release_group( &mut self, arid: &Mbid, album: &AlbumMeta, ) -> Result>, Error>; + fn browse_release_group( + &mut self, + artist: &Mbid, + paging: &mut PageSettings, + ) -> Result>>, Error>; } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Match { - pub score: u8, + pub score: Option, pub item: T, pub disambiguation: Option, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Lookup { +impl Match { + pub fn item(item: T) -> Self { + Match { + score: None, + item, + disambiguation: None, + } + } +} + +pub struct Paged { pub item: T, - pub disambiguation: Option, + pub next: NextPage, } pub type Error = musichoard::external::musicbrainz::api::Error; diff --git a/src/tui/ui/display.rs b/src/tui/ui/display.rs index 30c3950..cfc78df 100644 --- a/src/tui/ui/display.rs +++ b/src/tui/ui/display.rs @@ -5,10 +5,7 @@ use musichoard::collection::{ track::{TrackFormat, TrackQuality}, }; -use crate::tui::{ - app::{MatchOption, MatchStateInfo}, - lib::interface::musicbrainz::api::{Lookup, Match}, -}; +use crate::tui::app::{MatchOption, MatchStateInfo}; pub struct UiDisplay; @@ -129,23 +126,24 @@ impl UiDisplay { } } - pub fn display_search_option_artist(match_option: &MatchOption>) -> String { - match match_option { - MatchOption::Some(match_artist) => format!( - "{} ({}%)", - Self::display_option_artist(&match_artist.item, &match_artist.disambiguation), - match_artist.score, - ), - MatchOption::CannotHaveMbid => Self::display_cannot_have_mbid().to_string(), - MatchOption::ManualInputMbid => Self::display_manual_input_mbid().to_string(), - } + pub fn display_match_option_artist(match_option: &MatchOption) -> String { + Self::display_match_option(Self::display_option_artist, match_option) } - pub fn display_lookup_option_artist(lookup_option: &MatchOption>) -> String { - match lookup_option { - MatchOption::Some(match_artist) => { - Self::display_option_artist(&match_artist.item, &match_artist.disambiguation) - } + pub fn display_match_option_album(match_option: &MatchOption) -> String { + Self::display_match_option(Self::display_option_album, match_option) + } + + fn display_match_option(display_fn: Fn, match_option: &MatchOption) -> String + where + Fn: FnOnce(&T, &Option) -> String, + { + match match_option { + MatchOption::Some(match_artist) => format!( + "{}{}", + display_fn(&match_artist.item, &match_artist.disambiguation), + Self::display_option_score(match_artist.score), + ), MatchOption::CannotHaveMbid => Self::display_cannot_have_mbid().to_string(), MatchOption::ManualInputMbid => Self::display_manual_input_mbid().to_string(), } @@ -163,27 +161,7 @@ impl UiDisplay { ) } - pub fn display_search_option_album(match_option: &MatchOption>) -> String { - match match_option { - MatchOption::Some(match_album) => format!( - "{} ({}%)", - Self::display_option_album(&match_album.item), - match_album.score, - ), - MatchOption::CannotHaveMbid => Self::display_cannot_have_mbid().to_string(), - MatchOption::ManualInputMbid => Self::display_manual_input_mbid().to_string(), - } - } - - pub fn display_lookup_option_album(lookup_option: &MatchOption>) -> String { - match lookup_option { - MatchOption::Some(match_album) => Self::display_option_album(&match_album.item), - MatchOption::CannotHaveMbid => Self::display_cannot_have_mbid().to_string(), - MatchOption::ManualInputMbid => Self::display_manual_input_mbid().to_string(), - } - } - - fn display_option_album(album: &AlbumMeta) -> String { + fn display_option_album(album: &AlbumMeta, _disambiguation: &Option) -> String { format!( "{:010} | {} [{}]", UiDisplay::display_album_date(&album.date), @@ -192,6 +170,10 @@ impl UiDisplay { ) } + fn display_option_score(score: Option) -> String { + score.map(|s| format!(" ({s}%)")).unwrap_or_default() + } + fn display_cannot_have_mbid() -> &'static str { "-- Cannot have a MusicBrainz Identifier --" } diff --git a/src/tui/ui/match_state.rs b/src/tui/ui/match_state.rs index bf611fe..741ec26 100644 --- a/src/tui/ui/match_state.rs +++ b/src/tui/ui/match_state.rs @@ -2,7 +2,7 @@ use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta}; use ratatui::widgets::{List, ListItem}; use crate::tui::{ - app::{ListOption, MatchStateInfo, WidgetState}, + app::{MatchOption, MatchStateInfo, WidgetState}, ui::display::UiDisplay, }; @@ -22,19 +22,12 @@ impl<'a, 'b> MatchOverlay<'a, 'b> { fn artists( matching: &ArtistMeta, - matches: &'a ListOption, + matches: &'a Vec>, state: &'b mut WidgetState, ) -> Self { let matching = UiDisplay::display_artist_matching(matching); - let list = match matches { - ListOption::Search(matches) => { - Self::display_list(UiDisplay::display_search_option_artist, matches) - } - ListOption::Lookup(matches) => { - Self::display_list(UiDisplay::display_lookup_option_artist, matches) - } - }; + let list = Self::display_list(UiDisplay::display_match_option_artist, matches); MatchOverlay { matching, @@ -45,19 +38,12 @@ impl<'a, 'b> MatchOverlay<'a, 'b> { fn albums( matching: &AlbumMeta, - matches: &'a ListOption, + matches: &'a Vec>, state: &'b mut WidgetState, ) -> Self { let matching = UiDisplay::display_album_matching(matching); - let list = match matches { - ListOption::Search(matches) => { - Self::display_list(UiDisplay::display_search_option_album, matches) - } - ListOption::Lookup(matches) => { - Self::display_list(UiDisplay::display_lookup_option_album, matches) - } - }; + let list = Self::display_list(UiDisplay::display_match_option_album, matches); MatchOverlay { matching, diff --git a/src/tui/ui/mod.rs b/src/tui/ui/mod.rs index 5cf579c..4827a3d 100644 --- a/src/tui/ui/mod.rs +++ b/src/tui/ui/mod.rs @@ -204,7 +204,7 @@ mod tests { use crate::tui::{ app::{AppPublic, AppPublicInner, Delta, MatchStatePublic}, - lib::interface::musicbrainz::api::{Lookup, Match}, + lib::interface::musicbrainz::api::Match, testmod::COLLECTION, tests::terminal, }; @@ -331,7 +331,7 @@ mod tests { fn artist_matches() -> MatchStateInfo { let artist = artist_meta(); - let artist_match = Match::new(80, artist.clone()); + let artist_match = Match::with_score(artist.clone(), 80); let list = vec![artist_match.clone(), artist_match.clone()]; let mut info = MatchStateInfo::artist_search(artist, list); @@ -342,7 +342,7 @@ mod tests { fn artist_lookup() -> MatchStateInfo { let artist = artist_meta(); - let artist_lookup = Lookup::new(artist.clone()); + let artist_lookup = Match::item(artist.clone()); let mut info = MatchStateInfo::artist_lookup(artist, artist_lookup); info.push_cannot_have_mbid(); @@ -369,7 +369,7 @@ mod tests { fn album_matches() -> MatchStateInfo { let artist_id = album_artist_id(); let album = album_meta(); - let album_match = Match::new(80, album.clone()); + let album_match = Match::with_score(album.clone(), 80); let list = vec![album_match.clone(), album_match.clone()]; let mut info = MatchStateInfo::album_search(artist_id, album, list); @@ -381,7 +381,7 @@ mod tests { fn album_lookup() -> MatchStateInfo { let artist_id = album_artist_id(); let album = album_meta(); - let album_lookup = Lookup::new(album.clone()); + let album_lookup = Match::item(album.clone()); let mut info = MatchStateInfo::album_lookup(artist_id, album, album_lookup); info.push_cannot_have_mbid();