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 {
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()

View File

@ -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<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 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 {
@ -29,7 +126,7 @@ impl AppMachine<AppMatches> {
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<AppMachine<AppMatches>> for App {
impl<'a> From<&'a mut AppMachine<AppMatches>> for AppPublic<'a> {
fn from(machine: &'a mut AppMachine<AppMatches>) -> 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<AppMatches> {
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<AppMatches> {
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);
}