From 97c9080d50d0f40c6613097562468e75c5b0b475 Mon Sep 17 00:00:00 2001 From: Wojciech Kozlowski Date: Sun, 21 May 2023 12:00:01 +0200 Subject: [PATCH] Add unit tests and url checking --- src/lib.rs | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 48219cd..942e62c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,20 @@ pub trait IUuid { fn uuid(&self) -> &str; } +#[derive(Debug, PartialEq, Eq)] +enum UrlType { + MusicBrainz, + MusicButler, + Bandcamp, + Qobuz, +} + +#[derive(Debug, PartialEq, Eq)] +struct InvalidUrlError { + url_type: UrlType, + url: String, +} + /// MusicBrainz reference. #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub struct MusicBrainz { @@ -54,18 +68,25 @@ pub struct MusicBrainz { impl MusicBrainz { pub fn new>(url: S) -> Result { let url = Url::parse(url.as_ref())?; - let mbid: Mbid = match url.path_segments().and_then(|mut ps| ps.nth(2)) { + + if !url.domain().map(|u| u.ends_with("musicbrainz.org")).unwrap_or(false) { + return Err(Self::invalid_url_error(url).into()) + } + + let mbid: Mbid = match url.path_segments().and_then(|mut ps| ps.nth(1)) { Some(segment) => Mbid::new(segment)?, - None => { - return Err(Error::UrlParseError(format!( - "invalid MusicBrainz URL: {}", - url.as_str() - ))) - } + None => return Err(Self::invalid_url_error(url).into()), }; Ok(MusicBrainz { url, mbid }) } + + fn invalid_url_error>(url: S) -> InvalidUrlError { + InvalidUrlError { + url_type: UrlType::MusicBrainz, + url: url.into(), + } + } } impl IUrl for MusicBrainz { @@ -89,8 +110,20 @@ pub struct MusicButler { impl MusicButler { pub fn new>(url: S) -> Result { let url = Url::parse(url.as_ref())?; + + if !url.domain().map(|u| u.ends_with("musicbutler.io")).unwrap_or(false) { + return Err(Self::invalid_url_error(url).into()) + } + Ok(MusicButler { url }) } + + fn invalid_url_error>(url: S) -> InvalidUrlError { + InvalidUrlError { + url_type: UrlType::MusicButler, + url: url.into(), + } + } } impl IUrl for MusicButler { @@ -108,8 +141,20 @@ pub struct Bandcamp { impl Bandcamp { pub fn new>(url: S) -> Result { let url = Url::parse(url.as_ref())?; + + if !url.domain().map(|u| u.ends_with("bandcamp.com")).unwrap_or(false) { + return Err(Self::invalid_url_error(url).into()) + } + Ok(Bandcamp { url }) } + + fn invalid_url_error>(url: S) -> InvalidUrlError { + InvalidUrlError { + url_type: UrlType::Bandcamp, + url: url.into(), + } + } } impl IUrl for Bandcamp { @@ -127,8 +172,20 @@ pub struct Qobuz { impl Qobuz { pub fn new>(url: S) -> Result { let url = Url::parse(url.as_ref())?; + + if !url.domain().map(|u| u.ends_with("qobuz.com")).unwrap_or(false) { + return Err(Self::invalid_url_error(url).into()) + } + Ok(Qobuz { url }) } + + fn invalid_url_error>(url: S) -> InvalidUrlError { + InvalidUrlError { + url_type: UrlType::Qobuz, + url: url.into(), + } + } } impl IUrl for Qobuz { @@ -323,6 +380,8 @@ pub enum Error { DatabaseError(String), /// The [`MusicHoard`] failed to parse a user-provided URL. UrlParseError(String), + /// The user-provided URL is not valid. + InvalidUrlError(String), } impl fmt::Display for Error { @@ -333,6 +392,7 @@ impl fmt::Display for Error { write!(f, "failed to read/write from/to the database: {s}") } Self::UrlParseError(ref s) => write!(f, "failed to parse a user-provided URL: {s}"), + Self::InvalidUrlError(ref s) => write!(f, "user-provided URL is invalid: {s}"), } } } @@ -367,6 +427,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: InvalidUrlError) -> Error { + Error::InvalidUrlError(format!("{:?} - {}", err.url_type, err.url)) + } +} + /// The Music Hoard. It is responsible for pulling information from both the library and the /// database, ensuring its consistent and writing back any changes. pub struct MusicHoard { @@ -545,6 +611,49 @@ mod tests { items } + #[test] + fn musicbrainz() { + let uuid = "d368baa8-21ca-4759-9731-0b2753071ad8"; + let url = format!("https://musicbrainz.org/artist/{uuid}"); + let mb = MusicBrainz::new(&url).unwrap(); + assert_eq!(url, mb.url()); + assert_eq!(uuid, mb.uuid()); + + let url = format!("https://musicbrainz.org/artist/i-am-not-a-uuid"); + assert!(MusicBrainz::new(&url).is_err()); + + let url = format!("https://musicbrainz.org/artist"); + assert!(MusicBrainz::new(&url).is_err()); + } + + #[test] + fn urls() { + let musicbrainz = "https://musicbrainz.org/artist/d368baa8-21ca-4759-9731-0b2753071ad8"; + let musicbutler = "https://www.musicbutler.io/artist-page/483340948"; + let bandcamp = "https://thelasthangmen.bandcamp.com/"; + let qobuz = "https://www.qobuz.com/nl-nl/interpreter/the-last-hangmen/1244413"; + + assert!(MusicBrainz::new(&musicbrainz).is_ok()); + assert!(MusicBrainz::new(&musicbutler).is_err()); + assert!(MusicBrainz::new(&bandcamp).is_err()); + assert!(MusicBrainz::new(&qobuz).is_err()); + + assert!(MusicButler::new(&musicbrainz).is_err()); + assert!(MusicButler::new(&musicbutler).is_ok()); + assert!(MusicButler::new(&bandcamp).is_err()); + assert!(MusicButler::new(&qobuz).is_err()); + + assert!(Bandcamp::new(&musicbrainz).is_err()); + assert!(Bandcamp::new(&musicbutler).is_err()); + assert!(Bandcamp::new(&bandcamp).is_ok()); + assert!(Bandcamp::new(&qobuz).is_err()); + + assert!(Qobuz::new(&musicbrainz).is_err()); + assert!(Qobuz::new(&musicbutler).is_err()); + assert!(Qobuz::new(&bandcamp).is_err()); + assert!(Qobuz::new(&qobuz).is_ok()); + } + #[test] fn merge_track() { let left = Track {