Integrate browse API into TUI MB daemon #230

Merged
wojtek merged 15 commits from 160---provide-a-keyboard-shortcut-to-pull-all-release-groups-of-an-artist into main 2024-10-06 15:32:46 +02:00
10 changed files with 89 additions and 86 deletions
Showing only changes of commit 898d2e1469 - Show all commits

View File

@ -290,7 +290,7 @@ mod tests {
machine::tests::{inner, music_hoard},
Delta, IApp, IAppAccess, IAppInteractBrowse, MatchOption, MatchStateInfo,
},
lib::interface::musicbrainz::{self, api::Match, daemon::MockIMbJobSender},
lib::interface::musicbrainz::{self, api::Entity, daemon::MockIMbJobSender},
testmod::COLLECTION,
};
@ -310,13 +310,13 @@ mod tests {
let artist = COLLECTION[3].meta.clone();
let matches: Vec<Match<ArtistMeta>> = vec![];
let matches: Vec<Entity<ArtistMeta>> = vec![];
let fetch_result = MatchStateInfo::artist_search(artist.clone(), matches);
fetch_tx.send(Ok(fetch_result.clone())).unwrap();
assert_eq!(fetch.try_recv(), Err(TryRecvError::Empty));
let lookup = Match::item(artist.clone());
let lookup = Entity::new(artist.clone());
let lookup_result = MatchStateInfo::artist_lookup(artist.clone(), lookup);
lookup_tx.send(Ok(lookup_result.clone())).unwrap();
@ -601,7 +601,7 @@ mod tests {
let (tx, rx) = mpsc::channel::<MbApiResult>();
let artist = COLLECTION[3].meta.clone();
let artist_match = Match::with_score(COLLECTION[2].meta.clone(), 80);
let artist_match = Entity::with_score(COLLECTION[2].meta.clone(), 80);
let artist_match_info =
MatchStateInfo::artist_search(artist.clone(), vec![artist_match.clone()]);
let fetch_result = Ok(artist_match_info);
@ -677,7 +677,7 @@ mod tests {
assert!(matches!(app, AppState::Fetch(_)));
let artist = COLLECTION[3].meta.clone();
let match_info = MatchStateInfo::artist_search::<Match<ArtistMeta>>(artist, vec![]);
let match_info = MatchStateInfo::artist_search::<Entity<ArtistMeta>>(artist, vec![]);
let fetch_result = Ok(match_info);
tx.send(fetch_result).unwrap();

View File

@ -37,7 +37,7 @@ impl GetInfo for MatchOption<ArtistMeta> {
fn into_info(&self, mut info: Self::InfoType) -> InfoOption<Self::InfoType> {
match self {
MatchOption::Some(option) => info.musicbrainz = option.item.info.musicbrainz.clone(),
MatchOption::Some(option) => info.musicbrainz = option.entity.info.musicbrainz.clone(),
MatchOption::CannotHaveMbid => info.musicbrainz = MbRefOption::CannotHaveMbid,
MatchOption::ManualInputMbid => return InfoOption::NeedInput,
}
@ -50,7 +50,7 @@ impl GetInfo for MatchOption<AlbumMeta> {
fn into_info(&self, mut info: Self::InfoType) -> InfoOption<Self::InfoType> {
match self {
MatchOption::Some(option) => info = option.item.info.clone(),
MatchOption::Some(option) => info = option.entity.info.clone(),
MatchOption::CannotHaveMbid => info.musicbrainz = MbRefOption::CannotHaveMbid,
MatchOption::ManualInputMbid => return InfoOption::NeedInput,
}
@ -271,18 +271,18 @@ mod tests {
IApp, IAppAccess, IAppInput,
},
lib::interface::musicbrainz::{
api::Match,
api::Entity,
daemon::{MbParams, MockIMbJobSender},
},
};
use super::*;
impl<T> Match<T> {
pub fn with_score(item: T, score: u8) -> Self {
Match {
impl<T> Entity<T> {
pub fn with_score(entity: T, score: u8) -> Self {
Entity {
score: Some(score),
item,
entity,
disambiguation: None,
}
}
@ -302,10 +302,10 @@ mod tests {
let artist = artist_meta();
let artist_1 = artist.clone();
let artist_match_1 = Match::with_score(artist_1, 100);
let artist_match_1 = Entity::with_score(artist_1, 100);
let artist_2 = artist.clone();
let mut artist_match_2 = Match::with_score(artist_2, 100);
let mut artist_match_2 = Entity::with_score(artist_2, 100);
artist_match_2.disambiguation = Some(String::from("some disambiguation"));
let list = vec![artist_match_1.clone(), artist_match_2.clone()];
@ -314,7 +314,7 @@ mod tests {
fn artist_lookup() -> MatchStateInfo {
let artist = artist_meta();
let lookup = Match::item(artist.clone());
let lookup = Entity::new(artist.clone());
MatchStateInfo::artist_lookup(artist, lookup)
}
@ -335,12 +335,12 @@ mod tests {
let album = album_meta();
let album_1 = album.clone();
let album_match_1 = Match::with_score(album_1, 100);
let album_match_1 = Entity::with_score(album_1, 100);
let mut album_2 = album.clone();
album_2.id.title.push_str(" extra title part");
album_2.info.secondary_types.pop();
let album_match_2 = Match::with_score(album_2, 100);
let album_match_2 = Entity::with_score(album_2, 100);
let list = vec![album_match_1.clone(), album_match_2.clone()];
MatchStateInfo::album_search(artist_id, album, list)
@ -349,7 +349,7 @@ mod tests {
fn album_lookup() -> MatchStateInfo {
let artist_id = ArtistId::new("Artist");
let album = album_meta();
let lookup = Match::item(album.clone());
let lookup = Entity::new(album.clone());
MatchStateInfo::album_lookup(artist_id, album, lookup)
}
@ -597,7 +597,7 @@ mod tests {
.with(predicate::always(), predicate::eq(requests))
.return_once(|_, _| Ok(()));
let matches_vec: Vec<Match<ArtistMeta>> = vec![];
let matches_vec: Vec<Entity<ArtistMeta>> = vec![];
let artist_match = MatchStateInfo::artist_search(artist.clone(), matches_vec);
let matches = AppMachine::match_state(
inner_with_mb(music_hoard(vec![]), mb_job_sender),
@ -630,7 +630,7 @@ mod tests {
.with(predicate::always(), predicate::eq(requests))
.return_once(|_, _| Ok(()));
let matches_vec: Vec<Match<AlbumMeta>> = vec![];
let matches_vec: Vec<Entity<AlbumMeta>> = vec![];
let album_match =
MatchStateInfo::album_search(artist_id.clone(), album.clone(), matches_vec);
let matches = AppMachine::match_state(

View File

@ -226,7 +226,10 @@ mod tests {
use crate::tui::{
app::{AppState, IApp, IAppInput, IAppInteractBrowse, InputEvent, MatchStateInfo},
lib::{interface::musicbrainz::{api::Match, daemon::MockIMbJobSender}, MockIMusicHoard},
lib::{
interface::musicbrainz::{api::Entity, daemon::MockIMbJobSender},
MockIMusicHoard,
},
};
use super::*;
@ -517,7 +520,7 @@ mod tests {
let (_, rx) = mpsc::channel();
let fetch = FetchState::new(rx);
let artist = ArtistMeta::new(ArtistId::new("Artist"));
let info = MatchStateInfo::artist_lookup(artist.clone(), Match::item(artist.clone()));
let info = MatchStateInfo::artist_lookup(artist.clone(), Entity::new(artist.clone()));
app =
AppMachine::match_state(app.unwrap_browse().inner, MatchState::new(info, fetch)).into();

View File

@ -11,7 +11,7 @@ use musichoard::collection::{
Collection,
};
use crate::tui::lib::interface::musicbrainz::api::Match;
use crate::tui::lib::interface::musicbrainz::api::Entity;
pub enum AppState<B, I, R, S, F, M, E, C> {
Browse(B),
@ -215,13 +215,13 @@ pub type InputPublic<'app> = &'app tui_input::Input;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MatchOption<T> {
Some(Match<T>),
Some(Entity<T>),
CannotHaveMbid,
ManualInputMbid,
}
impl<T> From<Match<T>> for MatchOption<T> {
fn from(value: Match<T>) -> Self {
impl<T> From<Entity<T>> for MatchOption<T> {
fn from(value: Entity<T>) -> Self {
MatchOption::Some(value)
}
}

View File

@ -25,7 +25,7 @@ use musichoard::{
},
};
use crate::tui::lib::interface::musicbrainz::api::{Error, IMusicBrainz, Match, Paged};
use crate::tui::lib::interface::musicbrainz::api::{Entity, Error, IMusicBrainz, Paged};
// GRCOV_EXCL_START
pub struct MusicBrainz<Http> {
@ -39,7 +39,7 @@ impl<Http> MusicBrainz<Http> {
}
impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
fn lookup_artist(&mut self, mbid: &Mbid) -> Result<Match<ArtistMeta>, Error> {
fn lookup_artist(&mut self, mbid: &Mbid) -> Result<Entity<ArtistMeta>, Error> {
let request = LookupArtistRequest::new(mbid);
let mb_response = self.client.lookup_artist(&request)?;
@ -47,7 +47,7 @@ impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
Ok(from_lookup_artist_response(mb_response))
}
fn lookup_release_group(&mut self, mbid: &Mbid) -> Result<Match<AlbumMeta>, Error> {
fn lookup_release_group(&mut self, mbid: &Mbid) -> Result<Entity<AlbumMeta>, Error> {
let request = LookupReleaseGroupRequest::new(mbid);
let mb_response = self.client.lookup_release_group(&request)?;
@ -55,7 +55,7 @@ impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
Ok(from_lookup_release_group_response(mb_response))
}
fn search_artist(&mut self, artist: &ArtistMeta) -> Result<Vec<Match<ArtistMeta>>, Error> {
fn search_artist(&mut self, artist: &ArtistMeta) -> Result<Vec<Entity<ArtistMeta>>, Error> {
let query = SearchArtistRequest::new().string(&artist.id.name);
let paging = PageSettings::default();
@ -72,7 +72,7 @@ impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
&mut self,
arid: &Mbid,
album: &AlbumMeta,
) -> Result<Vec<Match<AlbumMeta>>, Error> {
) -> Result<Vec<Entity<AlbumMeta>>, Error> {
// Some release groups may have a promotional early release messing up the search. Searching
// with just the year should be enough anyway.
let date = AlbumDate::new(album.date.year, None, None);
@ -98,7 +98,7 @@ impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
&mut self,
artist: &Mbid,
paging: &mut PageSettings,
) -> Result<Paged<Vec<Match<AlbumMeta>>>, Error> {
) -> Result<Paged<Vec<Entity<AlbumMeta>>>, Error> {
let request = BrowseReleaseGroupRequest::artist(artist);
let mb_response = self.client.browse_release_group(&request, paging)?;
@ -139,47 +139,47 @@ fn from_mb_release_group_meta(meta: MbReleaseGroupMeta) -> AlbumMeta {
}
}
fn from_lookup_artist_response(entity: LookupArtistResponse) -> Match<ArtistMeta> {
let (item, disambiguation) = from_mb_artist_meta(entity.meta);
Match {
fn from_lookup_artist_response(response: LookupArtistResponse) -> Entity<ArtistMeta> {
let (entity, disambiguation) = from_mb_artist_meta(response.meta);
Entity {
score: None,
item,
entity,
disambiguation,
}
}
fn from_lookup_release_group_response(entity: LookupReleaseGroupResponse) -> Match<AlbumMeta> {
Match {
fn from_lookup_release_group_response(response: LookupReleaseGroupResponse) -> Entity<AlbumMeta> {
Entity {
score: None,
item: from_mb_release_group_meta(entity.meta),
entity: from_mb_release_group_meta(response.meta),
disambiguation: None,
}
}
fn from_search_artist_response_artist(entity: SearchArtistResponseArtist) -> Match<ArtistMeta> {
let (item, disambiguation) = from_mb_artist_meta(entity.meta);
Match {
score: Some(entity.score),
item,
fn from_search_artist_response_artist(response: SearchArtistResponseArtist) -> Entity<ArtistMeta> {
let (entity, disambiguation) = from_mb_artist_meta(response.meta);
Entity {
score: Some(response.score),
entity,
disambiguation,
}
}
fn from_search_release_group_response_release_group(
entity: SearchReleaseGroupResponseReleaseGroup,
) -> Match<AlbumMeta> {
Match {
score: Some(entity.score),
item: from_mb_release_group_meta(entity.meta),
response: SearchReleaseGroupResponseReleaseGroup,
) -> Entity<AlbumMeta> {
Entity {
score: Some(response.score),
entity: from_mb_release_group_meta(response.meta),
disambiguation: None,
}
}
fn from_browse_release_group_response(
entity: BrowseReleaseGroupResponse,
) -> Vec<Match<AlbumMeta>> {
) -> Vec<Entity<AlbumMeta>> {
let rgs = entity.release_groups.into_iter();
let metas = rgs.map(from_mb_release_group_meta);
metas.map(|meta| Match::item(meta)).collect()
metas.map(Entity::new).collect()
}
// GRCOV_EXCL_STOP

View File

@ -321,7 +321,7 @@ mod tests {
use crate::tui::{
event::{Event, EventError, MockIFetchCompleteEventSender},
lib::interface::musicbrainz::api::{Match, MockIMusicBrainz},
lib::interface::musicbrainz::api::{Entity, MockIMusicBrainz},
testmod::COLLECTION,
};
@ -408,11 +408,11 @@ mod tests {
VecDeque::from([MbParams::search_artist(artist)])
}
fn search_artist_expectations() -> (ArtistMeta, Vec<Match<ArtistMeta>>) {
fn search_artist_expectations() -> (ArtistMeta, Vec<Entity<ArtistMeta>>) {
let artist = COLLECTION[3].meta.clone();
let artist_match_1 = Match::with_score(artist.clone(), 100);
let artist_match_2 = Match::with_score(artist.clone(), 50);
let artist_match_1 = Entity::with_score(artist.clone(), 100);
let artist_match_2 = Entity::with_score(artist.clone(), 50);
let matches = vec![artist_match_1.clone(), artist_match_2.clone()];
(artist, matches)
@ -441,23 +441,23 @@ mod tests {
mb_ref_opt_unwrap(mbref).mbid().clone()
}
fn search_album_expectations_1() -> (AlbumMeta, Vec<Match<AlbumMeta>>) {
fn search_album_expectations_1() -> (AlbumMeta, Vec<Entity<AlbumMeta>>) {
let album_1 = COLLECTION[1].albums[0].meta.clone();
let album_4 = COLLECTION[1].albums[3].meta.clone();
let album_match_1_1 = Match::with_score(album_1.clone(), 100);
let album_match_1_2 = Match::with_score(album_4.clone(), 50);
let album_match_1_1 = Entity::with_score(album_1.clone(), 100);
let album_match_1_2 = Entity::with_score(album_4.clone(), 50);
let matches_1 = vec![album_match_1_1.clone(), album_match_1_2.clone()];
(album_1, matches_1)
}
fn search_album_expectations_4() -> (AlbumMeta, Vec<Match<AlbumMeta>>) {
fn search_album_expectations_4() -> (AlbumMeta, Vec<Entity<AlbumMeta>>) {
let album_1 = COLLECTION[1].albums[0].meta.clone();
let album_4 = COLLECTION[1].albums[3].meta.clone();
let album_match_4_1 = Match::with_score(album_4.clone(), 100);
let album_match_4_2 = Match::with_score(album_1.clone(), 30);
let album_match_4_1 = Entity::with_score(album_4.clone(), 100);
let album_match_4_2 = Entity::with_score(album_1.clone(), 30);
let matches_4 = vec![album_match_4_1.clone(), album_match_4_2.clone()];
(album_4, matches_4)
@ -539,7 +539,7 @@ mod tests {
fn lookup_artist_expectation(
musicbrainz: &mut MockIMusicBrainz,
mbid: &Mbid,
lookup: &Match<ArtistMeta>,
lookup: &Entity<ArtistMeta>,
) {
let result = Ok(lookup.clone());
musicbrainz
@ -554,7 +554,7 @@ mod tests {
let mut musicbrainz = musicbrainz();
let mbid = mbid();
let artist = COLLECTION[3].meta.clone();
let lookup = Match::item(artist.clone());
let lookup = Entity::new(artist.clone());
lookup_artist_expectation(&mut musicbrainz, &mbid, &lookup);
let mut event_sender = event_sender();
@ -581,7 +581,7 @@ mod tests {
fn lookup_release_group_expectation(
musicbrainz: &mut MockIMusicBrainz,
mbid: &Mbid,
lookup: &Match<AlbumMeta>,
lookup: &Entity<AlbumMeta>,
) {
let result = Ok(lookup.clone());
musicbrainz
@ -596,7 +596,7 @@ mod tests {
let mut musicbrainz = musicbrainz();
let mbid = mbid();
let album = COLLECTION[1].albums[0].meta.clone();
let lookup = Match::item(album.clone());
let lookup = Entity::new(album.clone());
lookup_release_group_expectation(&mut musicbrainz, &mbid, &lookup);
let mut event_sender = event_sender();
@ -627,7 +627,7 @@ mod tests {
fn search_artist_expectation(
musicbrainz: &mut MockIMusicBrainz,
artist: &ArtistMeta,
matches: &[Match<ArtistMeta>],
matches: &[Entity<ArtistMeta>],
) {
let result = Ok(matches.to_owned());
musicbrainz
@ -669,7 +669,7 @@ mod tests {
seq: &mut Sequence,
arid: &Mbid,
album: &AlbumMeta,
matches: &[Match<AlbumMeta>],
matches: &[Entity<AlbumMeta>],
) {
let result = Ok(matches.to_owned());
musicbrainz

View File

@ -10,33 +10,33 @@ use musichoard::{
#[cfg_attr(test, automock)]
pub trait IMusicBrainz {
fn lookup_artist(&mut self, mbid: &Mbid) -> Result<Match<ArtistMeta>, Error>;
fn lookup_release_group(&mut self, mbid: &Mbid) -> Result<Match<AlbumMeta>, Error>;
fn search_artist(&mut self, artist: &ArtistMeta) -> Result<Vec<Match<ArtistMeta>>, Error>;
fn lookup_artist(&mut self, mbid: &Mbid) -> Result<Entity<ArtistMeta>, Error>;
fn lookup_release_group(&mut self, mbid: &Mbid) -> Result<Entity<AlbumMeta>, Error>;
fn search_artist(&mut self, artist: &ArtistMeta) -> Result<Vec<Entity<ArtistMeta>>, Error>;
fn search_release_group(
&mut self,
arid: &Mbid,
album: &AlbumMeta,
) -> Result<Vec<Match<AlbumMeta>>, Error>;
) -> Result<Vec<Entity<AlbumMeta>>, Error>;
fn browse_release_group(
&mut self,
artist: &Mbid,
paging: &mut PageSettings,
) -> Result<Paged<Vec<Match<AlbumMeta>>>, Error>;
) -> Result<Paged<Vec<Entity<AlbumMeta>>>, Error>;
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Match<T> {
pub struct Entity<T> {
pub score: Option<u8>,
pub item: T,
pub entity: T,
pub disambiguation: Option<String>,
}
impl<T> Match<T> {
pub fn item(item: T) -> Self {
Match {
impl<T> Entity<T> {
pub fn new(entity: T) -> Self {
Entity {
score: None,
item,
entity,
disambiguation: None,
}
}

View File

@ -141,7 +141,7 @@ impl UiDisplay {
match match_option {
MatchOption::Some(match_artist) => format!(
"{}{}",
display_fn(&match_artist.item, &match_artist.disambiguation),
display_fn(&match_artist.entity, &match_artist.disambiguation),
Self::display_option_score(match_artist.score),
),
MatchOption::CannotHaveMbid => Self::display_cannot_have_mbid().to_string(),

View File

@ -22,7 +22,7 @@ impl<'a, 'b> MatchOverlay<'a, 'b> {
fn artists(
matching: &ArtistMeta,
matches: &'a Vec<MatchOption<ArtistMeta>>,
matches: &'a [MatchOption<ArtistMeta>],
state: &'b mut WidgetState,
) -> Self {
let matching = UiDisplay::display_artist_matching(matching);
@ -38,7 +38,7 @@ impl<'a, 'b> MatchOverlay<'a, 'b> {
fn albums(
matching: &AlbumMeta,
matches: &'a Vec<MatchOption<AlbumMeta>>,
matches: &'a [MatchOption<AlbumMeta>],
state: &'b mut WidgetState,
) -> Self {
let matching = UiDisplay::display_album_matching(matching);

View File

@ -204,7 +204,7 @@ mod tests {
use crate::tui::{
app::{AppPublic, AppPublicInner, Delta, MatchStatePublic},
lib::interface::musicbrainz::api::Match,
lib::interface::musicbrainz::api::Entity,
testmod::COLLECTION,
tests::terminal,
};
@ -331,7 +331,7 @@ mod tests {
fn artist_matches() -> MatchStateInfo {
let artist = artist_meta();
let artist_match = Match::with_score(artist.clone(), 80);
let artist_match = Entity::with_score(artist.clone(), 80);
let list = vec![artist_match.clone(), artist_match.clone()];
let mut info = MatchStateInfo::artist_search(artist, list);
@ -342,7 +342,7 @@ mod tests {
fn artist_lookup() -> MatchStateInfo {
let artist = artist_meta();
let artist_lookup = Match::item(artist.clone());
let artist_lookup = Entity::new(artist.clone());
let mut info = MatchStateInfo::artist_lookup(artist, artist_lookup);
info.push_cannot_have_mbid();
@ -369,7 +369,7 @@ mod tests {
fn album_matches() -> MatchStateInfo {
let artist_id = album_artist_id();
let album = album_meta();
let album_match = Match::with_score(album.clone(), 80);
let album_match = Entity::with_score(album.clone(), 80);
let list = vec![album_match.clone(), album_match.clone()];
let mut info = MatchStateInfo::album_search(artist_id, album, list);
@ -381,7 +381,7 @@ mod tests {
fn album_lookup() -> MatchStateInfo {
let artist_id = album_artist_id();
let album = album_meta();
let album_lookup = Match::item(album.clone());
let album_lookup = Entity::new(album.clone());
let mut info = MatchStateInfo::album_lookup(artist_id, album, album_lookup);
info.push_cannot_have_mbid();