|
@@ -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<S>(url: S) -> Self
|
|
|
- where
|
|
|
- S: Into<String>,
|
|
|
- {
|
|
|
- let url: String = url.into();
|
|
|
- Self(url.trim_end_matches('/').to_string())
|
|
|
+ fn format_url(url: &str) -> Result<String, Error> {
|
|
|
+ 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::<Vec<&str>>()
|
|
|
+ .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<Self, Self::Err> {
|
|
|
- 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()
|
|
|
+ );
|
|
|
}
|
|
|
}
|