From 0e97f26b6e292754faafd8db8243aa0f0fd84dd1 Mon Sep 17 00:00:00 2001 From: Wojciech Kozlowski Date: Wed, 28 Aug 2024 14:58:48 +0200 Subject: [PATCH] Do not implement deserialize for core types --- src/external/musicbrainz/mod.rs | 170 +++++++++++++++++++++----------- 1 file changed, 115 insertions(+), 55 deletions(-) diff --git a/src/external/musicbrainz/mod.rs b/src/external/musicbrainz/mod.rs index 11a16d4..0a4de11 100644 --- a/src/external/musicbrainz/mod.rs +++ b/src/external/musicbrainz/mod.rs @@ -119,7 +119,8 @@ impl MusicBrainzClient { form_urlencoded::byte_serialize(query.join(" AND ").as_bytes()).collect(); let url = format!("{MB_BASE_URL}/release-group?query={query}"); - Ok(self.http.get(&url)?) + let response: DeserializeSearchReleaseGroupResponse = self.http.get(&url)?; + Ok(response.into()) } } @@ -153,29 +154,71 @@ impl<'a> SearchReleaseGroupRequest<'a> { } } -// TODO: Separate deserialize types from internal types like in JSON code. -#[derive(Deserialize)] -#[serde(rename_all(deserialize = "kebab-case"))] pub struct SearchReleaseGroupResponse { pub release_groups: Vec, } #[derive(Deserialize)] #[serde(rename_all(deserialize = "kebab-case"))] +struct DeserializeSearchReleaseGroupResponse { + release_groups: Vec, +} + +impl From for SearchReleaseGroupResponse { + fn from(value: DeserializeSearchReleaseGroupResponse) -> Self { + SearchReleaseGroupResponse { + release_groups: value.release_groups.into_iter().map(Into::into).collect(), + } + } +} + pub struct SearchReleaseGroupResponseUnit { pub score: u8, pub id: Mbid, pub title: String, pub first_release_date: AlbumDate, - #[serde(with = "AlbumPrimaryTypeDef")] pub primary_type: AlbumPrimaryType, pub secondary_types: Option>, } -struct MbidVisitor; +#[derive(Deserialize)] +#[serde(rename_all(deserialize = "kebab-case"))] +struct DeserializeSearchReleaseGroupResponseUnit { + score: u8, + id: SerdeMbid, + title: String, + first_release_date: SerdeAlbumDate, + primary_type: SerdeAlbumPrimaryType, + secondary_types: Option>, +} -impl<'de> Visitor<'de> for MbidVisitor { - type Value = Mbid; +impl From for SearchReleaseGroupResponseUnit { + fn from(value: DeserializeSearchReleaseGroupResponseUnit) -> Self { + SearchReleaseGroupResponseUnit { + score: value.score, + id: value.id.into(), + title: value.title, + 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 SerdeMbid(Mbid); + +impl From for Mbid { + fn from(value: SerdeMbid) -> Self { + value.0 + } +} + +struct SerdeMbidVisitor; + +impl<'de> Visitor<'de> for SerdeMbidVisitor { + type Value = SerdeMbid; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid MusicBrainz identifier") @@ -185,24 +228,34 @@ impl<'de> Visitor<'de> for MbidVisitor { where E: serde::de::Error, { - Ok(v.try_into() - .map_err(|e: MbidError| E::custom(e.to_string()))?) + Ok(SerdeMbid( + v.try_into() + .map_err(|e: MbidError| E::custom(e.to_string()))?, + )) } } -impl<'de> Deserialize<'de> for Mbid { +impl<'de> Deserialize<'de> for SerdeMbid { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - deserializer.deserialize_str(MbidVisitor) + deserializer.deserialize_str(SerdeMbidVisitor) } } -struct AlbumDateVisitor; +pub struct SerdeAlbumDate(AlbumDate); -impl<'de> Visitor<'de> for AlbumDateVisitor { - type Value = AlbumDate; +impl From for AlbumDate { + fn from(value: SerdeAlbumDate) -> Self { + value.0 + } +} + +struct SerdeAlbumDateVisitor; + +impl<'de> Visitor<'de> for SerdeAlbumDateVisitor { + type Value = SerdeAlbumDate; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid YYYY(-MM-(-DD)) date") @@ -232,16 +285,16 @@ impl<'de> Visitor<'de> for AlbumDateVisitor { .transpose() .map_err(|e: ParseIntError| E::custom(e.to_string()))?; - Ok(AlbumDate::new(year, month, day)) + Ok(SerdeAlbumDate(AlbumDate::new(year, month, day))) } } -impl<'de> Deserialize<'de> for AlbumDate { +impl<'de> Deserialize<'de> for SerdeAlbumDate { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - deserializer.deserialize_str(AlbumDateVisitor) + deserializer.deserialize_str(SerdeAlbumDateVisitor) } } @@ -256,47 +309,54 @@ pub enum AlbumPrimaryTypeDef { Other, } -// AlbumSecondaryType is implemented manually because deserializing to a remote type is not (yet) -// supported for Option/Vec/Map by serde: https://github.com/serde-rs/serde/issues/723. -struct AlbumSecondaryTypeVisitor; +#[derive(Debug, Deserialize)] +pub struct SerdeAlbumPrimaryType(#[serde(with = "AlbumPrimaryTypeDef")] AlbumPrimaryType); -impl<'de> Visitor<'de> for AlbumSecondaryTypeVisitor { - type Value = AlbumSecondaryType; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a valid MusicBrainz album secondary type") - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - let variant = match v { - "Compilation" => AlbumSecondaryType::Compilation, - "Soundtrack" => AlbumSecondaryType::Soundtrack, - "Spokenword" => AlbumSecondaryType::Spokenword, - "Interview" => AlbumSecondaryType::Interview, - "Audiobook" => AlbumSecondaryType::Audiobook, - "Audio drama" => AlbumSecondaryType::AudioDrama, - "Live" => AlbumSecondaryType::Live, - "Remix" => AlbumSecondaryType::Remix, - "DJ-mix" => AlbumSecondaryType::DjMix, - "Mixtape/Street" => AlbumSecondaryType::MixtapeStreet, - "Demo" => AlbumSecondaryType::Demo, - "Field recording" => AlbumSecondaryType::FieldRecording, - _ => return Err(E::custom(format!("unknown album secondary type: {v}"))), - }; - - Ok(variant) +impl From for AlbumPrimaryType { + fn from(value: SerdeAlbumPrimaryType) -> Self { + value.0 } } -impl<'de> Deserialize<'de> for AlbumSecondaryType { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_str(AlbumSecondaryTypeVisitor) +impl From for SerdeAlbumPrimaryType { + fn from(value: AlbumPrimaryType) -> Self { + SerdeAlbumPrimaryType(value) + } +} + +#[derive(Debug, Deserialize)] +#[serde(remote = "AlbumSecondaryType")] +pub enum AlbumSecondaryTypeDef { + Compilation, + Soundtrack, + Spokenword, + Interview, + Audiobook, + #[serde(rename = "Audio drama")] + AudioDrama, + Live, + Remix, + #[serde(rename = "DJ-mix")] + DjMix, + #[serde(rename = "Mixtape/Street")] + MixtapeStreet, + Demo, + #[serde(rename = "Field recording")] + FieldRecording, +} + +#[derive(Debug, Deserialize)] +pub struct SerdeAlbumSecondaryType(#[serde(with = "AlbumSecondaryTypeDef")] AlbumSecondaryType); + +impl From for AlbumSecondaryType { + fn from(value: SerdeAlbumSecondaryType) -> Self { + value.0 + } +} + +impl From for SerdeAlbumSecondaryType { + fn from(value: AlbumSecondaryType) -> Self { + SerdeAlbumSecondaryType(value) } }