diff --git a/examples/musicbrainz_api/lookup_artist.rs b/examples/musicbrainz_api/lookup_artist.rs index feb9821..f072d72 100644 --- a/examples/musicbrainz_api/lookup_artist.rs +++ b/examples/musicbrainz_api/lookup_artist.rs @@ -1,11 +1,11 @@ #![allow(non_snake_case)] use musichoard::{ + collection::musicbrainz::Mbid, external::musicbrainz::{ api::{lookup::LookupArtistRequest, MusicBrainzClient}, http::MusicBrainzHttp, }, - interface::musicbrainz::Mbid, }; use structopt::StructOpt; use uuid::Uuid; diff --git a/examples/musicbrainz_api/search_release_group.rs b/examples/musicbrainz_api/search_release_group.rs index bd7aa66..93e2a0a 100644 --- a/examples/musicbrainz_api/search_release_group.rs +++ b/examples/musicbrainz_api/search_release_group.rs @@ -3,11 +3,11 @@ use std::{num::ParseIntError, str::FromStr}; use musichoard::{ - collection::album::AlbumDate, + collection::{album::AlbumDate, musicbrainz::Mbid}, external::musicbrainz::{ - api::search::SearchReleaseGroupRequest, api::MusicBrainzClient, http::MusicBrainzHttp, + api::{search::SearchReleaseGroupRequest, MusicBrainzClient}, + http::MusicBrainzHttp, }, - interface::musicbrainz::Mbid, }; use structopt::StructOpt; use uuid::Uuid; diff --git a/src/core/collection/mod.rs b/src/core/collection/mod.rs index f54f5b4..bd964f9 100644 --- a/src/core/collection/mod.rs +++ b/src/core/collection/mod.rs @@ -16,6 +16,8 @@ pub type Collection = Vec; /// Error type for the [`collection`] module. #[derive(Debug, PartialEq, Eq)] pub enum Error { + /// An error occurred when processing an MBID. + MbidError(String), /// An error occurred when processing a URL. UrlError(String), } @@ -23,6 +25,7 @@ pub enum Error { impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { + Self::MbidError(ref s) => write!(f, "an error occurred when processing an MBID: {s}"), Self::UrlError(ref s) => write!(f, "an error occurred when processing a URL: {s}"), } } @@ -36,6 +39,6 @@ impl From for Error { impl From for Error { fn from(err: uuid::Error) -> Error { - Error::UrlError(err.to_string()) + Error::MbidError(err.to_string()) } } diff --git a/src/core/collection/musicbrainz.rs b/src/core/collection/musicbrainz.rs index ae73f74..6f6f836 100644 --- a/src/core/collection/musicbrainz.rs +++ b/src/core/collection/musicbrainz.rs @@ -3,10 +3,41 @@ use std::fmt::{Debug, Display}; use url::Url; use uuid::Uuid; -use crate::{core::collection::Error, interface::musicbrainz::Mbid}; +use crate::core::collection::Error; const MB_DOMAIN: &str = "musicbrainz.org"; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Mbid(Uuid); + +impl Mbid { + pub fn uuid(&self) -> &Uuid { + &self.0 + } +} + +impl From for Mbid { + fn from(value: Uuid) -> Self { + Mbid(value) + } +} + +macro_rules! try_from_impl_for_mbid { + ($from:ty) => { + impl TryFrom<$from> for Mbid { + type Error = Error; + + fn try_from(value: $from) -> Result { + Ok(Uuid::parse_str(value.as_ref())?.into()) + } + } + }; +} + +try_from_impl_for_mbid!(&str); +try_from_impl_for_mbid!(&String); +try_from_impl_for_mbid!(String); + #[derive(Clone, Debug, PartialEq, Eq)] struct MusicBrainzRef { mbid: Mbid, diff --git a/src/core/interface/database/mod.rs b/src/core/interface/database/mod.rs index 5e05561..b60575c 100644 --- a/src/core/interface/database/mod.rs +++ b/src/core/interface/database/mod.rs @@ -59,7 +59,9 @@ impl From for LoadError { impl From for LoadError { fn from(err: collection::Error) -> Self { match err { - collection::Error::UrlError(e) => LoadError::SerDeError(e), + collection::Error::UrlError(e) | collection::Error::MbidError(e) => { + LoadError::SerDeError(e) + } } } } diff --git a/src/core/interface/mod.rs b/src/core/interface/mod.rs index 6becfd7..2b9c4cf 100644 --- a/src/core/interface/mod.rs +++ b/src/core/interface/mod.rs @@ -1,3 +1,2 @@ pub mod database; pub mod library; -pub mod musicbrainz; diff --git a/src/core/interface/musicbrainz/mod.rs b/src/core/interface/musicbrainz/mod.rs deleted file mode 100644 index a59a348..0000000 --- a/src/core/interface/musicbrainz/mod.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::fmt; - -use uuid::{self, Uuid}; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Mbid(Uuid); - -impl Mbid { - pub fn uuid(&self) -> &Uuid { - &self.0 - } -} - -impl From for Mbid { - fn from(value: Uuid) -> Self { - Mbid(value) - } -} - -#[derive(Debug)] -pub struct MbidError(String); - -impl fmt::Display for MbidError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "failed to parse a MBID: {}", self.0) - } -} - -impl From for MbidError { - fn from(value: uuid::Error) -> Self { - MbidError(value.to_string()) - } -} - -macro_rules! try_from_impl_for_mbid { - ($from:ty) => { - impl TryFrom<$from> for Mbid { - type Error = MbidError; - - fn try_from(value: $from) -> Result { - Ok(Uuid::parse_str(value.as_ref())?.into()) - } - } - }; -} - -try_from_impl_for_mbid!(&str); -try_from_impl_for_mbid!(&String); -try_from_impl_for_mbid!(String); - -#[test] -fn errors() { - let mbid_err: MbidError = TryInto::::try_into("i-am-not-a-uuid").unwrap_err(); - assert!(!mbid_err.to_string().is_empty()); - assert!(!format!("{mbid_err:?}").is_empty()); -} diff --git a/src/external/database/serde/deserialize.rs b/src/external/database/serde/deserialize.rs index e456795..82cb220 100644 --- a/src/external/database/serde/deserialize.rs +++ b/src/external/database/serde/deserialize.rs @@ -3,10 +3,10 @@ use std::{collections::HashMap, fmt}; use serde::{de::Visitor, Deserialize, Deserializer}; use crate::{ + collection::musicbrainz::Mbid, core::collection::{ album::{Album, AlbumDate, AlbumId, AlbumSeq}, artist::{Artist, ArtistId}, - musicbrainz::{MbAlbumRef, MbArtistRef}, Collection, Error as CollectionError, }, external::database::serde::common::{SerdeAlbumPrimaryType, SerdeAlbumSecondaryType}, @@ -31,7 +31,7 @@ impl From for Collection { pub struct DeserializeArtist { name: String, sort: Option, - musicbrainz: Option, + musicbrainz: Option, properties: HashMap>, albums: Vec, } @@ -40,24 +40,24 @@ pub struct DeserializeArtist { pub struct DeserializeAlbum { title: String, seq: u8, - musicbrainz: Option, + musicbrainz: Option, primary_type: Option, secondary_types: Vec, } #[derive(Clone, Debug)] -pub struct DeserializeMbArtistRef(MbArtistRef); +pub struct DeserializeMbid(Mbid); -impl From for MbArtistRef { - fn from(value: DeserializeMbArtistRef) -> Self { +impl From for Mbid { + fn from(value: DeserializeMbid) -> Self { value.0 } } -struct DeserializeMbArtistRefVisitor; +struct DeserializeMbidVisitor; -impl<'de> Visitor<'de> for DeserializeMbArtistRefVisitor { - type Value = DeserializeMbArtistRef; +impl<'de> Visitor<'de> for DeserializeMbidVisitor { + type Value = DeserializeMbid; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid MusicBrainz identifier") @@ -67,55 +67,19 @@ impl<'de> Visitor<'de> for DeserializeMbArtistRefVisitor { where E: serde::de::Error, { - Ok(DeserializeMbArtistRef( - MbArtistRef::from_uuid_str(v).map_err(|e: CollectionError| E::custom(e.to_string()))?, + Ok(DeserializeMbid( + v.try_into() + .map_err(|e: CollectionError| E::custom(e.to_string()))?, )) } } -impl<'de> Deserialize<'de> for DeserializeMbArtistRef { +impl<'de> Deserialize<'de> for DeserializeMbid { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - deserializer.deserialize_str(DeserializeMbArtistRefVisitor) - } -} - -#[derive(Clone, Debug)] -pub struct DeserializeMbAlbumRef(MbAlbumRef); - -impl From for MbAlbumRef { - fn from(value: DeserializeMbAlbumRef) -> Self { - value.0 - } -} - -struct DeserializeMbAlbumRefVisitor; - -impl<'de> Visitor<'de> for DeserializeMbAlbumRefVisitor { - type Value = DeserializeMbAlbumRef; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a valid MusicBrainz identifier") - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - Ok(DeserializeMbAlbumRef( - MbAlbumRef::from_uuid_str(v).map_err(|e: CollectionError| E::custom(e.to_string()))?, - )) - } -} - -impl<'de> Deserialize<'de> for DeserializeMbAlbumRef { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_str(DeserializeMbAlbumRefVisitor) + deserializer.deserialize_str(DeserializeMbidVisitor) } } @@ -124,7 +88,7 @@ impl From for Artist { Artist { id: ArtistId::new(artist.name), sort: artist.sort.map(ArtistId::new), - musicbrainz: artist.musicbrainz.map(Into::into), + musicbrainz: artist.musicbrainz.map(Into::::into).map(Into::into), properties: artist.properties, albums: artist.albums.into_iter().map(Into::into).collect(), } @@ -137,7 +101,7 @@ impl From for Album { id: AlbumId { title: album.title }, date: AlbumDate::default(), seq: AlbumSeq(album.seq), - musicbrainz: album.musicbrainz.map(Into::into), + musicbrainz: album.musicbrainz.map(Into::::into).map(Into::into), primary_type: album.primary_type.map(Into::into), secondary_types: album.secondary_types.into_iter().map(Into::into).collect(), tracks: vec![], @@ -150,35 +114,17 @@ mod tests { use super::*; #[test] - fn deserialize_mb_artist_ref() { + fn deserialize_mbid() { let mbid = "\"d368baa8-21ca-4759-9731-0b2753071ad8\""; - let mbref: DeserializeMbArtistRef = serde_json::from_str(mbid).unwrap(); - let mbref: MbArtistRef = mbref.into(); + let mbid: DeserializeMbid = serde_json::from_str(mbid).unwrap(); + let mbid: Mbid = mbid.into(); assert_eq!( - mbref, - MbArtistRef::from_uuid_str("d368baa8-21ca-4759-9731-0b2753071ad8").unwrap() + mbid, + "d368baa8-21ca-4759-9731-0b2753071ad8".try_into().unwrap() ); let mbid = "null"; - let result: Result = serde_json::from_str(mbid); - assert!(result - .unwrap_err() - .to_string() - .contains("a valid MusicBrainz identifier")); - } - - #[test] - fn deserialize_mb_album_ref() { - let mbid = "\"d368baa8-21ca-4759-9731-0b2753071ad8\""; - let mbref: DeserializeMbAlbumRef = serde_json::from_str(mbid).unwrap(); - let mbref: MbAlbumRef = mbref.into(); - assert_eq!( - mbref, - MbAlbumRef::from_uuid_str("d368baa8-21ca-4759-9731-0b2753071ad8").unwrap() - ); - - let mbid = "null"; - let result: Result = serde_json::from_str(mbid); + let result: Result = serde_json::from_str(mbid); assert!(result .unwrap_err() .to_string() diff --git a/src/external/database/serde/serialize.rs b/src/external/database/serde/serialize.rs index 53d5b4b..874a01c 100644 --- a/src/external/database/serde/serialize.rs +++ b/src/external/database/serde/serialize.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use serde::Serialize; use crate::{ - collection::musicbrainz::{MbAlbumRef, MbArtistRef}, + collection::musicbrainz::Mbid, core::collection::{album::Album, artist::Artist, musicbrainz::IMusicBrainzRef, Collection}, external::database::serde::common::{SerdeAlbumPrimaryType, SerdeAlbumSecondaryType}, }; @@ -23,7 +23,7 @@ impl<'a> From<&'a Collection> for SerializeDatabase<'a> { pub struct SerializeArtist<'a> { name: &'a str, sort: Option<&'a str>, - musicbrainz: Option>, + musicbrainz: Option>, properties: BTreeMap<&'a str, &'a Vec>, albums: Vec>, } @@ -32,32 +32,20 @@ pub struct SerializeArtist<'a> { pub struct SerializeAlbum<'a> { title: &'a str, seq: u8, - musicbrainz: Option>, + musicbrainz: Option>, primary_type: Option, secondary_types: Vec, } #[derive(Clone, Debug)] -pub struct SerializeMbArtistRef<'a>(&'a MbArtistRef); +pub struct SerializeMbid<'a>(&'a Mbid); -impl<'a> Serialize for SerializeMbArtistRef<'a> { +impl<'a> Serialize for SerializeMbid<'a> { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { - serializer.serialize_str(&self.0.mbid().uuid().as_hyphenated().to_string()) - } -} - -#[derive(Clone, Debug)] -pub struct SerializeMbAlbumRef<'a>(&'a MbAlbumRef); - -impl<'a> Serialize for SerializeMbAlbumRef<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.0.mbid().uuid().as_hyphenated().to_string()) + serializer.serialize_str(&self.0.uuid().as_hyphenated().to_string()) } } @@ -66,7 +54,10 @@ impl<'a> From<&'a Artist> for SerializeArtist<'a> { SerializeArtist { name: &artist.id.name, sort: artist.sort.as_ref().map(|id| id.name.as_ref()), - musicbrainz: artist.musicbrainz.as_ref().map(SerializeMbArtistRef), + musicbrainz: artist + .musicbrainz + .as_ref() + .map(|mbref| SerializeMbid(mbref.mbid())), properties: artist .properties .iter() @@ -82,7 +73,10 @@ impl<'a> From<&'a Album> for SerializeAlbum<'a> { SerializeAlbum { title: &album.id.title, seq: album.seq.0, - musicbrainz: album.musicbrainz.as_ref().map(SerializeMbAlbumRef), + musicbrainz: album + .musicbrainz + .as_ref() + .map(|mbref| SerializeMbid(mbref.mbid())), primary_type: album.primary_type.map(Into::into), secondary_types: album .secondary_types diff --git a/src/external/musicbrainz/api/lookup.rs b/src/external/musicbrainz/api/lookup.rs index 41e4f07..a4df16a 100644 --- a/src/external/musicbrainz/api/lookup.rs +++ b/src/external/musicbrainz/api/lookup.rs @@ -2,7 +2,10 @@ use serde::Deserialize; use url::form_urlencoded; use crate::{ - collection::album::{AlbumDate, AlbumPrimaryType, AlbumSecondaryType}, + collection::{ + album::{AlbumDate, AlbumPrimaryType, AlbumSecondaryType}, + musicbrainz::Mbid, + }, external::musicbrainz::{ api::{ Error, MusicBrainzClient, SerdeAlbumDate, SerdeAlbumPrimaryType, @@ -10,7 +13,6 @@ use crate::{ }, IMusicBrainzHttp, }, - interface::musicbrainz::Mbid, }; impl MusicBrainzClient { diff --git a/src/external/musicbrainz/api/mod.rs b/src/external/musicbrainz/api/mod.rs index 9291561..b828450 100644 --- a/src/external/musicbrainz/api/mod.rs +++ b/src/external/musicbrainz/api/mod.rs @@ -3,9 +3,12 @@ use std::{fmt, num}; use serde::{de::Visitor, Deserialize, Deserializer}; use crate::{ - collection::album::{AlbumDate, AlbumPrimaryType, AlbumSecondaryType}, + collection::{ + album::{AlbumDate, AlbumPrimaryType, AlbumSecondaryType}, + musicbrainz::Mbid, + Error as CollectionError, + }, external::musicbrainz::HttpError, - interface::musicbrainz::{Mbid, MbidError}, }; pub mod lookup; @@ -92,7 +95,7 @@ impl<'de> Visitor<'de> for SerdeMbidVisitor { { Ok(SerdeMbid( v.try_into() - .map_err(|e: MbidError| E::custom(e.to_string()))?, + .map_err(|e: CollectionError| E::custom(e.to_string()))?, )) } } diff --git a/src/external/musicbrainz/api/search.rs b/src/external/musicbrainz/api/search.rs index 5378300..71ada9f 100644 --- a/src/external/musicbrainz/api/search.rs +++ b/src/external/musicbrainz/api/search.rs @@ -4,11 +4,8 @@ use serde::Deserialize; use url::form_urlencoded; use crate::{ - collection::album::AlbumDate, - core::{ - collection::album::{AlbumPrimaryType, AlbumSecondaryType}, - interface::musicbrainz::Mbid, - }, + collection::{album::AlbumDate, musicbrainz::Mbid}, + core::collection::album::{AlbumPrimaryType, AlbumSecondaryType}, external::musicbrainz::{ api::{ Error, MusicBrainzClient, SerdeAlbumDate, SerdeAlbumPrimaryType, diff --git a/src/tui/app/machine/browse.rs b/src/tui/app/machine/browse.rs index e0f0ec9..486ce5e 100644 --- a/src/tui/app/machine/browse.rs +++ b/src/tui/app/machine/browse.rs @@ -129,7 +129,7 @@ impl IAppInteractBrowse for AppMachine { #[cfg(test)] mod tests { use mockall::{predicate, Sequence}; - use musichoard::{collection::album::Album, interface::musicbrainz::Mbid}; + use musichoard::collection::{album::Album, musicbrainz::Mbid}; use crate::tui::{ app::{ diff --git a/src/tui/lib/external/musicbrainz/mod.rs b/src/tui/lib/external/musicbrainz/mod.rs index 78b8f5f..b68312a 100644 --- a/src/tui/lib/external/musicbrainz/mod.rs +++ b/src/tui/lib/external/musicbrainz/mod.rs @@ -1,7 +1,10 @@ //! Module for interacting with the [MusicBrainz API](https://musicbrainz.org/doc/MusicBrainz_API). use musichoard::{ - collection::album::{Album, AlbumDate}, + collection::{ + album::{Album, AlbumDate}, + musicbrainz::Mbid, + }, external::musicbrainz::{ api::{ search::{SearchReleaseGroupRequest, SearchReleaseGroupResponseReleaseGroup}, @@ -9,7 +12,6 @@ use musichoard::{ }, IMusicBrainzHttp, }, - interface::musicbrainz::Mbid, }; use crate::tui::lib::interface::musicbrainz::{Error, IMusicBrainz, Match}; diff --git a/src/tui/lib/interface/musicbrainz/mod.rs b/src/tui/lib/interface/musicbrainz/mod.rs index 354764a..1df8176 100644 --- a/src/tui/lib/interface/musicbrainz/mod.rs +++ b/src/tui/lib/interface/musicbrainz/mod.rs @@ -3,7 +3,7 @@ #[cfg(test)] use mockall::automock; -use musichoard::{collection::album::Album, interface::musicbrainz::Mbid}; +use musichoard::collection::{album::Album, musicbrainz::Mbid}; /// Trait for interacting with the MusicBrainz API. #[cfg_attr(test, automock)]