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

View File

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

View File

@ -4,7 +4,7 @@ mod selection;
pub use machine::App;
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;
@ -129,14 +129,42 @@ 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(Debug, PartialEq, Eq)]
pub struct AppPublicAlbumMatches<'app> {
pub matching: &'app 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 matches: Option<AppPublicAlbumMatches<'app>>,
pub matches: Option<AppPublicMatchesKind<'app>>,
pub state: &'app mut WidgetState,
}

View File

@ -1,9 +1,10 @@
use musichoard::collection::{
album::{Album, AlbumDate, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq, AlbumStatus},
artist::Artist,
track::{TrackFormat, TrackQuality},
};
use crate::tui::lib::interface::musicbrainz::Match;
use crate::tui::{app::AppPublicMatchesKind, lib::interface::musicbrainz::Match};
pub struct UiDisplay;
@ -97,18 +98,37 @@ impl UiDisplay {
}
}
pub fn display_matching_info(matching: Option<&Album>) -> String {
match matching {
Some(matching) => format!(
"Matching: {} | {}",
UiDisplay::display_album_date(&matching.date),
&matching.id.title
),
None => String::from("Matching: nothing"),
pub fn display_artist_matching_info(artist: &Artist) -> String {
format!("Matching artist: {}", &artist.id.name)
}
pub fn display_album_matching_info(album: &Album) -> String {
format!(
"Matching album: {} | {}",
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!(
"{:010} | {} [{}] ({}%)",
UiDisplay::display_album_date(&match_album.item.date),
@ -120,6 +140,17 @@ impl UiDisplay {
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)]

View File

@ -1,23 +1,42 @@
use musichoard::collection::album::Album;
use musichoard::collection::{album::Album, artist::Artist};
use ratatui::widgets::{List, ListItem};
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 state: &'b mut WidgetState,
}
impl<'a, 'b> AlbumMatchesState<'a, 'b> {
pub fn new(matches: &[Match<Album>], state: &'b mut WidgetState) -> Self {
impl<'a, 'b> MatchesState<'a, 'b> {
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(
matches
.iter()
.map(UiDisplay::display_match_string)
.map(UiDisplay::display_artist_match_string)
.map(ListItem::new)
.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 {
paragraphs: vec![
Paragraph::new(UiDisplay::display_matching_info(
public.matches.as_ref().map(|m| m.matching),
)),
Paragraph::new(UiDisplay::display_matching_info(public.matches.as_ref())),
Paragraph::new("q: abort"),
],
columns: 2,

View File

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