Functional feature

This commit is contained in:
Wojciech Kozlowski 2024-08-30 15:52:47 +02:00
parent 43ce06a5e8
commit 6d77e2155f
2 changed files with 166 additions and 50 deletions

View File

@ -91,34 +91,35 @@ impl IAppInteractBrowse for AppMachine<AppBrowse> {
} }
}; };
let arid = match artist.musicbrainz { let mut matches = vec![];
Some(ref mbid) => mbid.mbid(),
None => { match artist.musicbrainz {
return AppMachine::error(self.inner, "cannot fetch: missing artist MBID").into() 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![]; AppMachine::matches(self.inner, matches).into()
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()
} }
fn no_op(self) -> Self::APP { fn no_op(self) -> Self::APP {
@ -257,7 +258,15 @@ mod tests {
let public_matches = public.state.unwrap_matches(); 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!( assert_eq!(
public_matches.matches.as_ref().unwrap().album_ref().list, public_matches.matches.as_ref().unwrap().album_ref().list,
matches_1.as_slice() matches_1.as_slice()
@ -270,7 +279,15 @@ mod tests {
let public_matches = public.state.unwrap_matches(); 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!( assert_eq!(
public_matches.matches.as_ref().unwrap().album_ref().list, public_matches.matches.as_ref().unwrap().album_ref().list,
matches_4.as_slice() matches_4.as_slice()

View File

@ -1,20 +1,117 @@
use std::cmp; use std::cmp;
use musichoard::collection::album::Album; use musichoard::collection::{album::Album, artist::Artist};
use crate::tui::{ use crate::tui::{
app::{ app::{
machine::{App, AppInner, AppMachine}, machine::{App, AppInner, AppMachine},
AppPublic, AppPublicAlbumMatches, AppPublicMatches, AppPublicMatchesKind, AppState, AppPublic, AppPublicAlbumMatches, AppPublicArtistMatches, AppPublicMatches,
IAppInteractMatches, WidgetState, AppPublicMatchesKind, AppState, IAppInteractMatches, WidgetState,
}, },
lib::interface::musicbrainz::Match, lib::interface::musicbrainz::Match,
}; };
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct AppMatchesInfo { pub struct AppArtistMatchesInfo {
pub matching: Artist,
pub list: Vec<Match<Artist>>,
}
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 matching: Album,
pub matches: Vec<Match<Album>>, pub list: Vec<Match<Album>>,
}
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<Match<Artist>>) -> Self {
AppMatchesInfo::Artist(AppArtistMatchesInfo { matching, list })
}
pub fn album(matching: Album, list: Vec<Match<Album>>) -> 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 { pub struct AppMatches {
@ -29,7 +126,7 @@ impl AppMachine<AppMatches> {
let mut state = WidgetState::default(); let mut state = WidgetState::default();
if let Some(matches_info) = matches_info_vec.first() { if let Some(matches_info) = matches_info_vec.first() {
index = Some(0); index = Some(0);
if !matches_info.matches.is_empty() { if !matches_info.is_empty() {
state.list.select(Some(0)); state.list.select(Some(0));
} }
} }
@ -53,12 +150,10 @@ impl From<AppMachine<AppMatches>> for App {
impl<'a> From<&'a mut AppMachine<AppMatches>> for AppPublic<'a> { impl<'a> From<&'a mut AppMachine<AppMatches>> for AppPublic<'a> {
fn from(machine: &'a mut AppMachine<AppMatches>) -> Self { fn from(machine: &'a mut AppMachine<AppMatches>) -> Self {
let matches = machine.state.index.map(|index| { let matches = machine
AppPublicMatchesKind::Album(AppPublicAlbumMatches { .state
matching: &machine.state.matches_info_vec[index].matching, .index
list: machine.state.matches_info_vec[index].matches.as_slice(), .map(|index| (&machine.state.matches_info_vec[index]).into());
})
});
AppPublic { AppPublic {
inner: (&mut machine.inner).into(), inner: (&mut machine.inner).into(),
@ -88,7 +183,6 @@ impl IAppInteractMatches for AppMachine<AppMatches> {
let to = cmp::min( let to = cmp::min(
result, result,
self.state.matches_info_vec[self.state.index.unwrap()] self.state.matches_info_vec[self.state.index.unwrap()]
.matches
.len() .len()
.saturating_sub(1), .saturating_sub(1),
); );
@ -103,7 +197,7 @@ impl IAppInteractMatches for AppMachine<AppMatches> {
self.state.state = WidgetState::default(); self.state.state = WidgetState::default();
if let Some(index) = self.state.index { if let Some(index) = self.state.index {
if let Some(matches_info) = self.state.matches_info_vec.get(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)); self.state.state.list.select(Some(0));
} }
return self.into(); return self.into();
@ -157,10 +251,10 @@ mod tests {
disambiguation: None, disambiguation: None,
}; };
let matches_info_1 = AppMatchesInfo { let matches_info_1 = AppMatchesInfo::Album(AppAlbumMatchesInfo {
matching: album_1.clone(), 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( let album_2 = Album::new(
AlbumId::new("Album 2"), AlbumId::new("Album 2"),
@ -176,10 +270,10 @@ mod tests {
disambiguation: None, disambiguation: None,
}; };
let matches_info_2 = AppMatchesInfo { let matches_info_2 = AppMatchesInfo::Album(AppAlbumMatchesInfo {
matching: album_2.clone(), 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] vec![matches_info_1, matches_info_2]
} }
@ -219,12 +313,17 @@ mod tests {
let public_matches = public.state.unwrap_matches(); let public_matches = public.state.unwrap_matches();
assert_eq!( assert_eq!(
public_matches.matches.as_ref().unwrap().album_ref().matching, public_matches
&matches_info_vec[0].matching .matches
.as_ref()
.unwrap()
.album_ref()
.matching,
&matches_info_vec[0].album_ref().matching
); );
assert_eq!( assert_eq!(
public_matches.matches.as_ref().unwrap().album_ref().list, 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); assert_eq!(public_matches.state, &widget_state);
} }