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 77 additions and 161 deletions
Showing only changes of commit 006a981347 - Show all commits

View File

@ -3,9 +3,9 @@ use std::{sync::mpsc, thread, time};
use musichoard::collection::musicbrainz::IMusicBrainzRef;
use crate::tui::app::{
machine::{matches::AppMatchesInfo, App, AppInner, AppMachine},
machine::{App, AppInner, AppMachine},
selection::{Delta, ListSelection},
AppPublic, AppState, IAppInteractBrowse,
AppMatchesInfo, AppPublic, AppState, IAppInteractBrowse,
};
pub struct AppBrowse;
@ -139,8 +139,8 @@ mod tests {
use crate::tui::{
app::{
machine::tests::{inner, inner_with_mb, music_hoard},
AppPublicAlbumMatches, AppPublicArtistMatches, AppPublicMatchesInfo, Category,
IAppAccess, IAppInteract, IAppInteractMatches,
AppAlbumMatches, AppArtistMatches, AppMatchesInfo, Category, IAppAccess, IAppInteract,
IAppInteractMatches,
},
lib::interface::musicbrainz::{self, Match, MockIMusicBrainz},
testmod::COLLECTION,
@ -263,11 +263,11 @@ mod tests {
let public_matches = public.state.unwrap_matches();
let expected = Some(AppPublicMatchesInfo::Album(AppPublicAlbumMatches {
matching: &album_1,
list: &matches_1,
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();
@ -276,11 +276,11 @@ mod tests {
let public_matches = public.state.unwrap_matches();
let expected = Some(AppPublicMatchesInfo::Album(AppPublicAlbumMatches {
matching: &album_4,
list: &matches_4,
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();
app.unwrap_browse();
@ -324,11 +324,11 @@ mod tests {
let public_matches = public.state.unwrap_matches();
let expected = Some(AppPublicMatchesInfo::Artist(AppPublicArtistMatches {
matching: &artist,
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();
app.unwrap_browse();

View File

@ -1,23 +1,12 @@
use std::{cmp, sync::mpsc};
use musichoard::collection::{album::Album, artist::Artist};
use crate::tui::{
app::{
machine::{App, AppInner, AppMachine},
AppPublic, AppPublicAlbumMatches, AppPublicArtistMatches, AppPublicMatches,
AppPublicMatchesInfo, AppState, IAppInteractMatches, WidgetState,
},
lib::interface::musicbrainz::Match,
use crate::tui::app::{
machine::{App, AppInner, AppMachine},
AppAlbumMatches, AppArtistMatches, AppMatchesInfo, AppPublic, AppPublicMatches, AppState,
IAppInteractMatches, WidgetState,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AppArtistMatchesInfo {
pub matching: Artist,
pub list: Vec<Match<Artist>>,
}
impl AppArtistMatchesInfo {
impl AppArtistMatches {
fn is_empty(&self) -> bool {
self.list.is_empty()
}
@ -27,22 +16,7 @@ impl AppArtistMatchesInfo {
}
}
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 list: Vec<Match<Album>>,
}
impl AppAlbumMatchesInfo {
impl AppAlbumMatches {
fn is_empty(&self) -> bool {
self.list.is_empty()
}
@ -52,21 +26,6 @@ impl AppAlbumMatchesInfo {
}
}
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 {
@ -81,23 +40,6 @@ impl AppMatchesInfo {
Self::Album(a) => a.len(),
}
}
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 AppPublicMatchesInfo<'app> {
fn from(value: &'app AppMatchesInfo) -> Self {
match value {
AppMatchesInfo::Artist(a) => AppPublicMatchesInfo::Artist(a.into()),
AppMatchesInfo::Album(a) => AppPublicMatchesInfo::Album(a.into()),
}
}
}
pub struct AppMatches {
@ -209,13 +151,16 @@ impl IAppInteractMatchesPrivate for AppMatches {
mod tests {
use mpsc::Receiver;
use musichoard::collection::{
album::{AlbumDate, AlbumId, AlbumPrimaryType, AlbumSecondaryType},
artist::ArtistId,
album::{Album, AlbumDate, AlbumId, AlbumPrimaryType, AlbumSecondaryType},
artist::{Artist, ArtistId},
};
use crate::tui::app::{
machine::tests::{inner, music_hoard},
IAppAccess,
use crate::tui::{
app::{
machine::tests::{inner, music_hoard},
IAppAccess,
},
lib::interface::musicbrainz::Match,
};
use super::*;
@ -322,7 +267,7 @@ mod tests {
let public = app.get();
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);
}

View File

@ -129,26 +129,36 @@ pub struct AppPublicInner<'app> {
pub selection: &'app mut Selection,
}
#[derive(Debug, PartialEq, Eq)]
pub struct AppPublicArtistMatches<'app> {
pub matching: &'app Artist,
pub list: &'app [Match<Artist>],
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AppArtistMatches {
pub matching: Artist,
pub list: Vec<Match<Artist>>,
}
#[derive(Debug, PartialEq, Eq)]
pub struct AppPublicAlbumMatches<'app> {
pub matching: &'app Album,
pub list: &'app [Match<Album>],
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AppAlbumMatches {
pub matching: Album,
pub list: Vec<Match<Album>>,
}
#[derive(Debug, PartialEq, Eq)]
pub enum AppPublicMatchesInfo<'app> {
Artist(AppPublicArtistMatches<'app>),
Album(AppPublicAlbumMatches<'app>),
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AppMatchesInfo {
Artist(AppArtistMatches),
Album(AppAlbumMatches),
}
impl AppMatchesInfo {
pub fn artist(matching: Artist, list: Vec<Match<Artist>>) -> Self {
AppMatchesInfo::Artist(AppArtistMatches { matching, list })
}
pub fn album(matching: Album, list: Vec<Match<Album>>) -> Self {
AppMatchesInfo::Album(AppAlbumMatches { matching, list })
}
}
pub struct AppPublicMatches<'app> {
pub matches: Option<AppPublicMatchesInfo<'app>>,
pub matches: Option<&'app AppMatchesInfo>,
pub state: &'app mut WidgetState,
}

View File

@ -4,7 +4,7 @@ use musichoard::collection::{
track::{TrackFormat, TrackQuality},
};
use crate::tui::{app::AppPublicMatchesInfo, lib::interface::musicbrainz::Match};
use crate::tui::{app::AppMatchesInfo, lib::interface::musicbrainz::Match};
pub struct UiDisplay;
@ -114,11 +114,11 @@ impl UiDisplay {
"Matching nothing"
}
pub fn display_matching_info(matches: Option<&AppPublicMatchesInfo>) -> String {
pub fn display_matching_info(matches: Option<&AppMatchesInfo>) -> String {
match matches.as_ref() {
Some(kind) => match kind {
AppPublicMatchesInfo::Artist(m) => UiDisplay::display_artist_matching(m.matching),
AppPublicMatchesInfo::Album(m) => UiDisplay::display_album_matching(m.matching),
AppMatchesInfo::Artist(m) => UiDisplay::display_artist_matching(&m.matching),
AppMatchesInfo::Album(m) => UiDisplay::display_album_matching(&m.matching),
},
None => UiDisplay::display_nothing_matching().to_string(),
}

View File

@ -2,7 +2,7 @@ use musichoard::collection::{album::Album, artist::Artist};
use ratatui::widgets::{List, ListItem};
use crate::tui::{
app::{AppPublicMatchesInfo, WidgetState},
app::{AppMatchesInfo, WidgetState},
lib::interface::musicbrainz::Match,
ui::display::UiDisplay,
};
@ -14,11 +14,11 @@ pub struct 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 {
Some(info) => match info {
AppPublicMatchesInfo::Artist(m) => Self::artists(m.matching, m.list, state),
AppPublicMatchesInfo::Album(m) => Self::albums(m.matching, m.list, state),
AppMatchesInfo::Artist(m) => Self::artists(&m.matching, &m.list, state),
AppMatchesInfo::Album(m) => Self::albums(&m.matching, &m.list, state),
},
None => Self::empty(state),
}

View File

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

View File

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