Add a "cannot-have-an-mbid" entry to possible matches #208

Merged
wojtek merged 2 commits from 190---add-a--cannot-have-an-mbid--entry-to-possible-matches into main 2024-08-31 16:29:37 +02:00
7 changed files with 175 additions and 208 deletions

View File

@ -3,9 +3,9 @@ use std::{sync::mpsc, thread, time};
use musichoard::collection::musicbrainz::IMusicBrainzRef; use musichoard::collection::musicbrainz::IMusicBrainzRef;
use crate::tui::app::{ use crate::tui::app::{
machine::{matches::AppMatchesInfo, App, AppInner, AppMachine}, machine::{App, AppInner, AppMachine},
selection::{Delta, ListSelection}, selection::{Delta, ListSelection},
AppPublic, AppState, IAppInteractBrowse, AppMatchesInfo, AppPublic, AppState, IAppInteractBrowse,
}; };
pub struct AppBrowse; pub struct AppBrowse;
@ -139,8 +139,8 @@ mod tests {
use crate::tui::{ use crate::tui::{
app::{ app::{
machine::tests::{inner, inner_with_mb, music_hoard}, machine::tests::{inner, inner_with_mb, music_hoard},
AppPublicAlbumMatches, AppPublicArtistMatches, AppPublicMatchesInfo, Category, AppAlbumMatches, AppArtistMatches, AppMatchesInfo, Category, IAppAccess, IAppInteract,
IAppAccess, IAppInteract, IAppInteractMatches, IAppInteractMatches, MatchOption,
}, },
lib::interface::musicbrainz::{self, Match, MockIMusicBrainz}, lib::interface::musicbrainz::{self, Match, MockIMusicBrainz},
testmod::COLLECTION, testmod::COLLECTION,
@ -263,11 +263,14 @@ mod tests {
let public_matches = public.state.unwrap_matches(); let public_matches = public.state.unwrap_matches();
let expected = Some(AppPublicMatchesInfo::Album(AppPublicAlbumMatches { let mut matches_1: Vec<MatchOption<Album>> =
matching: &album_1, matches_1.into_iter().map(Into::into).collect();
list: &matches_1, matches_1.push(MatchOption::CannotHaveMbid);
let expected = Some(AppMatchesInfo::Album(AppAlbumMatches {
matching: album_1.clone(),
list: matches_1.clone(),
})); }));
assert_eq!(public_matches.matches, expected); assert_eq!(public_matches.matches, expected.as_ref());
let mut app = app.unwrap_matches().select(); let mut app = app.unwrap_matches().select();
@ -276,11 +279,14 @@ mod tests {
let public_matches = public.state.unwrap_matches(); let public_matches = public.state.unwrap_matches();
let expected = Some(AppPublicMatchesInfo::Album(AppPublicAlbumMatches { let mut matches_4: Vec<MatchOption<Album>> =
matching: &album_4, matches_4.into_iter().map(Into::into).collect();
list: &matches_4, matches_4.push(MatchOption::CannotHaveMbid);
let expected = Some(AppMatchesInfo::Album(AppAlbumMatches {
matching: album_4.clone(),
list: matches_4.clone(),
})); }));
assert_eq!(public_matches.matches, expected); assert_eq!(public_matches.matches, expected.as_ref());
let app = app.unwrap_matches().select(); let app = app.unwrap_matches().select();
app.unwrap_browse(); app.unwrap_browse();
@ -324,11 +330,13 @@ mod tests {
let public_matches = public.state.unwrap_matches(); let public_matches = public.state.unwrap_matches();
let expected = Some(AppPublicMatchesInfo::Artist(AppPublicArtistMatches { let mut matches: Vec<MatchOption<Artist>> = matches.into_iter().map(Into::into).collect();
matching: &artist, matches.push(MatchOption::CannotHaveMbid);
list: &matches, let expected = Some(AppMatchesInfo::Artist(AppArtistMatches {
matching: artist.clone(),
list: matches.clone(),
})); }));
assert_eq!(public_matches.matches, expected); assert_eq!(public_matches.matches, expected.as_ref());
let app = app.unwrap_matches().select(); let app = app.unwrap_matches().select();
app.unwrap_browse(); app.unwrap_browse();

View File

@ -1,80 +1,32 @@
use std::{cmp, sync::mpsc}; use std::{cmp, sync::mpsc};
use musichoard::collection::{album::Album, artist::Artist}; use crate::tui::app::{
use crate::tui::{
app::{
machine::{App, AppInner, AppMachine}, machine::{App, AppInner, AppMachine},
AppPublic, AppPublicAlbumMatches, AppPublicArtistMatches, AppPublicMatches, AppAlbumMatches, AppArtistMatches, AppMatchesInfo, AppPublic, AppPublicMatches, AppState,
AppPublicMatchesInfo, AppState, IAppInteractMatches, WidgetState, IAppInteractMatches, MatchOption, WidgetState,
},
lib::interface::musicbrainz::Match,
}; };
#[derive(Clone, Debug, PartialEq, Eq)] impl AppArtistMatches {
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 { fn len(&self) -> usize {
self.list.len() self.list.len()
} }
}
impl<'app> From<&'app AppArtistMatchesInfo> for AppPublicArtistMatches<'app> { fn push_cannot_have_mbid(&mut self) {
fn from(value: &'app AppArtistMatchesInfo) -> Self { self.list.push(MatchOption::CannotHaveMbid)
AppPublicArtistMatches {
matching: &value.matching,
list: &value.list,
}
} }
} }
#[derive(Clone, Debug, PartialEq, Eq)] impl AppAlbumMatches {
pub struct AppAlbumMatchesInfo {
pub matching: Album,
pub list: Vec<Match<Album>>,
}
impl AppAlbumMatchesInfo {
fn is_empty(&self) -> bool {
self.list.is_empty()
}
fn len(&self) -> usize { fn len(&self) -> usize {
self.list.len() self.list.len()
} }
}
impl<'app> From<&'app AppAlbumMatchesInfo> for AppPublicAlbumMatches<'app> { fn push_cannot_have_mbid(&mut self) {
fn from(value: &'app AppAlbumMatchesInfo) -> Self { self.list.push(MatchOption::CannotHaveMbid)
AppPublicAlbumMatches {
matching: &value.matching,
list: &value.list,
} }
} }
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AppMatchesInfo {
Artist(AppArtistMatchesInfo),
Album(AppAlbumMatchesInfo),
}
impl AppMatchesInfo { 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 { fn len(&self) -> usize {
match self { match self {
Self::Artist(a) => a.len(), Self::Artist(a) => a.len(),
@ -82,20 +34,10 @@ impl AppMatchesInfo {
} }
} }
pub fn artist(matching: Artist, list: Vec<Match<Artist>>) -> Self { fn push_cannot_have_mbid(&mut self) {
AppMatchesInfo::Artist(AppArtistMatchesInfo { matching, list }) match self {
} Self::Artist(a) => a.push_cannot_have_mbid(),
Self::Album(a) => a.push_cannot_have_mbid(),
pub fn album(matching: Album, list: Vec<Match<Album>>) -> Self {
AppMatchesInfo::Album(AppAlbumMatchesInfo { matching, list })
}
}
impl<'app> From<&'app AppMatchesInfo> for AppPublicMatchesInfo<'app> {
fn from(value: &'app AppMatchesInfo) -> Self {
match value {
AppMatchesInfo::Artist(a) => AppPublicMatchesInfo::Artist(a.into()),
AppMatchesInfo::Album(a) => AppPublicMatchesInfo::Album(a.into()),
} }
} }
} }
@ -165,13 +107,7 @@ impl IAppInteractMatches for AppMachine<AppMatches> {
fn select(mut self) -> Self::APP { fn select(mut self) -> Self::APP {
self.state.next_matches_info(); self.state.next_matches_info();
match self.state.current { match self.state.current {
Some(ref matches_info) => { Some(_) => self.into(),
self.state.state = WidgetState::default();
if !matches_info.is_empty() {
self.state.state.list.select(Some(0));
}
self.into()
}
None => AppMachine::browse(self.inner).into(), None => AppMachine::browse(self.inner).into(),
} }
} }
@ -193,11 +129,10 @@ impl IAppInteractMatchesPrivate for AppMatches {
fn next_matches_info(&mut self) { fn next_matches_info(&mut self) {
// FIXME: try_recv might not be appropriate for asynchronous version. // FIXME: try_recv might not be appropriate for asynchronous version.
(self.current, self.state) = match self.matches_rx.try_recv() { (self.current, self.state) = match self.matches_rx.try_recv() {
Ok(next_match) => { Ok(mut next_match) => {
next_match.push_cannot_have_mbid();
let mut state = WidgetState::default(); let mut state = WidgetState::default();
if !next_match.is_empty() {
state.list.select(Some(0)); state.list.select(Some(0));
}
(Some(next_match), state) (Some(next_match), state)
} }
Err(_) => (None, WidgetState::default()), Err(_) => (None, WidgetState::default()),
@ -209,13 +144,16 @@ impl IAppInteractMatchesPrivate for AppMatches {
mod tests { mod tests {
use mpsc::Receiver; use mpsc::Receiver;
use musichoard::collection::{ use musichoard::collection::{
album::{AlbumDate, AlbumId, AlbumPrimaryType, AlbumSecondaryType}, album::{Album, AlbumDate, AlbumId, AlbumPrimaryType, AlbumSecondaryType},
artist::ArtistId, artist::{Artist, ArtistId},
}; };
use crate::tui::app::{ use crate::tui::{
app::{
machine::tests::{inner, music_hoard}, machine::tests::{inner, music_hoard},
IAppAccess, IAppAccess,
},
lib::interface::musicbrainz::Match,
}; };
use super::*; use super::*;
@ -287,6 +225,12 @@ mod tests {
rx rx
} }
fn push_cannot_have_mbid(matches_info_vec: &mut [AppMatchesInfo]) {
for matches_info in matches_info_vec.iter_mut() {
matches_info.push_cannot_have_mbid();
}
}
#[test] #[test]
fn create_empty() { fn create_empty() {
let matches = AppMachine::matches(inner(music_hoard(vec![])), receiver(vec![])); let matches = AppMachine::matches(inner(music_hoard(vec![])), receiver(vec![]));
@ -306,11 +250,12 @@ mod tests {
#[test] #[test]
fn create_nonempty() { fn create_nonempty() {
let matches_info_vec = album_matches_info_vec(); let mut matches_info_vec = album_matches_info_vec();
let matches = AppMachine::matches( let matches = AppMachine::matches(
inner(music_hoard(vec![])), inner(music_hoard(vec![])),
receiver(matches_info_vec.clone()), receiver(matches_info_vec.clone()),
); );
push_cannot_have_mbid(&mut matches_info_vec);
let mut widget_state = WidgetState::default(); let mut widget_state = WidgetState::default();
widget_state.list.select(Some(0)); widget_state.list.select(Some(0));
@ -322,15 +267,16 @@ mod tests {
let public = app.get(); let public = app.get();
let public_matches = public.state.unwrap_matches(); let public_matches = public.state.unwrap_matches();
assert_eq!(public_matches.matches, Some((&matches_info_vec[0]).into())); assert_eq!(public_matches.matches, Some(&matches_info_vec[0]));
assert_eq!(public_matches.state, &widget_state); assert_eq!(public_matches.state, &widget_state);
} }
fn matches_flow(matches_info_vec: Vec<AppMatchesInfo>) { fn matches_flow(mut matches_info_vec: Vec<AppMatchesInfo>) {
let matches = AppMachine::matches( let matches = AppMachine::matches(
inner(music_hoard(vec![])), inner(music_hoard(vec![])),
receiver(matches_info_vec.clone()), receiver(matches_info_vec.clone()),
); );
push_cannot_have_mbid(&mut matches_info_vec);
let mut widget_state = WidgetState::default(); let mut widget_state = WidgetState::default();
widget_state.list.select(Some(0)); widget_state.list.select(Some(0));
@ -348,10 +294,16 @@ mod tests {
assert_eq!(matches.state.current.as_ref(), Some(&matches_info_vec[0])); assert_eq!(matches.state.current.as_ref(), Some(&matches_info_vec[0]));
assert_eq!(matches.state.state.list.selected(), Some(1)); assert_eq!(matches.state.state.list.selected(), Some(1));
// Next is CannotHaveMBID
let matches = matches.next_match().unwrap_matches(); let matches = matches.next_match().unwrap_matches();
assert_eq!(matches.state.current.as_ref(), Some(&matches_info_vec[0])); assert_eq!(matches.state.current.as_ref(), Some(&matches_info_vec[0]));
assert_eq!(matches.state.state.list.selected(), Some(1)); assert_eq!(matches.state.state.list.selected(), Some(2));
let matches = matches.next_match().unwrap_matches();
assert_eq!(matches.state.current.as_ref(), Some(&matches_info_vec[0]));
assert_eq!(matches.state.state.list.selected(), Some(2));
let matches = matches.select().unwrap_matches(); let matches = matches.select().unwrap_matches();
@ -374,11 +326,12 @@ mod tests {
#[test] #[test]
fn matches_abort() { fn matches_abort() {
let matches_info_vec = album_matches_info_vec(); let mut matches_info_vec = album_matches_info_vec();
let matches = AppMachine::matches( let matches = AppMachine::matches(
inner(music_hoard(vec![])), inner(music_hoard(vec![])),
receiver(matches_info_vec.clone()), receiver(matches_info_vec.clone()),
); );
push_cannot_have_mbid(&mut matches_info_vec);
let mut widget_state = WidgetState::default(); let mut widget_state = WidgetState::default();
widget_state.list.select(Some(0)); widget_state.list.select(Some(0));

View File

@ -129,26 +129,50 @@ pub struct AppPublicInner<'app> {
pub selection: &'app mut Selection, pub selection: &'app mut Selection,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct AppPublicArtistMatches<'app> { pub enum MatchOption<T> {
pub matching: &'app Artist, Match(Match<T>),
pub list: &'app [Match<Artist>], CannotHaveMbid,
} }
#[derive(Debug, PartialEq, Eq)] impl<T> From<Match<T>> for MatchOption<T> {
pub struct AppPublicAlbumMatches<'app> { fn from(value: Match<T>) -> Self {
pub matching: &'app Album, MatchOption::Match(value)
pub list: &'app [Match<Album>], }
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum AppPublicMatchesInfo<'app> { pub struct AppArtistMatches {
Artist(AppPublicArtistMatches<'app>), pub matching: Artist,
Album(AppPublicAlbumMatches<'app>), pub list: Vec<MatchOption<Artist>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AppAlbumMatches {
pub matching: Album,
pub list: Vec<MatchOption<Album>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AppMatchesInfo {
Artist(AppArtistMatches),
Album(AppAlbumMatches),
}
impl AppMatchesInfo {
pub fn artist<M: Into<MatchOption<Artist>>>(matching: Artist, list: Vec<M>) -> Self {
let list: Vec<MatchOption<Artist>> = list.into_iter().map(Into::into).collect();
AppMatchesInfo::Artist(AppArtistMatches { matching, list })
}
pub fn album<M: Into<MatchOption<Album>>>(matching: Album, list: Vec<M>) -> Self {
let list: Vec<MatchOption<Album>> = list.into_iter().map(Into::into).collect();
AppMatchesInfo::Album(AppAlbumMatches { matching, list })
}
} }
pub struct AppPublicMatches<'app> { pub struct AppPublicMatches<'app> {
pub matches: Option<AppPublicMatchesInfo<'app>>, pub matches: Option<&'app AppMatchesInfo>,
pub state: &'app mut WidgetState, pub state: &'app mut WidgetState,
} }

View File

@ -4,7 +4,7 @@ use musichoard::collection::{
track::{TrackFormat, TrackQuality}, track::{TrackFormat, TrackQuality},
}; };
use crate::tui::{app::AppPublicMatchesInfo, lib::interface::musicbrainz::Match}; use crate::tui::app::{AppMatchesInfo, MatchOption};
pub struct UiDisplay; pub struct UiDisplay;
@ -114,18 +114,19 @@ impl UiDisplay {
"Matching nothing" "Matching nothing"
} }
pub fn display_matching_info(matches: Option<&AppPublicMatchesInfo>) -> String { pub fn display_matching_info(matches: Option<&AppMatchesInfo>) -> String {
match matches.as_ref() { match matches.as_ref() {
Some(kind) => match kind { Some(kind) => match kind {
AppPublicMatchesInfo::Artist(m) => UiDisplay::display_artist_matching(m.matching), AppMatchesInfo::Artist(m) => UiDisplay::display_artist_matching(&m.matching),
AppPublicMatchesInfo::Album(m) => UiDisplay::display_album_matching(m.matching), AppMatchesInfo::Album(m) => UiDisplay::display_album_matching(&m.matching),
}, },
None => UiDisplay::display_nothing_matching().to_string(), None => UiDisplay::display_nothing_matching().to_string(),
} }
} }
pub fn display_artist_match(match_artist: &Match<Artist>) -> String { pub fn display_artist_match(match_option: &MatchOption<Artist>) -> String {
format!( match match_option {
MatchOption::Match(match_artist) => format!(
"{}{} ({}%)", "{}{} ({}%)",
&match_artist.item.id.name, &match_artist.item.id.name,
&match_artist &match_artist
@ -134,11 +135,14 @@ impl UiDisplay {
.map(|d| format!(" ({d})")) .map(|d| format!(" ({d})"))
.unwrap_or_default(), .unwrap_or_default(),
match_artist.score, match_artist.score,
) ),
MatchOption::CannotHaveMbid => Self::display_cannot_have_mbid().to_string(),
}
} }
pub fn display_album_match(match_album: &Match<Album>) -> String { pub fn display_album_match(match_option: &MatchOption<Album>) -> String {
format!( match match_option {
MatchOption::Match(match_album) => format!(
"{:010} | {} [{}] ({}%)", "{:010} | {} [{}] ({}%)",
UiDisplay::display_album_date(&match_album.item.date), UiDisplay::display_album_date(&match_album.item.date),
&match_album.item.id.title, &match_album.item.id.title,
@ -147,7 +151,13 @@ impl UiDisplay {
&match_album.item.secondary_types &match_album.item.secondary_types
), ),
match_album.score, match_album.score,
) ),
MatchOption::CannotHaveMbid => Self::display_cannot_have_mbid().to_string(),
}
}
fn display_cannot_have_mbid() -> &'static str {
"-- Cannot have a MusicBrainz Identifier --"
} }
} }

View File

@ -2,8 +2,7 @@ use musichoard::collection::{album::Album, artist::Artist};
use ratatui::widgets::{List, ListItem}; use ratatui::widgets::{List, ListItem};
use crate::tui::{ use crate::tui::{
app::{AppPublicMatchesInfo, WidgetState}, app::{AppMatchesInfo, MatchOption, WidgetState},
lib::interface::musicbrainz::Match,
ui::display::UiDisplay, ui::display::UiDisplay,
}; };
@ -14,11 +13,11 @@ pub struct MatchesState<'a, 'b> {
} }
impl<'a, 'b> MatchesState<'a, 'b> { impl<'a, 'b> MatchesState<'a, 'b> {
pub fn new(matches: Option<AppPublicMatchesInfo>, state: &'b mut WidgetState) -> Self { pub fn new(matches: Option<&AppMatchesInfo>, state: &'b mut WidgetState) -> Self {
match matches { match matches {
Some(info) => match info { Some(info) => match info {
AppPublicMatchesInfo::Artist(m) => Self::artists(m.matching, m.list, state), AppMatchesInfo::Artist(m) => Self::artists(&m.matching, &m.list, state),
AppPublicMatchesInfo::Album(m) => Self::albums(m.matching, m.list, state), AppMatchesInfo::Album(m) => Self::albums(&m.matching, &m.list, state),
}, },
None => Self::empty(state), None => Self::empty(state),
} }
@ -32,7 +31,11 @@ impl<'a, 'b> MatchesState<'a, 'b> {
} }
} }
fn artists(matching: &Artist, matches: &[Match<Artist>], state: &'b mut WidgetState) -> Self { fn artists(
matching: &Artist,
matches: &[MatchOption<Artist>],
state: &'b mut WidgetState,
) -> Self {
let matching = UiDisplay::display_artist_matching(matching); let matching = UiDisplay::display_artist_matching(matching);
let list = List::new( let list = List::new(
@ -50,7 +53,11 @@ impl<'a, 'b> MatchesState<'a, 'b> {
} }
} }
fn albums(matching: &Album, matches: &[Match<Album>], state: &'b mut WidgetState) -> Self { fn albums(
matching: &Album,
matches: &[MatchOption<Album>],
state: &'b mut WidgetState,
) -> Self {
let matching = UiDisplay::display_album_matching(matching); let matching = UiDisplay::display_album_matching(matching);
let list = List::new( let list = List::new(

View File

@ -59,7 +59,7 @@ impl Minibuffer<'_> {
}, },
AppState::Matches(public) => Minibuffer { AppState::Matches(public) => Minibuffer {
paragraphs: vec![ paragraphs: vec![
Paragraph::new(UiDisplay::display_matching_info(public.matches.as_ref())), Paragraph::new(UiDisplay::display_matching_info(public.matches)),
Paragraph::new("q: abort"), Paragraph::new("q: abort"),
], ],
columns: 2, columns: 2,

View File

@ -14,10 +14,7 @@ use ratatui::{layout::Rect, widgets::Paragraph, Frame};
use musichoard::collection::{album::Album, Collection}; use musichoard::collection::{album::Album, Collection};
use crate::tui::{ use crate::tui::{
app::{ app::{AppMatchesInfo, AppPublicState, AppState, Category, IAppAccess, Selection, WidgetState},
AppPublicMatchesInfo, AppPublicState, AppState, Category, IAppAccess, Selection,
WidgetState,
},
ui::{ ui::{
browse::{ browse::{
AlbumArea, AlbumState, ArtistArea, ArtistState, FrameArea, TrackArea, TrackState, AlbumArea, AlbumState, ArtistArea, ArtistState, FrameArea, TrackArea, TrackState,
@ -135,7 +132,7 @@ impl Ui {
} }
fn render_matches_overlay( fn render_matches_overlay(
matches: Option<AppPublicMatchesInfo>, matches: Option<&AppMatchesInfo>,
state: &mut WidgetState, state: &mut WidgetState,
frame: &mut Frame, frame: &mut Frame,
) { ) {
@ -185,10 +182,7 @@ mod tests {
}; };
use crate::tui::{ use crate::tui::{
app::{ app::{AppPublic, AppPublicInner, AppPublicMatches, Delta, MatchOption},
AppPublic, AppPublicAlbumMatches, AppPublicArtistMatches, AppPublicInner,
AppPublicMatches, Delta,
},
lib::interface::musicbrainz::Match, lib::interface::musicbrainz::Match,
testmod::COLLECTION, testmod::COLLECTION,
tests::terminal, tests::terminal,
@ -196,33 +190,6 @@ mod tests {
use super::*; use super::*;
impl<'app> AppPublicArtistMatches<'app> {
fn get(&self) -> AppPublicArtistMatches<'app> {
AppPublicArtistMatches {
matching: self.matching,
list: self.list,
}
}
}
impl<'app> AppPublicAlbumMatches<'app> {
fn get(&self) -> AppPublicAlbumMatches<'app> {
AppPublicAlbumMatches {
matching: self.matching,
list: self.list,
}
}
}
impl<'app> AppPublicMatchesInfo<'app> {
fn get(&self) -> AppPublicMatchesInfo<'app> {
match self {
Self::Artist(a) => Self::Artist(a.get()),
Self::Album(a) => Self::Album(a.get()),
}
}
}
// Automock does not support returning types with generic lifetimes. // Automock does not support returning types with generic lifetimes.
impl<'app> IAppAccess for AppPublic<'app> { impl<'app> IAppAccess for AppPublic<'app> {
fn get(&mut self) -> AppPublic { fn get(&mut self) -> AppPublic {
@ -237,7 +204,7 @@ mod tests {
AppState::Reload(()) => AppState::Reload(()), AppState::Reload(()) => AppState::Reload(()),
AppState::Search(s) => AppState::Search(s), AppState::Search(s) => AppState::Search(s),
AppState::Matches(ref mut m) => AppState::Matches(AppPublicMatches { AppState::Matches(ref mut m) => AppState::Matches(AppPublicMatches {
matches: m.matches.as_mut().map(|k| k.get()), matches: m.matches,
state: m.state, state: m.state,
}), }),
AppState::Error(s) => AppState::Error(s), AppState::Error(s) => AppState::Error(s),
@ -257,18 +224,16 @@ mod tests {
} }
} }
fn artist_matches<'app>( fn artist_matches(matching: Artist, list: Vec<Match<Artist>>) -> AppMatchesInfo {
matching: &'app Artist, let mut list: Vec<MatchOption<Artist>> = list.into_iter().map(Into::into).collect();
list: &'app [Match<Artist>], list.push(MatchOption::CannotHaveMbid);
) -> AppPublicMatchesInfo<'app> { AppMatchesInfo::artist(matching, list)
AppPublicMatchesInfo::Artist(AppPublicArtistMatches { matching, list })
} }
fn album_matches<'app>( fn album_matches(matching: Album, list: Vec<Match<Album>>) -> AppMatchesInfo {
matching: &'app Album, let mut list: Vec<MatchOption<Album>> = list.into_iter().map(Into::into).collect();
list: &'app [Match<Album>], list.push(MatchOption::CannotHaveMbid);
) -> AppPublicMatchesInfo<'app> { AppMatchesInfo::album(matching, list)
AppPublicMatchesInfo::Album(AppPublicAlbumMatches { matching, list })
} }
fn draw_test_suite(collection: &Collection, selection: &mut Selection) { fn draw_test_suite(collection: &Collection, selection: &mut Selection) {
@ -364,21 +329,21 @@ mod tests {
let mut terminal = terminal(); let mut terminal = terminal();
let artist = Artist::new(ArtistId::new("an artist")); let artist = Artist::new(ArtistId::new("an artist"));
let artist_match = Match { let artist_match = Match {
score: 80, score: 80,
item: artist.clone(), item: artist.clone(),
disambiguation: None, disambiguation: None,
}; };
let list = vec![artist_match.clone(), artist_match.clone()];
let artist_matches = artist_matches(artist, list);
let list = [artist_match.clone(), artist_match.clone()];
let mut widget_state = WidgetState::default(); let mut widget_state = WidgetState::default();
widget_state.list.select(Some(0)); widget_state.list.select(Some(0));
let mut app = AppPublic { let mut app = AppPublic {
inner: public_inner(collection, &mut selection), inner: public_inner(collection, &mut selection),
state: AppState::Matches(AppPublicMatches { state: AppState::Matches(AppPublicMatches {
matches: Some(artist_matches(&artist, &list)), matches: Some(&artist_matches),
state: &mut widget_state, state: &mut widget_state,
}), }),
}; };
@ -398,21 +363,21 @@ mod tests {
Some(AlbumPrimaryType::Album), Some(AlbumPrimaryType::Album),
vec![AlbumSecondaryType::Live, AlbumSecondaryType::Compilation], vec![AlbumSecondaryType::Live, AlbumSecondaryType::Compilation],
); );
let album_match = Match { let album_match = Match {
score: 80, score: 80,
item: album.clone(), item: album.clone(),
disambiguation: None, disambiguation: None,
}; };
let list = vec![album_match.clone(), album_match.clone()];
let album_matches = album_matches(album, list);
let list = [album_match.clone(), album_match.clone()];
let mut widget_state = WidgetState::default(); let mut widget_state = WidgetState::default();
widget_state.list.select(Some(0)); widget_state.list.select(Some(0));
let mut app = AppPublic { let mut app = AppPublic {
inner: public_inner(collection, &mut selection), inner: public_inner(collection, &mut selection),
state: AppState::Matches(AppPublicMatches { state: AppState::Matches(AppPublicMatches {
matches: Some(album_matches(&album, &list)), matches: Some(&album_matches),
state: &mut widget_state, state: &mut widget_state,
}), }),
}; };