use anyhow::{Context, Result};
use url::Url;
pub(crate) fn canonicalize(input: &str, username: Option<&str>) -> Result<String> {
let mut url = Url::parse(input)
.or_else(|_| Url::parse(format!("https://{input}").as_ref()))
.context("invalid url")?;
if let Some(username) = username {
url.set_username(username)
.map_err(|()| anyhow::anyhow!("failed setting username"))?;
}
Ok(url.into())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn canonicalize_identity() -> Result<()> {
let tests = [
"https://google.com/",
"mailto:me@example.com",
"http://localhost/",
];
for url in tests {
assert_eq!(String::from(url), canonicalize(url, None)?);
}
Ok(())
}
#[test]
fn canonicalize_host() -> Result<()> {
let tests = [
("https://google.com/", "google.com"),
("https://iphone.local/", "iphone.local"),
("https://localhost/", "localhost"),
];
for (want, inp) in tests {
assert_eq!(String::from(want), canonicalize(inp, None)?);
}
Ok(())
}
#[test]
fn canonicalize_username() -> Result<()> {
let tests = [
("https://test@example.com/", ("example.com", "test")),
(
"https://foo%40bar@baz.com/",
("https://baz.com/", "foo@bar"),
),
];
for (want, (url, username)) in tests {
assert_eq!(String::from(want), canonicalize(url, Some(username))?);
}
Ok(())
}
}