Api submodule
This commit is contained in:
parent
38517caf4e
commit
f4472ee95d
@ -84,7 +84,7 @@ mod tests {
|
|||||||
machine::tests::{inner, inner_with_mb, music_hoard},
|
machine::tests::{inner, inner_with_mb, music_hoard},
|
||||||
Category, IApp, IAppAccess,
|
Category, IApp, IAppAccess,
|
||||||
},
|
},
|
||||||
lib::interface::musicbrainz::MockIMusicBrainz,
|
lib::interface::musicbrainz::api::MockIMusicBrainz,
|
||||||
testmod::COLLECTION,
|
testmod::COLLECTION,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,7 +18,10 @@ use crate::tui::{
|
|||||||
AppPublicState, AppState, IAppEventFetch, IAppInteractFetch, MatchStateInfo,
|
AppPublicState, AppState, IAppEventFetch, IAppInteractFetch, MatchStateInfo,
|
||||||
},
|
},
|
||||||
event::{Event, EventSender},
|
event::{Event, EventSender},
|
||||||
lib::interface::musicbrainz::{self, Error as MbError, IMusicBrainz},
|
lib::interface::musicbrainz::{
|
||||||
|
self,
|
||||||
|
api::{Error as MbError, IMusicBrainz},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct FetchState {
|
pub struct FetchState {
|
||||||
@ -147,7 +150,7 @@ impl AppMachine<FetchState> {
|
|||||||
fn send_fetch_result(
|
fn send_fetch_result(
|
||||||
fetch_tx: &FetchSender,
|
fetch_tx: &FetchSender,
|
||||||
events: &EventSender,
|
events: &EventSender,
|
||||||
result: Result<MatchStateInfo, musicbrainz::Error>,
|
result: Result<MatchStateInfo, musicbrainz::api::Error>,
|
||||||
) -> Result<(), ()> {
|
) -> Result<(), ()> {
|
||||||
// If receiver disconnects just drop the rest.
|
// If receiver disconnects just drop the rest.
|
||||||
fetch_tx.send(result).map_err(|_| ())?;
|
fetch_tx.send(result).map_err(|_| ())?;
|
||||||
@ -199,7 +202,10 @@ mod tests {
|
|||||||
AlbumMatches, ArtistMatches, IApp,
|
AlbumMatches, ArtistMatches, IApp,
|
||||||
},
|
},
|
||||||
event::EventReceiver,
|
event::EventReceiver,
|
||||||
lib::interface::musicbrainz::{self, Match, MockIMusicBrainz},
|
lib::interface::musicbrainz::{
|
||||||
|
self,
|
||||||
|
api::{Match, MockIMusicBrainz},
|
||||||
|
},
|
||||||
testmod::COLLECTION,
|
testmod::COLLECTION,
|
||||||
EventChannel,
|
EventChannel,
|
||||||
};
|
};
|
||||||
@ -415,7 +421,7 @@ mod tests {
|
|||||||
fn recv_ok_fetch_err() {
|
fn recv_ok_fetch_err() {
|
||||||
let (tx, rx) = mpsc::channel::<FetchResult>();
|
let (tx, rx) = mpsc::channel::<FetchResult>();
|
||||||
|
|
||||||
let fetch_result = Err(musicbrainz::Error::RateLimit);
|
let fetch_result = Err(musicbrainz::api::Error::RateLimit);
|
||||||
tx.send(fetch_result).unwrap();
|
tx.send(fetch_result).unwrap();
|
||||||
|
|
||||||
let inner = inner(music_hoard(COLLECTION.clone()));
|
let inner = inner(music_hoard(COLLECTION.clone()));
|
||||||
|
@ -179,7 +179,7 @@ mod tests {
|
|||||||
machine::tests::{inner, music_hoard},
|
machine::tests::{inner, music_hoard},
|
||||||
IApp, IAppAccess, IAppInput,
|
IApp, IAppAccess, IAppInput,
|
||||||
},
|
},
|
||||||
lib::interface::musicbrainz::Match,
|
lib::interface::musicbrainz::api::Match,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -16,7 +16,7 @@ use crate::tui::{
|
|||||||
IAppAccess, IAppBase, IAppState,
|
IAppAccess, IAppBase, IAppState,
|
||||||
},
|
},
|
||||||
event::EventSender,
|
event::EventSender,
|
||||||
lib::{interface::musicbrainz::IMusicBrainz, IMusicHoard},
|
lib::{interface::musicbrainz::api::IMusicBrainz, IMusicHoard},
|
||||||
};
|
};
|
||||||
|
|
||||||
use browse_state::BrowseState;
|
use browse_state::BrowseState;
|
||||||
@ -230,7 +230,7 @@ mod tests {
|
|||||||
|
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
app::{AppState, IApp, IAppInput, IAppInteractBrowse},
|
app::{AppState, IApp, IAppInput, IAppInteractBrowse},
|
||||||
lib::{interface::musicbrainz::MockIMusicBrainz, MockIMusicHoard},
|
lib::{interface::musicbrainz::api::MockIMusicBrainz, MockIMusicHoard},
|
||||||
EventChannel,
|
EventChannel,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ pub use selection::{Category, Delta, Selection, WidgetState};
|
|||||||
|
|
||||||
use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta, Collection};
|
use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta, Collection};
|
||||||
|
|
||||||
use crate::tui::lib::interface::musicbrainz::Match;
|
use crate::tui::lib::interface::musicbrainz::api::Match;
|
||||||
|
|
||||||
pub enum AppState<B, I, R, S, F, M, E, C> {
|
pub enum AppState<B, I, R, S, F, M, E, C> {
|
||||||
Browse(B),
|
Browse(B),
|
||||||
|
104
src/tui/lib/external/musicbrainz/api/mod.rs
vendored
Normal file
104
src/tui/lib/external/musicbrainz/api/mod.rs
vendored
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
//! Module for interacting with the [MusicBrainz API](https://musicbrainz.org/doc/MusicBrainz_API).
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use musichoard::{
|
||||||
|
collection::{
|
||||||
|
album::{AlbumDate, AlbumMeta, AlbumSeq},
|
||||||
|
artist::ArtistMeta,
|
||||||
|
musicbrainz::Mbid,
|
||||||
|
},
|
||||||
|
external::musicbrainz::{
|
||||||
|
api::{
|
||||||
|
search::{
|
||||||
|
SearchArtistRequest, SearchArtistResponseArtist, SearchReleaseGroupRequest,
|
||||||
|
SearchReleaseGroupResponseReleaseGroup,
|
||||||
|
},
|
||||||
|
MusicBrainzClient,
|
||||||
|
},
|
||||||
|
IMusicBrainzHttp,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::tui::lib::interface::musicbrainz::api::{Error, IMusicBrainz, Match};
|
||||||
|
|
||||||
|
// GRCOV_EXCL_START
|
||||||
|
pub struct MusicBrainz<Http> {
|
||||||
|
client: MusicBrainzClient<Http>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Http> MusicBrainz<Http> {
|
||||||
|
pub fn new(client: MusicBrainzClient<Http>) -> Self {
|
||||||
|
MusicBrainz { client }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
|
||||||
|
fn search_artist(&mut self, artist: &ArtistMeta) -> Result<Vec<Match<ArtistMeta>>, Error> {
|
||||||
|
let query = SearchArtistRequest::new().string(&artist.id.name);
|
||||||
|
|
||||||
|
let mb_response = self.client.search_artist(query)?;
|
||||||
|
|
||||||
|
Ok(mb_response
|
||||||
|
.artists
|
||||||
|
.into_iter()
|
||||||
|
.map(from_search_artist_response_artist)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_release_group(
|
||||||
|
&mut self,
|
||||||
|
arid: &Mbid,
|
||||||
|
album: &AlbumMeta,
|
||||||
|
) -> Result<Vec<Match<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);
|
||||||
|
|
||||||
|
let query = SearchReleaseGroupRequest::new()
|
||||||
|
.arid(arid)
|
||||||
|
.and()
|
||||||
|
.first_release_date(&date)
|
||||||
|
.and()
|
||||||
|
.release_group(&album.id.title);
|
||||||
|
|
||||||
|
let mb_response = self.client.search_release_group(query)?;
|
||||||
|
|
||||||
|
Ok(mb_response
|
||||||
|
.release_groups
|
||||||
|
.into_iter()
|
||||||
|
.map(from_search_release_group_response_release_group)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_search_artist_response_artist(entity: SearchArtistResponseArtist) -> Match<ArtistMeta> {
|
||||||
|
Match {
|
||||||
|
score: entity.score,
|
||||||
|
item: ArtistMeta {
|
||||||
|
id: entity.name,
|
||||||
|
sort: entity.sort.map(Into::into),
|
||||||
|
musicbrainz: Some(entity.id.into()),
|
||||||
|
properties: HashMap::new(),
|
||||||
|
},
|
||||||
|
disambiguation: entity.disambiguation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_search_release_group_response_release_group(
|
||||||
|
entity: SearchReleaseGroupResponseReleaseGroup,
|
||||||
|
) -> Match<AlbumMeta> {
|
||||||
|
Match {
|
||||||
|
score: entity.score,
|
||||||
|
item: AlbumMeta {
|
||||||
|
id: entity.title,
|
||||||
|
date: entity.first_release_date,
|
||||||
|
seq: AlbumSeq::default(),
|
||||||
|
musicbrainz: Some(entity.id.into()),
|
||||||
|
primary_type: Some(entity.primary_type),
|
||||||
|
secondary_types: entity.secondary_types.unwrap_or_default(),
|
||||||
|
},
|
||||||
|
disambiguation: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// GRCOV_EXCL_STOP
|
105
src/tui/lib/external/musicbrainz/mod.rs
vendored
105
src/tui/lib/external/musicbrainz/mod.rs
vendored
@ -1,104 +1 @@
|
|||||||
//! Module for interacting with the [MusicBrainz API](https://musicbrainz.org/doc/MusicBrainz_API).
|
pub mod api;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use musichoard::{
|
|
||||||
collection::{
|
|
||||||
album::{AlbumDate, AlbumMeta, AlbumSeq},
|
|
||||||
artist::ArtistMeta,
|
|
||||||
musicbrainz::Mbid,
|
|
||||||
},
|
|
||||||
external::musicbrainz::{
|
|
||||||
api::{
|
|
||||||
search::{
|
|
||||||
SearchArtistRequest, SearchArtistResponseArtist, SearchReleaseGroupRequest,
|
|
||||||
SearchReleaseGroupResponseReleaseGroup,
|
|
||||||
},
|
|
||||||
MusicBrainzClient,
|
|
||||||
},
|
|
||||||
IMusicBrainzHttp,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::tui::lib::interface::musicbrainz::{Error, IMusicBrainz, Match};
|
|
||||||
|
|
||||||
// GRCOV_EXCL_START
|
|
||||||
pub struct MusicBrainz<Http> {
|
|
||||||
client: MusicBrainzClient<Http>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Http> MusicBrainz<Http> {
|
|
||||||
pub fn new(client: MusicBrainzClient<Http>) -> Self {
|
|
||||||
MusicBrainz { client }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
|
|
||||||
fn search_artist(&mut self, artist: &ArtistMeta) -> Result<Vec<Match<ArtistMeta>>, Error> {
|
|
||||||
let query = SearchArtistRequest::new().string(&artist.id.name);
|
|
||||||
|
|
||||||
let mb_response = self.client.search_artist(query)?;
|
|
||||||
|
|
||||||
Ok(mb_response
|
|
||||||
.artists
|
|
||||||
.into_iter()
|
|
||||||
.map(from_search_artist_response_artist)
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_release_group(
|
|
||||||
&mut self,
|
|
||||||
arid: &Mbid,
|
|
||||||
album: &AlbumMeta,
|
|
||||||
) -> Result<Vec<Match<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);
|
|
||||||
|
|
||||||
let query = SearchReleaseGroupRequest::new()
|
|
||||||
.arid(arid)
|
|
||||||
.and()
|
|
||||||
.first_release_date(&date)
|
|
||||||
.and()
|
|
||||||
.release_group(&album.id.title);
|
|
||||||
|
|
||||||
let mb_response = self.client.search_release_group(query)?;
|
|
||||||
|
|
||||||
Ok(mb_response
|
|
||||||
.release_groups
|
|
||||||
.into_iter()
|
|
||||||
.map(from_search_release_group_response_release_group)
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_search_artist_response_artist(entity: SearchArtistResponseArtist) -> Match<ArtistMeta> {
|
|
||||||
Match {
|
|
||||||
score: entity.score,
|
|
||||||
item: ArtistMeta {
|
|
||||||
id: entity.name,
|
|
||||||
sort: entity.sort.map(Into::into),
|
|
||||||
musicbrainz: Some(entity.id.into()),
|
|
||||||
properties: HashMap::new(),
|
|
||||||
},
|
|
||||||
disambiguation: entity.disambiguation,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_search_release_group_response_release_group(
|
|
||||||
entity: SearchReleaseGroupResponseReleaseGroup,
|
|
||||||
) -> Match<AlbumMeta> {
|
|
||||||
Match {
|
|
||||||
score: entity.score,
|
|
||||||
item: AlbumMeta {
|
|
||||||
id: entity.title,
|
|
||||||
date: entity.first_release_date,
|
|
||||||
seq: AlbumSeq::default(),
|
|
||||||
musicbrainz: Some(entity.id.into()),
|
|
||||||
primary_type: Some(entity.primary_type),
|
|
||||||
secondary_types: entity.secondary_types.unwrap_or_default(),
|
|
||||||
},
|
|
||||||
disambiguation: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// GRCOV_EXCL_STOP
|
|
||||||
|
26
src/tui/lib/interface/musicbrainz/api/mod.rs
Normal file
26
src/tui/lib/interface/musicbrainz/api/mod.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//! Module for accessing MusicBrainz metadata.
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use mockall::automock;
|
||||||
|
|
||||||
|
use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta, musicbrainz::Mbid};
|
||||||
|
|
||||||
|
/// Trait for interacting with the MusicBrainz API.
|
||||||
|
#[cfg_attr(test, automock)]
|
||||||
|
pub trait IMusicBrainz {
|
||||||
|
fn search_artist(&mut self, name: &ArtistMeta) -> Result<Vec<Match<ArtistMeta>>, Error>;
|
||||||
|
fn search_release_group(
|
||||||
|
&mut self,
|
||||||
|
arid: &Mbid,
|
||||||
|
album: &AlbumMeta,
|
||||||
|
) -> Result<Vec<Match<AlbumMeta>>, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Match<T> {
|
||||||
|
pub score: u8,
|
||||||
|
pub item: T,
|
||||||
|
pub disambiguation: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Error = musichoard::external::musicbrainz::api::Error;
|
@ -1,26 +1 @@
|
|||||||
//! Module for accessing MusicBrainz metadata.
|
pub mod api;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
use mockall::automock;
|
|
||||||
|
|
||||||
use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta, musicbrainz::Mbid};
|
|
||||||
|
|
||||||
/// Trait for interacting with the MusicBrainz API.
|
|
||||||
#[cfg_attr(test, automock)]
|
|
||||||
pub trait IMusicBrainz {
|
|
||||||
fn search_artist(&mut self, name: &ArtistMeta) -> Result<Vec<Match<ArtistMeta>>, Error>;
|
|
||||||
fn search_release_group(
|
|
||||||
&mut self,
|
|
||||||
arid: &Mbid,
|
|
||||||
album: &AlbumMeta,
|
|
||||||
) -> Result<Vec<Match<AlbumMeta>>, Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Match<T> {
|
|
||||||
pub score: u8,
|
|
||||||
pub item: T,
|
|
||||||
pub disambiguation: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Error = musichoard::external::musicbrainz::api::Error;
|
|
||||||
|
@ -8,7 +8,7 @@ mod ui;
|
|||||||
pub use app::App;
|
pub use app::App;
|
||||||
pub use event::EventChannel;
|
pub use event::EventChannel;
|
||||||
pub use handler::EventHandler;
|
pub use handler::EventHandler;
|
||||||
pub use lib::external::musicbrainz::MusicBrainz;
|
pub use lib::external::musicbrainz::api::MusicBrainz;
|
||||||
pub use listener::EventListener;
|
pub use listener::EventListener;
|
||||||
pub use ui::Ui;
|
pub use ui::Ui;
|
||||||
|
|
||||||
@ -175,7 +175,7 @@ mod tests {
|
|||||||
use std::{io, thread};
|
use std::{io, thread};
|
||||||
|
|
||||||
use event::EventSender;
|
use event::EventSender;
|
||||||
use lib::interface::musicbrainz::MockIMusicBrainz;
|
use lib::interface::musicbrainz::api::MockIMusicBrainz;
|
||||||
use ratatui::{backend::TestBackend, Terminal};
|
use ratatui::{backend::TestBackend, Terminal};
|
||||||
|
|
||||||
use musichoard::collection::Collection;
|
use musichoard::collection::Collection;
|
||||||
|
@ -207,7 +207,7 @@ mod tests {
|
|||||||
|
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
app::{AppPublic, AppPublicInner, Delta, MatchOption, MatchStatePublic},
|
app::{AppPublic, AppPublicInner, Delta, MatchOption, MatchStatePublic},
|
||||||
lib::interface::musicbrainz::Match,
|
lib::interface::musicbrainz::api::Match,
|
||||||
testmod::COLLECTION,
|
testmod::COLLECTION,
|
||||||
tests::terminal,
|
tests::terminal,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user