Decide carefully where external::musicbrainz
belongs
#196
170
src/external/musicbrainz/mod.rs
vendored
170
src/external/musicbrainz/mod.rs
vendored
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user