Add search_artist binding to TUI
This commit is contained in:
parent
ab8bdf9f3e
commit
f24737d5fa
@ -104,7 +104,7 @@ fn main() {
|
||||
|
||||
match opt.entity {
|
||||
OptEntity::Artist(opt_artist) => {
|
||||
let query = SearchArtistRequest::new().no_field(&opt_artist.string);
|
||||
let query = SearchArtistRequest::new().string(&opt_artist.string);
|
||||
|
||||
println!("Query: {query}");
|
||||
|
||||
|
10
src/external/musicbrainz/api/search/artist.rs
vendored
10
src/external/musicbrainz/api/search/artist.rs
vendored
@ -10,20 +10,20 @@ use crate::{
|
||||
use super::query::{impl_term, EmptyQuery, EmptyQueryJoin, Query, QueryJoin};
|
||||
|
||||
pub enum SearchArtist<'a> {
|
||||
NoField(&'a str),
|
||||
String(&'a str),
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for SearchArtist<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::NoField(s) => write!(f, "\"{s}\""),
|
||||
Self::String(s) => write!(f, "\"{s}\""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type SearchArtistRequest<'a> = Query<SearchArtist<'a>>;
|
||||
|
||||
impl_term!(no_field, SearchArtist<'a>, NoField, &'a str);
|
||||
impl_term!(string, SearchArtist<'a>, String, &'a str);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct SearchArtistResponse {
|
||||
@ -116,7 +116,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn search_no_field() {
|
||||
fn search_string() {
|
||||
let mut http = MockIMusicBrainzHttp::new();
|
||||
let url = format!(
|
||||
"https://musicbrainz.org/ws/2\
|
||||
@ -137,7 +137,7 @@ mod tests {
|
||||
|
||||
let name = "an artist";
|
||||
|
||||
let query = SearchArtistRequest::new().no_field(name);
|
||||
let query = SearchArtistRequest::new().string(name);
|
||||
|
||||
let matches = client.search_artist(query).unwrap();
|
||||
assert_eq!(matches, response);
|
||||
|
40
src/external/musicbrainz/api/search/query.rs
vendored
40
src/external/musicbrainz/api/search/query.rs
vendored
@ -225,52 +225,52 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
pub enum TestEntity<'a> {
|
||||
NoField(&'a str),
|
||||
String(&'a str),
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for TestEntity<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::NoField(s) => write!(f, "\"{s}\""),
|
||||
Self::String(s) => write!(f, "\"{s}\""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type TestEntityRequest<'a> = Query<TestEntity<'a>>;
|
||||
|
||||
impl_term!(no_field, TestEntity<'a>, NoField, &'a str);
|
||||
impl_term!(string, TestEntity<'a>, String, &'a str);
|
||||
|
||||
#[test]
|
||||
fn lucene_logical() {
|
||||
let query = TestEntityRequest::new()
|
||||
.no_field("jakarta apache")
|
||||
.string("jakarta apache")
|
||||
.or()
|
||||
.no_field("jakarta");
|
||||
.string("jakarta");
|
||||
assert_eq!(format!("{query}"), "\"jakarta apache\" OR \"jakarta\"");
|
||||
|
||||
let query = TestEntityRequest::new()
|
||||
.no_field("jakarta apache")
|
||||
.string("jakarta apache")
|
||||
.and()
|
||||
.no_field("jakarta");
|
||||
.string("jakarta");
|
||||
assert_eq!(format!("{query}"), "\"jakarta apache\" AND \"jakarta\"");
|
||||
|
||||
let query = TestEntityRequest::new()
|
||||
.require()
|
||||
.no_field("jakarta")
|
||||
.string("jakarta")
|
||||
.or()
|
||||
.no_field("lucene");
|
||||
.string("lucene");
|
||||
assert_eq!(format!("{query}"), "+\"jakarta\" OR \"lucene\"");
|
||||
|
||||
let query = TestEntityRequest::new()
|
||||
.no_field("lucene")
|
||||
.string("lucene")
|
||||
.require()
|
||||
.no_field("jakarta");
|
||||
.string("jakarta");
|
||||
assert_eq!(format!("{query}"), "\"lucene\" +\"jakarta\"");
|
||||
|
||||
let query = TestEntityRequest::new()
|
||||
.no_field("jakarta apache")
|
||||
.string("jakarta apache")
|
||||
.not()
|
||||
.no_field("Apache Lucene");
|
||||
.string("Apache Lucene");
|
||||
assert_eq!(
|
||||
format!("{query}"),
|
||||
"\"jakarta apache\" NOT \"Apache Lucene\""
|
||||
@ -278,18 +278,18 @@ mod tests {
|
||||
|
||||
let query = TestEntityRequest::new()
|
||||
.prohibit()
|
||||
.no_field("Apache Lucene")
|
||||
.string("Apache Lucene")
|
||||
.or()
|
||||
.no_field("jakarta apache");
|
||||
.string("jakarta apache");
|
||||
assert_eq!(
|
||||
format!("{query}"),
|
||||
"-\"Apache Lucene\" OR \"jakarta apache\""
|
||||
);
|
||||
|
||||
let query = TestEntityRequest::new()
|
||||
.no_field("jakarta apache")
|
||||
.string("jakarta apache")
|
||||
.prohibit()
|
||||
.no_field("Apache Lucene");
|
||||
.string("Apache Lucene");
|
||||
assert_eq!(format!("{query}"), "\"jakarta apache\" -\"Apache Lucene\"");
|
||||
}
|
||||
|
||||
@ -298,12 +298,12 @@ mod tests {
|
||||
let query = TestEntityRequest::new()
|
||||
.expression(
|
||||
TestEntityRequest::new()
|
||||
.no_field("jakarta")
|
||||
.string("jakarta")
|
||||
.or()
|
||||
.no_field("apache"),
|
||||
.string("apache"),
|
||||
)
|
||||
.and()
|
||||
.no_field("website");
|
||||
.string("website");
|
||||
assert_eq!(
|
||||
format!("{query}"),
|
||||
"(\"jakarta\" OR \"apache\") AND \"website\""
|
||||
|
@ -15,7 +15,7 @@ use crate::{
|
||||
use super::query::{impl_term, EmptyQuery, EmptyQueryJoin, Query, QueryJoin};
|
||||
|
||||
pub enum SearchReleaseGroup<'a> {
|
||||
NoField(&'a str),
|
||||
String(&'a str),
|
||||
Arid(&'a Mbid),
|
||||
FirstReleaseDate(&'a AlbumDate),
|
||||
ReleaseGroup(&'a str),
|
||||
@ -25,7 +25,7 @@ pub enum SearchReleaseGroup<'a> {
|
||||
impl<'a> fmt::Display for SearchReleaseGroup<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::NoField(s) => write!(f, "\"{s}\""),
|
||||
Self::String(s) => write!(f, "\"{s}\""),
|
||||
Self::Arid(arid) => write!(f, "arid:{}", arid.uuid().as_hyphenated()),
|
||||
Self::FirstReleaseDate(date) => write!(
|
||||
f,
|
||||
@ -40,7 +40,7 @@ impl<'a> fmt::Display for SearchReleaseGroup<'a> {
|
||||
|
||||
pub type SearchReleaseGroupRequest<'a> = Query<SearchReleaseGroup<'a>>;
|
||||
|
||||
impl_term!(no_field, SearchReleaseGroup<'a>, NoField, &'a str);
|
||||
impl_term!(string, SearchReleaseGroup<'a>, String, &'a str);
|
||||
impl_term!(arid, SearchReleaseGroup<'a>, Arid, &'a Mbid);
|
||||
impl_term!(
|
||||
first_release_date,
|
||||
@ -150,7 +150,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn search_no_field() {
|
||||
fn search_string() {
|
||||
let mut http = MockIMusicBrainzHttp::new();
|
||||
let url = format!(
|
||||
"https://musicbrainz.org/ws/2\
|
||||
@ -171,7 +171,7 @@ mod tests {
|
||||
|
||||
let title = "an album";
|
||||
|
||||
let query = SearchReleaseGroupRequest::new().no_field(title);
|
||||
let query = SearchReleaseGroupRequest::new().string(title);
|
||||
|
||||
let matches = client.search_release_group(query).unwrap();
|
||||
assert_eq!(matches, response);
|
||||
|
@ -146,6 +146,7 @@ mod tests {
|
||||
let album_match_1_1 = Match {
|
||||
score: 100,
|
||||
item: album_1_1,
|
||||
disambiguation: None,
|
||||
};
|
||||
|
||||
let mut album_1_2 = album_1.clone();
|
||||
@ -154,6 +155,7 @@ mod tests {
|
||||
let album_match_1_2 = Match {
|
||||
score: 100,
|
||||
item: album_1_2,
|
||||
disambiguation: None,
|
||||
};
|
||||
|
||||
let matches_info_1 = AppMatchesInfo {
|
||||
@ -172,6 +174,7 @@ mod tests {
|
||||
let album_match_2_1 = Match {
|
||||
score: 100,
|
||||
item: album_2_1,
|
||||
disambiguation: None,
|
||||
};
|
||||
|
||||
let matches_info_2 = AppMatchesInfo {
|
||||
|
36
src/tui/lib/external/musicbrainz/mod.rs
vendored
36
src/tui/lib/external/musicbrainz/mod.rs
vendored
@ -3,11 +3,15 @@
|
||||
use musichoard::{
|
||||
collection::{
|
||||
album::{Album, AlbumDate},
|
||||
artist::Artist,
|
||||
musicbrainz::Mbid,
|
||||
},
|
||||
external::musicbrainz::{
|
||||
api::{
|
||||
search::{SearchReleaseGroupRequest, SearchReleaseGroupResponseReleaseGroup},
|
||||
search::{
|
||||
SearchArtistRequest, SearchArtistResponseArtist, SearchReleaseGroupRequest,
|
||||
SearchReleaseGroupResponseReleaseGroup,
|
||||
},
|
||||
MusicBrainzClient,
|
||||
},
|
||||
IMusicBrainzHttp,
|
||||
@ -28,6 +32,21 @@ impl<Http> MusicBrainz<Http> {
|
||||
}
|
||||
|
||||
impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
|
||||
fn search_artist(
|
||||
&mut self,
|
||||
name: &str,
|
||||
) -> Result<Vec<Match<musichoard::collection::artist::Artist>>, Error> {
|
||||
let query = SearchArtistRequest::new().string(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,
|
||||
@ -54,6 +73,21 @@ impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_search_artist_response_artist(entity: SearchArtistResponseArtist) -> Match<Artist> {
|
||||
let mut artist = Artist::new(entity.name);
|
||||
if let Some(sort) = entity.sort {
|
||||
artist.set_sort_key(sort);
|
||||
}
|
||||
artist.set_musicbrainz_ref(entity.id.into());
|
||||
|
||||
let mut artist_match = Match::new(entity.score, artist);
|
||||
if let Some(disambiguation) = entity.disambiguation {
|
||||
artist_match.set_disambiguation(disambiguation);
|
||||
}
|
||||
|
||||
artist_match
|
||||
}
|
||||
|
||||
fn from_search_release_group_response_release_group(
|
||||
entity: SearchReleaseGroupResponseReleaseGroup,
|
||||
) -> Match<Album> {
|
||||
|
@ -3,11 +3,12 @@
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
use musichoard::collection::{album::Album, musicbrainz::Mbid};
|
||||
use musichoard::collection::{album::Album, artist::Artist, musicbrainz::Mbid};
|
||||
|
||||
/// Trait for interacting with the MusicBrainz API.
|
||||
#[cfg_attr(test, automock)]
|
||||
pub trait IMusicBrainz {
|
||||
fn search_artist(&mut self, name: &str) -> Result<Vec<Match<Artist>>, Error>;
|
||||
fn search_release_group(
|
||||
&mut self,
|
||||
arid: &Mbid,
|
||||
@ -19,11 +20,16 @@ pub trait IMusicBrainz {
|
||||
pub struct Match<T> {
|
||||
pub score: u8,
|
||||
pub item: T,
|
||||
pub disambiguation: Option<String>,
|
||||
}
|
||||
|
||||
impl<T> Match<T> {
|
||||
pub fn new(score: u8, item: T) -> Self {
|
||||
Match { score, item }
|
||||
Match { score, item, disambiguation: None }
|
||||
}
|
||||
|
||||
pub fn set_disambiguation<S: Into<String>>(&mut self, disambiguation: S) {
|
||||
self.disambiguation = Some(disambiguation.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,6 +230,7 @@ mod tests {
|
||||
let album_match = Match {
|
||||
score: 80,
|
||||
item: album.clone(),
|
||||
disambiguation: None,
|
||||
};
|
||||
|
||||
let mut app = AppPublic {
|
||||
|
Loading…
Reference in New Issue
Block a user