Decide carefully where external::musicbrainz belongs #196

Merged
wojtek merged 11 commits from 193---decide-carefully-where-external--musicbrainz-belongs into main 2024-08-28 18:21:13 +02:00
2 changed files with 86 additions and 30 deletions
Showing only changes of commit 76173e0468 - Show all commits

View File

@ -15,10 +15,7 @@ use url::form_urlencoded;
use crate::{ use crate::{
collection::album::AlbumDate, collection::album::AlbumDate,
core::{ core::{
collection::{ collection::album::{AlbumPrimaryType, AlbumSecondaryType},
album::{Album, AlbumPrimaryType, AlbumSecondaryType},
musicbrainz::IMusicBrainzRef,
},
interface::musicbrainz::Mbid, interface::musicbrainz::Mbid,
}, },
interface::musicbrainz::MbidError, interface::musicbrainz::MbidError,
@ -81,44 +78,91 @@ impl<Http> MusicBrainzClient<Http> {
} }
impl<Http: IMusicBrainzHttp> MusicBrainzClient<Http> { impl<Http: IMusicBrainzHttp> MusicBrainzClient<Http> {
fn display_album_date(date: &AlbumDate) -> Option<String> {
match date.year {
Some(year) => match date.month {
Some(month) => match date.day {
Some(day) => Some(format!("{year}-{month:02}-{day:02}")),
None => Some(format!("{year}-{month:02}")),
},
None => Some(format!("{year}")),
},
None => None,
}
}
pub fn search_release_group( pub fn search_release_group(
&mut self, &mut self,
arid: &Mbid, request: SearchReleaseGroupRequest,
album: &Album, ) -> Result<SearchReleaseGroupResponse, Error> {
) -> Result<ResponseSearchReleaseGroup, Error> { let mut query: Vec<String> = vec![];
let title = &album.id.title;
let arid = arid.uuid().as_hyphenated().to_string();
let mut query = format!("arid:{arid}");
match album.musicbrainz { if let Some(arid) = request.arid {
Some(ref mbref) => { query.push(format!("arid:{}", arid.uuid().as_hyphenated().to_string()));
let rgid = mbref.mbid().uuid().as_hyphenated().to_string();
query.push_str(&format!(" AND rgid:{rgid}"));
}
None => {
query.push_str(&format!(" AND releasegroup:\"{title}\""));
if let Some(year) = album.date.year {
query.push_str(&format!(" AND firstreleasedate:{year}"));
} }
if let Some(date) = request.first_release_date {
if let Some(date_string) = Self::display_album_date(date) {
query.push(format!("firstreleasedate:{date_string}"))
} }
} }
let query: String = form_urlencoded::byte_serialize(query.as_bytes()).collect(); if let Some(release_group) = request.release_group {
query.push(format!("releasegroup:\"{release_group}\""));
}
if let Some(rgid) = request.rgid {
query.push(format!("rgid:{}", rgid.uuid().as_hyphenated().to_string()));
}
let query: String =
form_urlencoded::byte_serialize(query.join(" AND ").as_bytes()).collect();
let url = format!("{MB_BASE_URL}/release-group?query={query}"); let url = format!("{MB_BASE_URL}/release-group?query={query}");
Ok(self.http.get(&url)?) Ok(self.http.get(&url)?)
} }
} }
#[derive(Default)]
pub struct SearchReleaseGroupRequest<'a> {
arid: Option<&'a Mbid>,
first_release_date: Option<&'a AlbumDate>,
release_group: Option<&'a str>,
rgid: Option<&'a Mbid>,
}
impl<'a> SearchReleaseGroupRequest<'a> {
pub fn arid(&mut self, arid: &'a Mbid) -> &mut Self {
self.arid = Some(arid);
self
}
pub fn first_release_date(&mut self, first_release_date: &'a AlbumDate) -> &mut Self {
self.first_release_date = Some(first_release_date);
self
}
pub fn release_group(&mut self, release_group: &'a str) -> &mut Self {
self.release_group = Some(release_group);
self
}
pub fn rgid(&mut self, rgid: &'a Mbid) -> &mut Self {
self.rgid = Some(rgid);
self
}
}
// TODO: Separate deserialize types from internal types like in JSON code.
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all(deserialize = "kebab-case"))] #[serde(rename_all(deserialize = "kebab-case"))]
pub struct ResponseSearchReleaseGroup { pub struct SearchReleaseGroupResponse {
pub release_groups: Vec<SearchReleaseGroup>, pub release_groups: Vec<SearchReleaseGroupResponseUnit>,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all(deserialize = "kebab-case"))] #[serde(rename_all(deserialize = "kebab-case"))]
pub struct SearchReleaseGroup { pub struct SearchReleaseGroupResponseUnit {
pub score: u8, pub score: u8,
pub id: Mbid, pub id: Mbid,
pub title: String, pub title: String,

View File

@ -1,8 +1,11 @@
//! Module for interacting with the [MusicBrainz API](https://musicbrainz.org/doc/MusicBrainz_API). //! Module for interacting with the [MusicBrainz API](https://musicbrainz.org/doc/MusicBrainz_API).
use musichoard::{ use musichoard::{
collection::album::Album, collection::{album::Album, musicbrainz::IMusicBrainzRef},
external::musicbrainz::{IMusicBrainzHttp, MusicBrainzClient, SearchReleaseGroup}, external::musicbrainz::{
IMusicBrainzHttp, MusicBrainzClient, SearchReleaseGroupRequest,
SearchReleaseGroupResponseUnit,
},
interface::musicbrainz::Mbid, interface::musicbrainz::Mbid,
}; };
@ -24,16 +27,25 @@ impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
arid: &Mbid, arid: &Mbid,
album: &Album, album: &Album,
) -> Result<Vec<Match<Album>>, Error> { ) -> Result<Vec<Match<Album>>, Error> {
let mb_response = self.client.search_release_group(arid, album)?; let mut request = SearchReleaseGroupRequest::default();
request
.arid(arid)
.release_group(&album.id.title)
.first_release_date(&album.date);
if let Some(ref mbref) = album.musicbrainz {
request.rgid(mbref.mbid());
}
let mb_response = self.client.search_release_group(request)?;
Ok(mb_response Ok(mb_response
.release_groups .release_groups
.into_iter() .into_iter()
.map(from_search_release_group) .map(from_search_release_group_response_unit)
.collect()) .collect())
} }
} }
fn from_search_release_group(entity: SearchReleaseGroup) -> Match<Album> { fn from_search_release_group_response_unit(entity: SearchReleaseGroupResponseUnit) -> Match<Album> {
let mut album = Album::new( let mut album = Album::new(
entity.title, entity.title,
entity.first_release_date, entity.first_release_date,