From 6d77e2155fd12c6cddb30f871c67879866c75243 Mon Sep 17 00:00:00 2001 From: Wojciech Kozlowski Date: Fri, 30 Aug 2024 15:52:47 +0200 Subject: [PATCH] Functional feature --- src/tui/app/machine/browse.rs | 71 ++++++++++------ src/tui/app/machine/matches.rs | 145 +++++++++++++++++++++++++++------ 2 files changed, 166 insertions(+), 50 deletions(-) diff --git a/src/tui/app/machine/browse.rs b/src/tui/app/machine/browse.rs index 67c9b12..dec034c 100644 --- a/src/tui/app/machine/browse.rs +++ b/src/tui/app/machine/browse.rs @@ -91,34 +91,35 @@ impl IAppInteractBrowse for AppMachine { } }; - let arid = match artist.musicbrainz { - Some(ref mbid) => mbid.mbid(), - None => { - return AppMachine::error(self.inner, "cannot fetch: missing artist MBID").into() + let mut matches = vec![]; + + match artist.musicbrainz { + Some(ref mbid) => { + let arid = mbid.mbid(); + + let mut album_iter = artist.albums.iter().peekable(); + while let Some(album) = album_iter.next() { + if album.musicbrainz.is_some() { + continue; + } + + match self.inner.musicbrainz.search_release_group(arid, album) { + Ok(list) => matches.push(AppMatchesInfo::album(album.clone(), list)), + Err(err) => return AppMachine::error(self.inner, err.to_string()).into(), + } + + if album_iter.peek().is_some() { + thread::sleep(time::Duration::from_secs(1)); + } + } } + None => match self.inner.musicbrainz.search_artist(&artist.id.name) { + Ok(list) => matches.push(AppMatchesInfo::artist(artist.clone(), list)), + Err(err) => return AppMachine::error(self.inner, err.to_string()).into(), + }, }; - let mut artist_album_matches = vec![]; - let mut album_iter = artist.albums.iter().peekable(); - while let Some(album) = album_iter.next() { - if album.musicbrainz.is_some() { - continue; - } - - match self.inner.musicbrainz.search_release_group(arid, album) { - Ok(matches) => artist_album_matches.push(AppMatchesInfo { - matching: album.clone(), - matches, - }), - Err(err) => return AppMachine::error(self.inner, err.to_string()).into(), - } - - if album_iter.peek().is_some() { - thread::sleep(time::Duration::from_secs(1)); - } - } - - AppMachine::matches(self.inner, artist_album_matches).into() + AppMachine::matches(self.inner, matches).into() } fn no_op(self) -> Self::APP { @@ -257,7 +258,15 @@ mod tests { let public_matches = public.state.unwrap_matches(); - assert_eq!(public_matches.matches.as_ref().unwrap().album_ref().matching, &album_1); + assert_eq!( + public_matches + .matches + .as_ref() + .unwrap() + .album_ref() + .matching, + &album_1 + ); assert_eq!( public_matches.matches.as_ref().unwrap().album_ref().list, matches_1.as_slice() @@ -270,7 +279,15 @@ mod tests { let public_matches = public.state.unwrap_matches(); - assert_eq!(public_matches.matches.as_ref().unwrap().album_ref().matching, &album_4); + assert_eq!( + public_matches + .matches + .as_ref() + .unwrap() + .album_ref() + .matching, + &album_4 + ); assert_eq!( public_matches.matches.as_ref().unwrap().album_ref().list, matches_4.as_slice() diff --git a/src/tui/app/machine/matches.rs b/src/tui/app/machine/matches.rs index 663c228..8bf4ce5 100644 --- a/src/tui/app/machine/matches.rs +++ b/src/tui/app/machine/matches.rs @@ -1,20 +1,117 @@ use std::cmp; -use musichoard::collection::album::Album; +use musichoard::collection::{album::Album, artist::Artist}; use crate::tui::{ app::{ machine::{App, AppInner, AppMachine}, - AppPublic, AppPublicAlbumMatches, AppPublicMatches, AppPublicMatchesKind, AppState, - IAppInteractMatches, WidgetState, + AppPublic, AppPublicAlbumMatches, AppPublicArtistMatches, AppPublicMatches, + AppPublicMatchesKind, AppState, IAppInteractMatches, WidgetState, }, lib::interface::musicbrainz::Match, }; #[derive(Clone, Debug, PartialEq, Eq)] -pub struct AppMatchesInfo { +pub struct AppArtistMatchesInfo { + pub matching: Artist, + pub list: Vec>, +} + +impl AppArtistMatchesInfo { + fn is_empty(&self) -> bool { + self.list.is_empty() + } + + fn len(&self) -> usize { + self.list.len() + } +} + +impl<'app> From<&'app AppArtistMatchesInfo> for AppPublicArtistMatches<'app> { + fn from(value: &'app AppArtistMatchesInfo) -> Self { + AppPublicArtistMatches { + matching: &value.matching, + list: &value.list, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AppAlbumMatchesInfo { pub matching: Album, - pub matches: Vec>, + pub list: Vec>, +} + +impl AppAlbumMatchesInfo { + fn is_empty(&self) -> bool { + self.list.is_empty() + } + + fn len(&self) -> usize { + self.list.len() + } +} + +impl<'app> From<&'app AppAlbumMatchesInfo> for AppPublicAlbumMatches<'app> { + fn from(value: &'app AppAlbumMatchesInfo) -> Self { + AppPublicAlbumMatches { + matching: &value.matching, + list: &value.list, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AppMatchesInfo { + Artist(AppArtistMatchesInfo), + Album(AppAlbumMatchesInfo), +} + +impl AppMatchesInfo { + fn is_empty(&self) -> bool { + match self { + Self::Artist(a) => a.is_empty(), + Self::Album(a) => a.is_empty(), + } + } + + fn len(&self) -> usize { + match self { + Self::Artist(a) => a.len(), + Self::Album(a) => a.len(), + } + } + + fn artist_ref(&self) -> &AppArtistMatchesInfo { + match self { + Self::Artist(a) => a, + Self::Album(_) => panic!(), + } + } + + fn album_ref(&self) -> &AppAlbumMatchesInfo { + match self { + Self::Album(a) => a, + Self::Artist(_) => panic!(), + } + } + + pub fn artist(matching: Artist, list: Vec>) -> Self { + AppMatchesInfo::Artist(AppArtistMatchesInfo { matching, list }) + } + + pub fn album(matching: Album, list: Vec>) -> Self { + AppMatchesInfo::Album(AppAlbumMatchesInfo { matching, list }) + } +} + +impl<'app> From<&'app AppMatchesInfo> for AppPublicMatchesKind<'app> { + fn from(value: &'app AppMatchesInfo) -> Self { + match value { + AppMatchesInfo::Artist(a) => AppPublicMatchesKind::Artist(a.into()), + AppMatchesInfo::Album(a) => AppPublicMatchesKind::Album(a.into()), + } + } } pub struct AppMatches { @@ -29,7 +126,7 @@ impl AppMachine { let mut state = WidgetState::default(); if let Some(matches_info) = matches_info_vec.first() { index = Some(0); - if !matches_info.matches.is_empty() { + if !matches_info.is_empty() { state.list.select(Some(0)); } } @@ -53,12 +150,10 @@ impl From> for App { impl<'a> From<&'a mut AppMachine> for AppPublic<'a> { fn from(machine: &'a mut AppMachine) -> Self { - let matches = machine.state.index.map(|index| { - AppPublicMatchesKind::Album(AppPublicAlbumMatches { - matching: &machine.state.matches_info_vec[index].matching, - list: machine.state.matches_info_vec[index].matches.as_slice(), - }) - }); + let matches = machine + .state + .index + .map(|index| (&machine.state.matches_info_vec[index]).into()); AppPublic { inner: (&mut machine.inner).into(), @@ -88,7 +183,6 @@ impl IAppInteractMatches for AppMachine { let to = cmp::min( result, self.state.matches_info_vec[self.state.index.unwrap()] - .matches .len() .saturating_sub(1), ); @@ -103,7 +197,7 @@ impl IAppInteractMatches for AppMachine { self.state.state = WidgetState::default(); if let Some(index) = self.state.index { if let Some(matches_info) = self.state.matches_info_vec.get(index) { - if !matches_info.matches.is_empty() { + if !matches_info.is_empty() { self.state.state.list.select(Some(0)); } return self.into(); @@ -157,10 +251,10 @@ mod tests { disambiguation: None, }; - let matches_info_1 = AppMatchesInfo { + let matches_info_1 = AppMatchesInfo::Album(AppAlbumMatchesInfo { matching: album_1.clone(), - matches: vec![album_match_1_1.clone(), album_match_1_2.clone()], - }; + list: vec![album_match_1_1.clone(), album_match_1_2.clone()], + }); let album_2 = Album::new( AlbumId::new("Album 2"), @@ -176,10 +270,10 @@ mod tests { disambiguation: None, }; - let matches_info_2 = AppMatchesInfo { + let matches_info_2 = AppMatchesInfo::Album(AppAlbumMatchesInfo { matching: album_2.clone(), - matches: vec![album_match_2_1.clone()], - }; + list: vec![album_match_2_1.clone()], + }); vec![matches_info_1, matches_info_2] } @@ -219,12 +313,17 @@ mod tests { let public_matches = public.state.unwrap_matches(); assert_eq!( - public_matches.matches.as_ref().unwrap().album_ref().matching, - &matches_info_vec[0].matching + public_matches + .matches + .as_ref() + .unwrap() + .album_ref() + .matching, + &matches_info_vec[0].album_ref().matching ); assert_eq!( public_matches.matches.as_ref().unwrap().album_ref().list, - matches_info_vec[0].matches.as_slice() + matches_info_vec[0].album_ref().list.as_slice() ); assert_eq!(public_matches.state, &widget_state); }