Add option for manual input during fetch #219

Merged
wojtek merged 9 commits from 188---add-option-for-manual-input-during-fetch into main 2024-09-23 22:40:25 +02:00
5 changed files with 161 additions and 173 deletions
Showing only changes of commit 0cc477fb05 - Show all commits

View File

@ -2,20 +2,15 @@ use serde::Deserialize;
use url::form_urlencoded; use url::form_urlencoded;
use crate::{ use crate::{
collection::{ collection::musicbrainz::Mbid,
album::{AlbumDate, AlbumId, AlbumPrimaryType, AlbumSecondaryType},
artist::ArtistId,
musicbrainz::Mbid,
},
external::musicbrainz::{ external::musicbrainz::{
api::{ api::{Error, MusicBrainzClient, MB_BASE_URL},
Error, MusicBrainzClient, SerdeAlbumDate, SerdeAlbumPrimaryType,
SerdeAlbumSecondaryType, SerdeMbid, MB_BASE_URL,
},
IMusicBrainzHttp, IMusicBrainzHttp,
}, },
}; };
use super::{MbArtistMeta, MbReleaseGroupMeta, SerdeMbArtistMeta, SerdeMbReleaseGroupMeta};
impl<Http: IMusicBrainzHttp> MusicBrainzClient<Http> { impl<Http: IMusicBrainzHttp> MusicBrainzClient<Http> {
pub fn lookup_artist( pub fn lookup_artist(
&mut self, &mut self,
@ -73,30 +68,22 @@ impl<'a> LookupArtistRequest<'a> {
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct LookupArtistResponse { pub struct LookupArtistResponse {
pub id: Mbid, pub meta: MbArtistMeta,
pub name: ArtistId, pub release_groups: Vec<MbReleaseGroupMeta>,
pub sort_name: ArtistId,
pub disambiguation: Option<String>,
pub release_groups: Vec<LookupArtistResponseReleaseGroup>,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all(deserialize = "kebab-case"))] #[serde(rename_all(deserialize = "kebab-case"))]
struct DeserializeLookupArtistResponse { struct DeserializeLookupArtistResponse {
id: SerdeMbid, #[serde(flatten)]
name: String, meta: SerdeMbArtistMeta,
sort_name: String, release_groups: Option<Vec<SerdeMbReleaseGroupMeta>>,
disambiguation: Option<String>,
release_groups: Option<Vec<DeserializeLookupArtistResponseReleaseGroup>>,
} }
impl From<DeserializeLookupArtistResponse> for LookupArtistResponse { impl From<DeserializeLookupArtistResponse> for LookupArtistResponse {
fn from(value: DeserializeLookupArtistResponse) -> Self { fn from(value: DeserializeLookupArtistResponse) -> Self {
LookupArtistResponse { LookupArtistResponse {
id: value.id.into(), meta: value.meta.into(),
name: value.name.into(),
sort_name: value.sort_name.into(),
disambiguation: value.disambiguation,
release_groups: value release_groups: value
.release_groups .release_groups
.map(|rgs| rgs.into_iter().map(Into::into).collect()) .map(|rgs| rgs.into_iter().map(Into::into).collect())
@ -105,37 +92,6 @@ impl From<DeserializeLookupArtistResponse> for LookupArtistResponse {
} }
} }
#[derive(Debug, PartialEq, Eq)]
pub struct LookupArtistResponseReleaseGroup {
pub id: Mbid,
pub title: AlbumId,
pub first_release_date: AlbumDate,
pub primary_type: AlbumPrimaryType,
pub secondary_types: Vec<AlbumSecondaryType>,
}
#[derive(Clone, Deserialize)]
#[serde(rename_all(deserialize = "kebab-case"))]
struct DeserializeLookupArtistResponseReleaseGroup {
id: SerdeMbid,
title: String,
first_release_date: SerdeAlbumDate,
primary_type: SerdeAlbumPrimaryType,
secondary_types: Vec<SerdeAlbumSecondaryType>,
}
impl From<DeserializeLookupArtistResponseReleaseGroup> for LookupArtistResponseReleaseGroup {
fn from(value: DeserializeLookupArtistResponseReleaseGroup) -> Self {
LookupArtistResponseReleaseGroup {
id: value.id.into(),
title: value.title.into(),
first_release_date: value.first_release_date.into(),
primary_type: value.primary_type.into(),
secondary_types: value.secondary_types.into_iter().map(Into::into).collect(),
}
}
}
pub struct LookupReleaseGroupRequest<'a> { pub struct LookupReleaseGroupRequest<'a> {
mbid: &'a Mbid, mbid: &'a Mbid,
} }
@ -148,33 +104,20 @@ impl<'a> LookupReleaseGroupRequest<'a> {
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct LookupReleaseGroupResponse { pub struct LookupReleaseGroupResponse {
pub id: Mbid, pub meta: MbReleaseGroupMeta,
pub title: AlbumId,
pub first_release_date: AlbumDate,
pub primary_type: AlbumPrimaryType,
pub secondary_types: Option<Vec<AlbumSecondaryType>>,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all(deserialize = "kebab-case"))] #[serde(rename_all(deserialize = "kebab-case"))]
struct DeserializeLookupReleaseGroupResponse { struct DeserializeLookupReleaseGroupResponse {
id: SerdeMbid, #[serde(flatten)]
title: String, meta: SerdeMbReleaseGroupMeta,
first_release_date: SerdeAlbumDate,
primary_type: SerdeAlbumPrimaryType,
secondary_types: Option<Vec<SerdeAlbumSecondaryType>>,
} }
impl From<DeserializeLookupReleaseGroupResponse> for LookupReleaseGroupResponse { impl From<DeserializeLookupReleaseGroupResponse> for LookupReleaseGroupResponse {
fn from(value: DeserializeLookupReleaseGroupResponse) -> Self { fn from(value: DeserializeLookupReleaseGroupResponse) -> Self {
LookupReleaseGroupResponse { LookupReleaseGroupResponse {
id: value.id.into(), meta: value.meta.into(),
title: value.title.into(),
first_release_date: value.first_release_date.into(),
primary_type: value.primary_type.into(),
secondary_types: value
.secondary_types
.map(|v| v.into_iter().map(Into::into).collect()),
} }
} }
} }
@ -183,7 +126,13 @@ impl From<DeserializeLookupReleaseGroupResponse> for LookupReleaseGroupResponse
mod tests { mod tests {
use mockall::predicate; use mockall::predicate;
use crate::external::musicbrainz::MockIMusicBrainzHttp; use crate::{
collection::album::{AlbumPrimaryType, AlbumSecondaryType},
external::musicbrainz::{
api::{SerdeAlbumDate, SerdeAlbumPrimaryType, SerdeAlbumSecondaryType, SerdeMbid},
MockIMusicBrainzHttp,
},
};
use super::*; use super::*;
@ -193,41 +142,38 @@ mod tests {
let mut http = MockIMusicBrainzHttp::new(); let mut http = MockIMusicBrainzHttp::new();
let url = format!("https://musicbrainz.org/ws/2/artist/{mbid}?inc=release-groups",); let url = format!("https://musicbrainz.org/ws/2/artist/{mbid}?inc=release-groups",);
let de_id = SerdeMbid(mbid.try_into().unwrap()); let de_meta = SerdeMbArtistMeta {
let de_name = String::from("the artist"); id: SerdeMbid(mbid.try_into().unwrap()),
let de_sort_name = String::from("artist, the"); name: String::from("the artist"),
let de_disambiguation = Some(String::from("disambig")); sort_name: String::from("artist, the"),
let de_release_group = DeserializeLookupArtistResponseReleaseGroup { disambiguation: Some(String::from("disambig")),
};
let de_release_group = SerdeMbReleaseGroupMeta {
id: SerdeMbid("11111111-1111-1111-1111-111111111111".try_into().unwrap()), id: SerdeMbid("11111111-1111-1111-1111-111111111111".try_into().unwrap()),
title: String::from("an album"), title: String::from("an album"),
first_release_date: SerdeAlbumDate((1986, 4).into()), first_release_date: SerdeAlbumDate((1986, 4).into()),
primary_type: SerdeAlbumPrimaryType(AlbumPrimaryType::Album), primary_type: SerdeAlbumPrimaryType(AlbumPrimaryType::Album),
secondary_types: vec![SerdeAlbumSecondaryType(AlbumSecondaryType::Compilation)], secondary_types: Some(vec![SerdeAlbumSecondaryType(
AlbumSecondaryType::Compilation,
)]),
}; };
let de_response = DeserializeLookupArtistResponse { let de_response = DeserializeLookupArtistResponse {
id: de_id.clone(), meta: de_meta.clone(),
name: de_name.clone(),
sort_name: de_sort_name.clone(),
disambiguation: de_disambiguation.clone(),
release_groups: Some(vec![de_release_group.clone()]), release_groups: Some(vec![de_release_group.clone()]),
}; };
let release_group = LookupArtistResponseReleaseGroup { let release_group = MbReleaseGroupMeta {
id: de_release_group.id.0, id: de_release_group.id.0,
title: de_release_group.title.into(), title: de_release_group.title.into(),
first_release_date: de_release_group.first_release_date.0, first_release_date: de_release_group.first_release_date.0,
primary_type: de_release_group.primary_type.0, primary_type: de_release_group.primary_type.0,
secondary_types: de_release_group secondary_types: de_release_group
.secondary_types .secondary_types
.into_iter() .as_ref()
.map(|st| st.0) .map(|v| v.into_iter().map(|st| st.0).collect()),
.collect(),
}; };
let response = LookupArtistResponse { let response = LookupArtistResponse {
id: de_id.0, meta: de_meta.into(),
name: de_name.into(),
sort_name: de_sort_name.into(),
disambiguation: de_disambiguation,
release_groups: vec![release_group], release_groups: vec![release_group],
}; };

View File

@ -4,7 +4,8 @@ use serde::{de::Visitor, Deserialize, Deserializer};
use crate::{ use crate::{
collection::{ collection::{
album::{AlbumDate, AlbumPrimaryType, AlbumSecondaryType}, album::{AlbumDate, AlbumId, AlbumPrimaryType, AlbumSecondaryType},
artist::ArtistId,
musicbrainz::Mbid, musicbrainz::Mbid,
Error as CollectionError, Error as CollectionError,
}, },
@ -58,6 +59,67 @@ impl<Http> MusicBrainzClient<Http> {
} }
} }
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MbArtistMeta {
pub id: Mbid,
pub name: ArtistId,
pub sort_name: ArtistId,
pub disambiguation: Option<String>,
}
#[derive(Clone, Deserialize)]
#[serde(rename_all(deserialize = "kebab-case"))]
struct SerdeMbArtistMeta {
id: SerdeMbid,
name: String,
sort_name: String,
disambiguation: Option<String>,
}
impl From<SerdeMbArtistMeta> for MbArtistMeta {
fn from(value: SerdeMbArtistMeta) -> Self {
MbArtistMeta {
id: value.id.into(),
name: value.name.into(),
sort_name: value.sort_name.into(),
disambiguation: value.disambiguation,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MbReleaseGroupMeta {
pub id: Mbid,
pub title: AlbumId,
pub first_release_date: AlbumDate,
pub primary_type: AlbumPrimaryType,
pub secondary_types: Option<Vec<AlbumSecondaryType>>,
}
#[derive(Clone, Deserialize)]
#[serde(rename_all(deserialize = "kebab-case"))]
pub struct SerdeMbReleaseGroupMeta {
id: SerdeMbid,
title: String,
first_release_date: SerdeAlbumDate,
primary_type: SerdeAlbumPrimaryType,
secondary_types: Option<Vec<SerdeAlbumSecondaryType>>,
}
impl From<SerdeMbReleaseGroupMeta> for MbReleaseGroupMeta {
fn from(value: SerdeMbReleaseGroupMeta) -> Self {
MbReleaseGroupMeta {
id: value.id.into(),
title: value.title.into(),
first_release_date: value.first_release_date.into(),
primary_type: value.primary_type.into(),
secondary_types: value
.secondary_types
.map(|v| v.into_iter().map(Into::into).collect()),
}
}
}
pub struct ApiDisplay; pub struct ApiDisplay;
impl ApiDisplay { impl ApiDisplay {

View File

@ -2,12 +2,9 @@ use std::fmt;
use serde::Deserialize; use serde::Deserialize;
use crate::{ use crate::external::musicbrainz::api::{
collection::{artist::ArtistId, musicbrainz::Mbid},
external::musicbrainz::api::{
search::query::{impl_term, EmptyQuery, EmptyQueryJoin, Query, QueryJoin}, search::query::{impl_term, EmptyQuery, EmptyQueryJoin, Query, QueryJoin},
SerdeMbid, MbArtistMeta, SerdeMbArtistMeta,
},
}; };
pub enum SearchArtist<'a> { pub enum SearchArtist<'a> {
@ -48,30 +45,22 @@ impl From<DeserializeSearchArtistResponse> for SearchArtistResponse {
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct SearchArtistResponseArtist { pub struct SearchArtistResponseArtist {
pub score: u8, pub score: u8,
pub id: Mbid, pub meta: MbArtistMeta,
pub name: ArtistId,
pub sort_name: ArtistId,
pub disambiguation: Option<String>,
} }
#[derive(Clone, Deserialize)] #[derive(Clone, Deserialize)]
#[serde(rename_all(deserialize = "kebab-case"))] #[serde(rename_all(deserialize = "kebab-case"))]
struct DeserializeSearchArtistResponseArtist { struct DeserializeSearchArtistResponseArtist {
score: u8, score: u8,
id: SerdeMbid, #[serde(flatten)]
name: String, meta: SerdeMbArtistMeta,
sort_name: String,
disambiguation: Option<String>,
} }
impl From<DeserializeSearchArtistResponseArtist> for SearchArtistResponseArtist { impl From<DeserializeSearchArtistResponseArtist> for SearchArtistResponseArtist {
fn from(value: DeserializeSearchArtistResponseArtist) -> Self { fn from(value: DeserializeSearchArtistResponseArtist) -> Self {
SearchArtistResponseArtist { SearchArtistResponseArtist {
score: value.score, score: value.score,
id: value.id.into(), meta: value.meta.into(),
name: value.name.into(),
sort_name: value.sort_name.into(),
disambiguation: value.disambiguation,
} }
} }
} }
@ -80,17 +69,22 @@ impl From<DeserializeSearchArtistResponseArtist> for SearchArtistResponseArtist
mod tests { mod tests {
use mockall::predicate; use mockall::predicate;
use crate::external::musicbrainz::{api::MusicBrainzClient, MockIMusicBrainzHttp}; use crate::external::musicbrainz::{
api::{MusicBrainzClient, SerdeMbid},
MockIMusicBrainzHttp,
};
use super::*; use super::*;
fn de_response() -> DeserializeSearchArtistResponse { fn de_response() -> DeserializeSearchArtistResponse {
let de_artist = DeserializeSearchArtistResponseArtist { let de_artist = DeserializeSearchArtistResponseArtist {
score: 67, score: 67,
meta: SerdeMbArtistMeta {
id: SerdeMbid("11111111-1111-1111-1111-111111111111".try_into().unwrap()), id: SerdeMbid("11111111-1111-1111-1111-111111111111".try_into().unwrap()),
name: String::from("an artist"), name: String::from("an artist"),
sort_name: String::from("artist, an"), sort_name: String::from("artist, an"),
disambiguation: None, disambiguation: None,
},
}; };
DeserializeSearchArtistResponse { DeserializeSearchArtistResponse {
artists: vec![de_artist.clone()], artists: vec![de_artist.clone()],
@ -104,10 +98,7 @@ mod tests {
.into_iter() .into_iter()
.map(|a| SearchArtistResponseArtist { .map(|a| SearchArtistResponseArtist {
score: 67, score: 67,
id: a.id.0, meta: a.meta.into(),
name: a.name.clone().into(),
sort_name: a.sort_name.clone().into(),
disambiguation: a.disambiguation,
}) })
.collect(), .collect(),
} }

View File

@ -3,13 +3,10 @@ use std::fmt;
use serde::Deserialize; use serde::Deserialize;
use crate::{ use crate::{
collection::{ collection::{album::AlbumDate, musicbrainz::Mbid},
album::{AlbumDate, AlbumId, AlbumPrimaryType, AlbumSecondaryType},
musicbrainz::Mbid,
},
external::musicbrainz::api::{ external::musicbrainz::api::{
search::query::{impl_term, EmptyQuery, EmptyQueryJoin, Query, QueryJoin}, search::query::{impl_term, EmptyQuery, EmptyQueryJoin, Query, QueryJoin},
ApiDisplay, SerdeAlbumDate, SerdeAlbumPrimaryType, SerdeAlbumSecondaryType, SerdeMbid, ApiDisplay, MbReleaseGroupMeta, SerdeMbReleaseGroupMeta,
}, },
}; };
@ -72,22 +69,15 @@ impl From<DeserializeSearchReleaseGroupResponse> for SearchReleaseGroupResponse
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct SearchReleaseGroupResponseReleaseGroup { pub struct SearchReleaseGroupResponseReleaseGroup {
pub score: u8, pub score: u8,
pub id: Mbid, pub meta: MbReleaseGroupMeta,
pub title: AlbumId,
pub first_release_date: AlbumDate,
pub primary_type: AlbumPrimaryType,
pub secondary_types: Option<Vec<AlbumSecondaryType>>,
} }
#[derive(Clone, Deserialize)] #[derive(Clone, Deserialize)]
#[serde(rename_all(deserialize = "kebab-case"))] #[serde(rename_all(deserialize = "kebab-case"))]
pub struct DeserializeSearchReleaseGroupResponseReleaseGroup { pub struct DeserializeSearchReleaseGroupResponseReleaseGroup {
score: u8, score: u8,
id: SerdeMbid, #[serde(flatten)]
title: String, meta: SerdeMbReleaseGroupMeta,
first_release_date: SerdeAlbumDate,
primary_type: SerdeAlbumPrimaryType,
secondary_types: Option<Vec<SerdeAlbumSecondaryType>>,
} }
impl From<DeserializeSearchReleaseGroupResponseReleaseGroup> impl From<DeserializeSearchReleaseGroupResponseReleaseGroup>
@ -96,13 +86,7 @@ impl From<DeserializeSearchReleaseGroupResponseReleaseGroup>
fn from(value: DeserializeSearchReleaseGroupResponseReleaseGroup) -> Self { fn from(value: DeserializeSearchReleaseGroupResponseReleaseGroup) -> Self {
SearchReleaseGroupResponseReleaseGroup { SearchReleaseGroupResponseReleaseGroup {
score: value.score, score: value.score,
id: value.id.into(), meta: value.meta.into(),
title: value.title.into(),
first_release_date: value.first_release_date.into(),
primary_type: value.primary_type.into(),
secondary_types: value
.secondary_types
.map(|v| v.into_iter().map(Into::into).collect()),
} }
} }
} }
@ -111,18 +95,29 @@ impl From<DeserializeSearchReleaseGroupResponseReleaseGroup>
mod tests { mod tests {
use mockall::predicate; use mockall::predicate;
use crate::external::musicbrainz::{api::MusicBrainzClient, MockIMusicBrainzHttp}; use crate::{
collection::album::{AlbumPrimaryType, AlbumSecondaryType},
external::musicbrainz::{
api::{
MusicBrainzClient, SerdeAlbumDate, SerdeAlbumPrimaryType, SerdeAlbumSecondaryType,
SerdeMbid,
},
MockIMusicBrainzHttp,
},
};
use super::*; use super::*;
fn de_response() -> DeserializeSearchReleaseGroupResponse { fn de_response() -> DeserializeSearchReleaseGroupResponse {
let de_release_group = DeserializeSearchReleaseGroupResponseReleaseGroup { let de_release_group = DeserializeSearchReleaseGroupResponseReleaseGroup {
score: 67, score: 67,
meta: SerdeMbReleaseGroupMeta {
id: SerdeMbid("11111111-1111-1111-1111-111111111111".try_into().unwrap()), id: SerdeMbid("11111111-1111-1111-1111-111111111111".try_into().unwrap()),
title: String::from("an album"), title: String::from("an album"),
first_release_date: SerdeAlbumDate((1986, 4).into()), first_release_date: SerdeAlbumDate((1986, 4).into()),
primary_type: SerdeAlbumPrimaryType(AlbumPrimaryType::Album), primary_type: SerdeAlbumPrimaryType(AlbumPrimaryType::Album),
secondary_types: Some(vec![SerdeAlbumSecondaryType(AlbumSecondaryType::Live)]), secondary_types: Some(vec![SerdeAlbumSecondaryType(AlbumSecondaryType::Live)]),
},
}; };
DeserializeSearchReleaseGroupResponse { DeserializeSearchReleaseGroupResponse {
release_groups: vec![de_release_group.clone()], release_groups: vec![de_release_group.clone()],
@ -136,13 +131,7 @@ mod tests {
.into_iter() .into_iter()
.map(|rg| SearchReleaseGroupResponseReleaseGroup { .map(|rg| SearchReleaseGroupResponseReleaseGroup {
score: 67, score: 67,
id: rg.id.0, meta: rg.meta.into(),
title: rg.title.into(),
first_release_date: rg.first_release_date.0,
primary_type: rg.primary_type.0,
secondary_types: rg
.secondary_types
.map(|v| v.into_iter().map(|st| st.0).collect()),
}) })
.collect(), .collect(),
} }

View File

@ -93,47 +93,47 @@ impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
} }
fn from_lookup_artist_response(entity: LookupArtistResponse) -> Lookup<ArtistMeta> { fn from_lookup_artist_response(entity: LookupArtistResponse) -> Lookup<ArtistMeta> {
let sort: Option<ArtistId> = Some(entity.sort_name) let sort: Option<ArtistId> = Some(entity.meta.sort_name)
.filter(|s| s != &entity.name) .filter(|s| s != &entity.meta.name)
.map(Into::into); .map(Into::into);
Lookup { Lookup {
item: ArtistMeta { item: ArtistMeta {
id: entity.name, id: entity.meta.name,
sort, sort,
musicbrainz: Some(entity.id.into()), musicbrainz: Some(entity.meta.id.into()),
properties: HashMap::new(), properties: HashMap::new(),
}, },
disambiguation: entity.disambiguation, disambiguation: entity.meta.disambiguation,
} }
} }
fn from_lookup_release_group_response(entity: LookupReleaseGroupResponse) -> Lookup<AlbumMeta> { fn from_lookup_release_group_response(entity: LookupReleaseGroupResponse) -> Lookup<AlbumMeta> {
Lookup { Lookup {
item: AlbumMeta { item: AlbumMeta {
id: entity.title, id: entity.meta.title,
date: entity.first_release_date, date: entity.meta.first_release_date,
seq: AlbumSeq::default(), seq: AlbumSeq::default(),
musicbrainz: Some(entity.id.into()), musicbrainz: Some(entity.meta.id.into()),
primary_type: Some(entity.primary_type), primary_type: Some(entity.meta.primary_type),
secondary_types: entity.secondary_types.unwrap_or_default(), secondary_types: entity.meta.secondary_types.unwrap_or_default(),
}, },
disambiguation: None, disambiguation: None,
} }
} }
fn from_search_artist_response_artist(entity: SearchArtistResponseArtist) -> Match<ArtistMeta> { fn from_search_artist_response_artist(entity: SearchArtistResponseArtist) -> Match<ArtistMeta> {
let sort: Option<ArtistId> = Some(entity.sort_name) let sort: Option<ArtistId> = Some(entity.meta.sort_name)
.filter(|s| s != &entity.name) .filter(|s| s != &entity.meta.name)
.map(Into::into); .map(Into::into);
Match { Match {
score: entity.score, score: entity.score,
item: ArtistMeta { item: ArtistMeta {
id: entity.name, id: entity.meta.name,
sort, sort,
musicbrainz: Some(entity.id.into()), musicbrainz: Some(entity.meta.id.into()),
properties: HashMap::new(), properties: HashMap::new(),
}, },
disambiguation: entity.disambiguation, disambiguation: entity.meta.disambiguation,
} }
} }
@ -143,12 +143,12 @@ fn from_search_release_group_response_release_group(
Match { Match {
score: entity.score, score: entity.score,
item: AlbumMeta { item: AlbumMeta {
id: entity.title, id: entity.meta.title,
date: entity.first_release_date, date: entity.meta.first_release_date,
seq: AlbumSeq::default(), seq: AlbumSeq::default(),
musicbrainz: Some(entity.id.into()), musicbrainz: Some(entity.meta.id.into()),
primary_type: Some(entity.primary_type), primary_type: Some(entity.meta.primary_type),
secondary_types: entity.secondary_types.unwrap_or_default(), secondary_types: entity.meta.secondary_types.unwrap_or_default(),
}, },
disambiguation: None, disambiguation: None,
} }