From e008bf1b104e439d87851b31763d1cd892531013 Mon Sep 17 00:00:00 2001 From: Wojciech Kozlowski Date: Sun, 17 Mar 2024 14:38:33 +0100 Subject: [PATCH 1/2] Search by RGID if available --- .../musicbrainz_api/search_release_group.rs | 40 ++++++++++++++++++- src/external/musicbrainz/api/mod.rs | 19 ++++++--- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/examples/musicbrainz_api/search_release_group.rs b/examples/musicbrainz_api/search_release_group.rs index 2a84ab7..48e4f7f 100644 --- a/examples/musicbrainz_api/search_release_group.rs +++ b/examples/musicbrainz_api/search_release_group.rs @@ -21,6 +21,20 @@ struct Opt { #[structopt(help = "Release group's artist MBID")] arid: Uuid, + #[structopt(subcommand)] + command: OptCommand, +} + +#[derive(StructOpt)] +enum OptCommand { + #[structopt(about = "Search by title (and date)")] + Title(OptTitle), + #[structopt(about = "Search by release group MBID")] + Rgid(OptRgid), +} + +#[derive(StructOpt)] +struct OptTitle { #[structopt(help = "Release group title")] title: String, @@ -28,6 +42,12 @@ struct Opt { date: Option, } +#[derive(StructOpt)] +struct OptRgid { + #[structopt(help = "Release group MBID")] + rgid: Uuid, +} + struct Date(AlbumDate); impl FromStr for Date { @@ -64,8 +84,24 @@ fn main() { let mut api = MusicBrainzApi::new(client); let arid: Mbid = opt.arid.into(); - let date: AlbumDate = opt.date.map(Into::into).unwrap_or_default(); - let album = Album::new(AlbumId::new(opt.title), date, None, vec![]); + + let album = match opt.command { + OptCommand::Title(opt_title) => { + let date: AlbumDate = opt_title.date.map(Into::into).unwrap_or_default(); + Album::new(AlbumId::new(opt_title.title), date, None, vec![]) + } + OptCommand::Rgid(opt_rgid) => { + let mut album = Album::new( + AlbumId::new(String::default()), + AlbumDate::default(), + None, + vec![], + ); + album.set_musicbrainz_ref(opt_rgid.rgid.into()); + album + } + }; + let matches = api .search_release_group(&arid, album) .expect("failed to make API call"); diff --git a/src/external/musicbrainz/api/mod.rs b/src/external/musicbrainz/api/mod.rs index 3c649b4..464a726 100644 --- a/src/external/musicbrainz/api/mod.rs +++ b/src/external/musicbrainz/api/mod.rs @@ -11,7 +11,7 @@ use mockall::automock; use crate::core::{ collection::{ album::{Album, AlbumDate, AlbumPrimaryType, AlbumSecondaryType}, - musicbrainz::MbAlbumRef, + musicbrainz::{IMusicBrainzRef, MbAlbumRef}, }, interface::musicbrainz::{Error, IMusicBrainz, Match, Mbid}, }; @@ -74,10 +74,19 @@ impl IMusicBrainz for MusicBrainzApi { ) -> Result>, Error> { let title = &album.id.title; let arid = arid.uuid().as_hyphenated().to_string(); - let mut query = format!("releasegroup:\"{title}\" AND arid:{arid}"); + let mut query = format!("arid:{arid}"); - if let Some(year) = album.date.year { - query.push_str(&format!(" AND firstreleasedate:{year}")); + match album.musicbrainz { + Some(mbref) => { + 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}")); + } + } } let query: String = form_urlencoded::byte_serialize(query.as_bytes()).collect(); @@ -292,7 +301,7 @@ mod tests { let url = format!( "https://musicbrainz.org/ws/2\ /release-group\ - ?query=releasegroup%3A%22{title}%22+AND+arid%3A{arid}+AND+firstreleasedate%3A{year}", + ?query=arid%3A{arid}+AND+releasegroup%3A%22{title}%22+AND+firstreleasedate%3A{year}", title = "an+album", arid = "00000000-0000-0000-0000-000000000000", year = "1986" -- 2.45.2 From 600a5a44c558aa59a48259336dcac09614e60065 Mon Sep 17 00:00:00 2001 From: Wojciech Kozlowski Date: Sun, 17 Mar 2024 17:14:15 +0100 Subject: [PATCH 2/2] Add unit test --- .../musicbrainz_api/search_release_group.rs | 2 +- src/core/interface/musicbrainz/mod.rs | 6 +-- src/external/musicbrainz/api/mod.rs | 53 +++++++++++++------ 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/examples/musicbrainz_api/search_release_group.rs b/examples/musicbrainz_api/search_release_group.rs index 48e4f7f..0b27b95 100644 --- a/examples/musicbrainz_api/search_release_group.rs +++ b/examples/musicbrainz_api/search_release_group.rs @@ -103,7 +103,7 @@ fn main() { }; let matches = api - .search_release_group(&arid, album) + .search_release_group(&arid, &album) .expect("failed to make API call"); println!("{matches:#?}"); diff --git a/src/core/interface/musicbrainz/mod.rs b/src/core/interface/musicbrainz/mod.rs index 3adc8c0..c0c7162 100644 --- a/src/core/interface/musicbrainz/mod.rs +++ b/src/core/interface/musicbrainz/mod.rs @@ -16,7 +16,7 @@ pub trait IMusicBrainz { fn search_release_group( &mut self, arid: &Mbid, - album: Album, + album: &Album, ) -> Result>, Error>; } @@ -44,7 +44,7 @@ impl IMusicBrainz for NullMusicBrainz { fn search_release_group( &mut self, _arid: &Mbid, - _album: Album, + _album: &Album, ) -> Result>, Error> { Ok(vec![]) } @@ -143,7 +143,7 @@ mod tests { let mbid: Mbid = "d368baa8-21ca-4759-9731-0b2753071ad8".try_into().unwrap(); let album = Album::new(AlbumId::new("an album"), AlbumDate::default(), None, vec![]); assert!(musicbrainz - .search_release_group(&mbid, album) + .search_release_group(&mbid, &album) .unwrap() .is_empty()); } diff --git a/src/external/musicbrainz/api/mod.rs b/src/external/musicbrainz/api/mod.rs index 464a726..1fbf909 100644 --- a/src/external/musicbrainz/api/mod.rs +++ b/src/external/musicbrainz/api/mod.rs @@ -70,14 +70,14 @@ impl IMusicBrainz for MusicBrainzApi { fn search_release_group( &mut self, arid: &Mbid, - album: Album, + album: &Album, ) -> Result>, Error> { let title = &album.id.title; let arid = arid.uuid().as_hyphenated().to_string(); let mut query = format!("arid:{arid}"); match album.musicbrainz { - Some(mbref) => { + Some(ref mbref) => { let rgid = mbref.mbid().uuid().as_hyphenated().to_string(); query.push_str(&format!(" AND rgid:{rgid}")); } @@ -136,13 +136,13 @@ impl TryFrom for Album { } } -#[derive(Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize)] #[serde(rename_all(deserialize = "kebab-case"))] struct ResponseSearchReleaseGroup { release_groups: Vec, } -#[derive(Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize)] #[serde(rename_all(deserialize = "kebab-case"))] struct SearchReleaseGroup { score: u8, @@ -199,7 +199,7 @@ pub enum SerdeAlbumPrimaryTypeDef { Other, } -#[derive(Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize)] pub struct SerdeAlbumPrimaryType(#[serde(with = "SerdeAlbumPrimaryTypeDef")] AlbumPrimaryType); impl From for AlbumPrimaryType { @@ -242,7 +242,7 @@ impl From for AlbumSecondaryType { #[cfg(test)] mod tests { - use mockall::predicate; + use mockall::{predicate, Sequence}; use crate::collection::album::AlbumId; @@ -298,14 +298,21 @@ mod tests { #[test] fn search_release_group() { let mut client = MockIMusicBrainzApiClient::new(); - let url = format!( + let url_title = format!( "https://musicbrainz.org/ws/2\ /release-group\ ?query=arid%3A{arid}+AND+releasegroup%3A%22{title}%22+AND+firstreleasedate%3A{year}", - title = "an+album", arid = "00000000-0000-0000-0000-000000000000", + title = "an+album", year = "1986" ); + let url_rgid = format!( + "https://musicbrainz.org/ws/2\ + /release-group\ + ?query=arid%3A{arid}+AND+rgid%3A{rgid}", + arid = "00000000-0000-0000-0000-000000000000", + rgid = "11111111-1111-1111-1111-111111111111", + ); let release_group = SearchReleaseGroup { score: 67, @@ -321,17 +328,23 @@ mod tests { // For code coverage of derive(Debug). assert!(!format!("{response:?}").is_empty()); + let mut seq = Sequence::new(); + + let title_response = response.clone(); client .expect_get() .times(1) - .with(predicate::eq(url)) - .return_once(|_| Ok(response)); + .with(predicate::eq(url_title)) + .return_once(|_| Ok(title_response)) + .in_sequence(&mut seq); - let mut api = MusicBrainzApi::new(client); - - let arid: Mbid = "00000000-0000-0000-0000-000000000000".try_into().unwrap(); - let album = Album::new(AlbumId::new("an album"), (1986, 4), None, vec![]); - let matches = api.search_release_group(&arid, album).unwrap(); + let rgid_response = response; + client + .expect_get() + .times(1) + .with(predicate::eq(url_rgid)) + .return_once(|_| Ok(rgid_response)) + .in_sequence(&mut seq); let mut album = Album::new( AlbumId::new("an album"), @@ -344,6 +357,16 @@ mod tests { ); let expected = vec![Match::new(67, album)]; + let mut api = MusicBrainzApi::new(client); + + let arid: Mbid = "00000000-0000-0000-0000-000000000000".try_into().unwrap(); + let mut album = Album::new(AlbumId::new("an album"), (1986, 4), None, vec![]); + let matches = api.search_release_group(&arid, &album).unwrap(); + assert_eq!(matches, expected); + + let rgid = MbAlbumRef::from_uuid_str("11111111-1111-1111-1111-111111111111").unwrap(); + album.set_musicbrainz_ref(rgid); + let matches = api.search_release_group(&arid, &album).unwrap(); assert_eq!(matches, expected); } -- 2.45.2