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
Showing only changes of commit 0e97f26b6e - Show all commits

View File

@ -119,7 +119,8 @@ impl<Http: IMusicBrainzHttp> MusicBrainzClient<Http> {
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<SearchReleaseGroupResponseUnit>,
}
#[derive(Deserialize)]
#[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 score: u8,
pub id: Mbid,
pub title: String,
pub first_release_date: AlbumDate,
#[serde(with = "AlbumPrimaryTypeDef")]
pub primary_type: AlbumPrimaryType,
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 {
type Value = Mbid;
impl From<DeserializeSearchReleaseGroupResponseUnit> 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<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 {
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<D>(deserializer: D) -> Result<Self, D::Error>
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<SerdeAlbumDate> 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<D>(deserializer: D) -> Result<Self, D::Error>
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<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 From<SerdeAlbumPrimaryType> for AlbumPrimaryType {
fn from(value: SerdeAlbumPrimaryType) -> Self {
value.0
}
}
impl<'de> Deserialize<'de> for AlbumSecondaryType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(AlbumSecondaryTypeVisitor)
impl From<AlbumPrimaryType> 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<SerdeAlbumSecondaryType> for AlbumSecondaryType {
fn from(value: SerdeAlbumSecondaryType) -> Self {
value.0
}
}
impl From<AlbumSecondaryType> for SerdeAlbumSecondaryType {
fn from(value: AlbumSecondaryType) -> Self {
SerdeAlbumSecondaryType(value)
}
}