diff --git a/crates/cdk/src/mint/mod.rs b/crates/cdk/src/mint/mod.rs index f09d076e..1462e770 100644 --- a/crates/cdk/src/mint/mod.rs +++ b/crates/cdk/src/mint/mod.rs @@ -154,10 +154,8 @@ impl Mint { } } - let mint_url = MintUrl::from(mint_url); - Ok(Self { - mint_url, + mint_url: MintUrl::from(mint_url), keysets: Arc::new(RwLock::new(active_keysets)), secp_ctx, xpriv, @@ -1569,7 +1567,10 @@ mod tests { #[tokio::test] async fn mint_mod_rotate_keyset() -> Result<(), Error> { - let config: MintConfig = Default::default(); + let config = MintConfig::<'_> { + mint_url: "http://example.com", + ..Default::default() + }; let mint = create_mint(config).await?; let keysets = mint.keysets().await.unwrap(); diff --git a/crates/cdk/src/mint_url.rs b/crates/cdk/src/mint_url.rs index e5706510..f6b6fd51 100644 --- a/crates/cdk/src/mint_url.rs +++ b/crates/cdk/src/mint_url.rs @@ -16,6 +16,9 @@ pub enum Error { /// Url error #[error(transparent)] Url(#[from] ParseError), + /// Invalid URL structure + #[error("Invalid URL")] + InvalidUrl, } /// MintUrl Url @@ -23,13 +26,38 @@ pub enum Error { pub struct MintUrl(String); impl MintUrl { - /// New mint url - pub fn new(url: S) -> Self - where - S: Into, - { - let url: String = url.into(); - Self(url.trim_end_matches('/').to_string()) + fn format_url(url: &str) -> Result { + if url.is_empty() { + return Err(Error::InvalidUrl); + } + let url = url.trim_end_matches('/'); + // https://URL.com/path/TO/resource -> https://url.com/path/TO/resource + let protocol = url + .split("://") + .nth(0) + .ok_or(Error::InvalidUrl)? + .to_lowercase(); + let host = url + .split("://") + .nth(1) + .ok_or(Error::InvalidUrl)? + .split('/') + .nth(0) + .ok_or(Error::InvalidUrl)? + .to_lowercase(); + let path = url + .split("://") + .nth(1) + .ok_or(Error::InvalidUrl)? + .split('/') + .skip(1) + .collect::>() + .join("/"); + let mut formatted_url = format!("{}://{}", protocol, host); + if !path.is_empty() { + formatted_url.push_str(&format!("/{}", path)); + } + Ok(formatted_url) } /// Empty mint url @@ -42,11 +70,6 @@ impl MintUrl { let url: Url = self.try_into()?; Ok(url.join(path)?) } - - /// Remove trailing slashes from url - pub fn trim_trailing_slashes(&self) -> Self { - Self(self.to_string().trim_end_matches('/').to_string()) - } } impl<'de> Deserialize<'de> for MintUrl { @@ -65,7 +88,8 @@ where { fn from(url: S) -> Self { let url: String = url.into(); - Self(url.trim_end_matches('/').to_string()) + let formatted_url = Self::format_url(&url).unwrap(); + Self(formatted_url) } } @@ -73,7 +97,11 @@ impl FromStr for MintUrl { type Err = Error; fn from_str(url: &str) -> Result { - Ok(Self::from(url)) + let formatted_url = Self::format_url(url); + match formatted_url { + Ok(url) => Ok(Self(url)), + Err(_) => Err(Error::InvalidUrl), + } } } @@ -111,12 +139,29 @@ mod tests { let formatted_url = "http://url-to-check.com"; let very_trimmed_url = MintUrl::from_str(very_unformatted_url).unwrap(); - assert_eq!("http://url-to-check.com", very_trimmed_url.to_string()); + assert_eq!(formatted_url, very_trimmed_url.to_string()); let trimmed_url = MintUrl::from_str(unformatted_url).unwrap(); - assert_eq!("http://url-to-check.com", trimmed_url.to_string()); + assert_eq!(formatted_url, trimmed_url.to_string()); let unchanged_url = MintUrl::from_str(formatted_url).unwrap(); - assert_eq!("http://url-to-check.com", unchanged_url.to_string()); + assert_eq!(formatted_url, unchanged_url.to_string()); + } + #[test] + fn test_case_insensitive() { + let wrong_cased_url = "http://URL-to-check.com"; + let correct_cased_url = "http://url-to-check.com"; + + let cased_url_formatted = MintUrl::from_str(wrong_cased_url).unwrap(); + assert_eq!(correct_cased_url, cased_url_formatted.to_string()); + + let wrong_cased_url_with_path = "http://URL-to-check.com/PATH/to/check"; + let correct_cased_url_with_path = "http://url-to-check.com/PATH/to/check"; + + let cased_url_with_path_formatted = MintUrl::from_str(wrong_cased_url_with_path).unwrap(); + assert_eq!( + correct_cased_url_with_path, + cased_url_with_path_formatted.to_string() + ); } }