Do not implement deserialize for core types

This commit is contained in:
Wojciech Kozlowski 2024-08-28 14:58:48 +02:00
parent 76173e0468
commit 0e97f26b6e

View File

@ -119,7 +119,8 @@ impl<Http: IMusicBrainzHttp> MusicBrainzClient<Http> {
form_urlencoded::byte_serialize(query.join(" AND ").as_bytes()).collect(); 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)?) 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 struct SearchReleaseGroupResponse {
pub release_groups: Vec<SearchReleaseGroupResponseUnit>, pub release_groups: Vec<SearchReleaseGroupResponseUnit>,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all(deserialize = "kebab-case"))] #[serde(rename_all(deserialize = "kebab-case"))]
struct DeserializeSearchReleaseGroupResponse {
release_groups: Vec<DeserializeSearchReleaseGroupResponseUnit>,
}
impl From<DeserializeSearchReleaseGroupResponse> for SearchReleaseGroupResponse {
fn from(value: DeserializeSearchReleaseGroupResponse) -> Self {
SearchReleaseGroupResponse {
release_groups: value.release_groups.into_iter().map(Into::into).collect(),
}
}
}
pub struct SearchReleaseGroupResponseUnit { pub struct SearchReleaseGroupResponseUnit {
pub score: u8, pub score: u8,
pub id: Mbid, pub id: Mbid,
pub title: String, pub title: String,
pub first_release_date: AlbumDate, pub first_release_date: AlbumDate,
#[serde(with = "AlbumPrimaryTypeDef")]
pub primary_type: AlbumPrimaryType, pub primary_type: AlbumPrimaryType,
pub secondary_types: Option<Vec<AlbumSecondaryType>>, pub secondary_types: Option<Vec<AlbumSecondaryType>>,
} }
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<Vec<SerdeAlbumSecondaryType>>,
}
impl<'de> Visitor<'de> for MbidVisitor { impl From<DeserializeSearchReleaseGroupResponseUnit> for SearchReleaseGroupResponseUnit {
type Value = Mbid; 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<SerdeMbid> 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 { fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid MusicBrainz identifier") formatter.write_str("a valid MusicBrainz identifier")
@ -185,24 +228,34 @@ impl<'de> Visitor<'de> for MbidVisitor {
where where
E: serde::de::Error, E: serde::de::Error,
{ {
Ok(v.try_into() Ok(SerdeMbid(
.map_err(|e: MbidError| E::custom(e.to_string()))?) 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<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
deserializer.deserialize_str(MbidVisitor) deserializer.deserialize_str(SerdeMbidVisitor)
} }
} }
struct AlbumDateVisitor; pub struct SerdeAlbumDate(AlbumDate);
impl<'de> Visitor<'de> for AlbumDateVisitor { impl From<SerdeAlbumDate> for AlbumDate {
type Value = 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 { fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid YYYY(-MM-(-DD)) date") formatter.write_str("a valid YYYY(-MM-(-DD)) date")
@ -232,16 +285,16 @@ impl<'de> Visitor<'de> for AlbumDateVisitor {
.transpose() .transpose()
.map_err(|e: ParseIntError| E::custom(e.to_string()))?; .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<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
deserializer.deserialize_str(AlbumDateVisitor) deserializer.deserialize_str(SerdeAlbumDateVisitor)
} }
} }
@ -256,47 +309,54 @@ pub enum AlbumPrimaryTypeDef {
Other, Other,
} }
// AlbumSecondaryType is implemented manually because deserializing to a remote type is not (yet) #[derive(Debug, Deserialize)]
// supported for Option/Vec/Map by serde: https://github.com/serde-rs/serde/issues/723. pub struct SerdeAlbumPrimaryType(#[serde(with = "AlbumPrimaryTypeDef")] AlbumPrimaryType);
struct AlbumSecondaryTypeVisitor;
impl<'de> Visitor<'de> for AlbumSecondaryTypeVisitor { impl From<SerdeAlbumPrimaryType> for AlbumPrimaryType {
type Value = AlbumSecondaryType; fn from(value: SerdeAlbumPrimaryType) -> Self {
value.0
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid MusicBrainz album secondary type")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
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<'de> Deserialize<'de> for AlbumSecondaryType { impl From<AlbumPrimaryType> for SerdeAlbumPrimaryType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn from(value: AlbumPrimaryType) -> Self {
where SerdeAlbumPrimaryType(value)
D: Deserializer<'de>, }
{ }
deserializer.deserialize_str(AlbumSecondaryTypeVisitor)
#[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<SerdeAlbumSecondaryType> for AlbumSecondaryType {
fn from(value: SerdeAlbumSecondaryType) -> Self {
value.0
}
}
impl From<AlbumSecondaryType> for SerdeAlbumSecondaryType {
fn from(value: AlbumSecondaryType) -> Self {
SerdeAlbumSecondaryType(value)
} }
} }