Support display of artist matches

This commit is contained in:
Wojciech Kozlowski 2024-08-30 14:52:51 +02:00
parent fbe6c23013
commit 43ce06a5e8
7 changed files with 148 additions and 47 deletions

View File

@ -257,9 +257,9 @@ mod tests {
let public_matches = public.state.unwrap_matches(); let public_matches = public.state.unwrap_matches();
assert_eq!(public_matches.matches.as_ref().unwrap().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().list, public_matches.matches.as_ref().unwrap().album_ref().list,
matches_1.as_slice() matches_1.as_slice()
); );
@ -270,9 +270,9 @@ mod tests {
let public_matches = public.state.unwrap_matches(); let public_matches = public.state.unwrap_matches();
assert_eq!(public_matches.matches.as_ref().unwrap().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().list, public_matches.matches.as_ref().unwrap().album_ref().list,
matches_4.as_slice() matches_4.as_slice()
); );

View File

@ -5,8 +5,8 @@ use musichoard::collection::album::Album;
use crate::tui::{ use crate::tui::{
app::{ app::{
machine::{App, AppInner, AppMachine}, machine::{App, AppInner, AppMachine},
AppPublic, AppPublicAlbumMatches, AppPublicMatches, AppState, IAppInteractMatches, AppPublic, AppPublicAlbumMatches, AppPublicMatches, AppPublicMatchesKind, AppState,
WidgetState, IAppInteractMatches, WidgetState,
}, },
lib::interface::musicbrainz::Match, lib::interface::musicbrainz::Match,
}; };
@ -53,9 +53,11 @@ 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| AppPublicAlbumMatches { let matches = machine.state.index.map(|index| {
matching: &machine.state.matches_info_vec[index].matching, AppPublicMatchesKind::Album(AppPublicAlbumMatches {
list: machine.state.matches_info_vec[index].matches.as_slice(), matching: &machine.state.matches_info_vec[index].matching,
list: machine.state.matches_info_vec[index].matches.as_slice(),
})
}); });
AppPublic { AppPublic {
@ -217,11 +219,11 @@ 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().matching, public_matches.matches.as_ref().unwrap().album_ref().matching,
&matches_info_vec[0].matching &matches_info_vec[0].matching
); );
assert_eq!( assert_eq!(
public_matches.matches.as_ref().unwrap().list, public_matches.matches.as_ref().unwrap().album_ref().list,
matches_info_vec[0].matches.as_slice() matches_info_vec[0].matches.as_slice()
); );
assert_eq!(public_matches.state, &widget_state); assert_eq!(public_matches.state, &widget_state);

View File

@ -4,7 +4,7 @@ mod selection;
pub use machine::App; pub use machine::App;
pub use selection::{Category, Delta, Selection, WidgetState}; pub use selection::{Category, Delta, Selection, WidgetState};
use musichoard::collection::{album::Album, Collection}; use musichoard::collection::{album::Album, artist::Artist, Collection};
use crate::tui::lib::interface::musicbrainz::Match; use crate::tui::lib::interface::musicbrainz::Match;
@ -129,14 +129,42 @@ pub struct AppPublicInner<'app> {
pub selection: &'app mut Selection, pub selection: &'app mut Selection,
} }
#[derive(Debug, PartialEq, Eq)]
pub struct AppPublicArtistMatches<'app> {
pub matching: &'app Artist,
pub list: &'app [Match<Artist>],
}
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct AppPublicAlbumMatches<'app> { pub struct AppPublicAlbumMatches<'app> {
pub matching: &'app Album, pub matching: &'app Album,
pub list: &'app [Match<Album>], pub list: &'app [Match<Album>],
} }
#[derive(Debug, PartialEq, Eq)]
pub enum AppPublicMatchesKind<'app> {
Artist(AppPublicArtistMatches<'app>),
Album(AppPublicAlbumMatches<'app>)
}
impl<'app> AppPublicMatchesKind<'app> {
pub fn artist_ref(&self) -> &AppPublicArtistMatches<'app> {
match self {
AppPublicMatchesKind::Artist(m) => m,
_ => panic!(),
}
}
pub fn album_ref(&self) -> &AppPublicAlbumMatches<'app> {
match self {
AppPublicMatchesKind::Album(m) => m,
_ => panic!(),
}
}
}
pub struct AppPublicMatches<'app> { pub struct AppPublicMatches<'app> {
pub matches: Option<AppPublicAlbumMatches<'app>>, pub matches: Option<AppPublicMatchesKind<'app>>,
pub state: &'app mut WidgetState, pub state: &'app mut WidgetState,
} }

View File

@ -1,9 +1,10 @@
use musichoard::collection::{ use musichoard::collection::{
album::{Album, AlbumDate, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq, AlbumStatus}, album::{Album, AlbumDate, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq, AlbumStatus},
artist::Artist,
track::{TrackFormat, TrackQuality}, track::{TrackFormat, TrackQuality},
}; };
use crate::tui::lib::interface::musicbrainz::Match; use crate::tui::{app::AppPublicMatchesKind, lib::interface::musicbrainz::Match};
pub struct UiDisplay; pub struct UiDisplay;
@ -97,18 +98,37 @@ impl UiDisplay {
} }
} }
pub fn display_matching_info(matching: Option<&Album>) -> String { pub fn display_artist_matching_info(artist: &Artist) -> String {
match matching { format!("Matching artist: {}", &artist.id.name)
Some(matching) => format!( }
"Matching: {} | {}",
UiDisplay::display_album_date(&matching.date), pub fn display_album_matching_info(album: &Album) -> String {
&matching.id.title format!(
), "Matching album: {} | {}",
None => String::from("Matching: nothing"), UiDisplay::display_album_date(&album.date),
&album.id.title
)
}
pub fn display_matching_nothing_info() -> &'static str {
"Matching nothing"
}
pub fn display_matching_info(matches: Option<&AppPublicMatchesKind>) -> String {
match matches.as_ref() {
Some(kind) => match kind {
AppPublicMatchesKind::Artist(m) => {
UiDisplay::display_artist_matching_info(m.matching)
}
AppPublicMatchesKind::Album(m) => {
UiDisplay::display_album_matching_info(m.matching)
}
},
None => UiDisplay::display_matching_nothing_info().to_string(),
} }
} }
pub fn display_match_string(match_album: &Match<Album>) -> String { pub fn display_album_match_string(match_album: &Match<Album>) -> String {
format!( format!(
"{:010} | {} [{}] ({}%)", "{:010} | {} [{}] ({}%)",
UiDisplay::display_album_date(&match_album.item.date), UiDisplay::display_album_date(&match_album.item.date),
@ -120,6 +140,17 @@ impl UiDisplay {
match_album.score, match_album.score,
) )
} }
pub fn display_artist_match_string(match_artist: &Match<Artist>) -> String {
let disambiguation_string = match match_artist.disambiguation.as_ref() {
Some(d) => format!(" ({d})"),
None => String::from(""),
};
format!(
"{}{} ({}%)",
&match_artist.item.id.name, &disambiguation_string, match_artist.score,
)
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,23 +1,42 @@
use musichoard::collection::album::Album; use musichoard::collection::{album::Album, artist::Artist};
use ratatui::widgets::{List, ListItem}; use ratatui::widgets::{List, ListItem};
use crate::tui::{app::WidgetState, lib::interface::musicbrainz::Match, ui::display::UiDisplay}; use crate::tui::{app::WidgetState, lib::interface::musicbrainz::Match, ui::display::UiDisplay};
pub struct AlbumMatchesState<'a, 'b> { pub struct MatchesState<'a, 'b> {
pub list: List<'a>, pub list: List<'a>,
pub state: &'b mut WidgetState, pub state: &'b mut WidgetState,
} }
impl<'a, 'b> AlbumMatchesState<'a, 'b> { impl<'a, 'b> MatchesState<'a, 'b> {
pub fn new(matches: &[Match<Album>], state: &'b mut WidgetState) -> Self { pub fn empty(state: &'b mut WidgetState) -> Self {
MatchesState {
list: List::default(),
state,
}
}
pub fn artists(matches: &[Match<Artist>], state: &'b mut WidgetState) -> Self {
let list = List::new( let list = List::new(
matches matches
.iter() .iter()
.map(UiDisplay::display_match_string) .map(UiDisplay::display_artist_match_string)
.map(ListItem::new) .map(ListItem::new)
.collect::<Vec<ListItem>>(), .collect::<Vec<ListItem>>(),
); );
AlbumMatchesState { list, state } MatchesState { list, state }
}
pub fn albums(matches: &[Match<Album>], state: &'b mut WidgetState) -> Self {
let list = List::new(
matches
.iter()
.map(UiDisplay::display_album_match_string)
.map(ListItem::new)
.collect::<Vec<ListItem>>(),
);
MatchesState { list, state }
} }
} }

View File

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

View File

@ -22,7 +22,7 @@ use crate::tui::{
display::UiDisplay, display::UiDisplay,
error::ErrorOverlay, error::ErrorOverlay,
info::{AlbumOverlay, ArtistOverlay}, info::{AlbumOverlay, ArtistOverlay},
matches::AlbumMatchesState, matches::MatchesState,
minibuffer::Minibuffer, minibuffer::Minibuffer,
overlay::{OverlayBuilder, OverlaySize}, overlay::{OverlayBuilder, OverlaySize},
reload::ReloadOverlay, reload::ReloadOverlay,
@ -30,7 +30,7 @@ use crate::tui::{
}, },
}; };
use super::app::AppPublicAlbumMatches; use super::app::AppPublicMatchesKind;
pub trait IUi { pub trait IUi {
fn render<APP: IAppAccess>(app: &mut APP, frame: &mut Frame); fn render<APP: IAppAccess>(app: &mut APP, frame: &mut Frame);
@ -134,18 +134,28 @@ impl Ui {
} }
fn render_matches_overlay( fn render_matches_overlay(
matches: Option<AppPublicAlbumMatches>, matches: Option<AppPublicMatchesKind>,
state: &mut WidgetState, state: &mut WidgetState,
frame: &mut Frame, frame: &mut Frame,
) { ) {
let area = OverlayBuilder::default().build(frame.size()); let area = OverlayBuilder::default().build(frame.size());
let (matching, list) = match matches { let (matching, st) = match matches {
Some(m) => (Some(m.matching), Some(m.list)), Some(kind) => match kind {
None => (None, None), AppPublicMatchesKind::Artist(m) => (
UiDisplay::display_artist_matching_info(m.matching),
MatchesState::artists(m.list, state),
),
AppPublicMatchesKind::Album(m) => (
UiDisplay::display_album_matching_info(m.matching),
MatchesState::albums(m.list, state),
),
},
None => (
UiDisplay::display_matching_nothing_info().to_string(),
MatchesState::empty(state),
),
}; };
let matching_string = UiDisplay::display_matching_info(matching); UiWidget::render_overlay_list_widget(&matching, st.list, st.state, true, area, frame)
let st = AlbumMatchesState::new(list.unwrap_or_default(), state);
UiWidget::render_overlay_list_widget(&matching_string, st.list, st.state, true, area, frame)
} }
fn render_error_overlay<S: AsRef<str>>(title: S, msg: S, frame: &mut Frame) { fn render_error_overlay<S: AsRef<str>>(title: S, msg: S, frame: &mut Frame) {
@ -189,7 +199,10 @@ mod tests {
}; };
use crate::tui::{ use crate::tui::{
app::{AppPublic, AppPublicAlbumMatches, AppPublicInner, AppPublicMatches, Delta}, app::{
AppPublic, AppPublicAlbumMatches, AppPublicArtistMatches, AppPublicInner,
AppPublicMatches, Delta,
},
lib::interface::musicbrainz::Match, lib::interface::musicbrainz::Match,
testmod::COLLECTION, testmod::COLLECTION,
tests::terminal, tests::terminal,
@ -211,9 +224,19 @@ 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_ref().map(|matches| AppPublicAlbumMatches { matches: m.matches.as_ref().map(|k| match k {
matching: matches.matching, AppPublicMatchesKind::Artist(a) => {
list: matches.list, AppPublicMatchesKind::Artist(AppPublicArtistMatches {
matching: a.matching,
list: a.list,
})
}
AppPublicMatchesKind::Album(a) => {
AppPublicMatchesKind::Album(AppPublicAlbumMatches {
matching: a.matching,
list: a.list,
})
}
}), }),
state: m.state, state: m.state,
}), }),
@ -263,10 +286,10 @@ mod tests {
widget_state.list.select(Some(0)); widget_state.list.select(Some(0));
app.state = AppState::Matches(AppPublicMatches { app.state = AppState::Matches(AppPublicMatches {
matches: Some(AppPublicAlbumMatches { matches: Some(AppPublicMatchesKind::Album(AppPublicAlbumMatches {
matching: &album, matching: &album,
list: &album_matches, list: &album_matches,
}), })),
state: &mut widget_state, state: &mut widget_state,
}); });
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();