use url::{self, Host};
use std::str::FromStr;
use std::fmt;
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Url {
generic_url: url::Url,
}
impl Url {
pub fn parse(input: &str) -> Result<Url, String> {
match url::Url::parse(input) {
Ok(raw_url) => Url::from_generic_url(raw_url),
Err(e) => Err(format!("{}", e))
}
}
pub fn from_generic_url(raw_url: url::Url) -> Result<Url, String> {
if raw_url.cannot_be_a_base() {
Err(format!("Not a special scheme: `{}`", raw_url.scheme()))
} else if raw_url.port_or_known_default().is_none() {
Err(format!("Invalid special scheme: `{}`", raw_url.scheme()))
} else {
Ok(Url {
generic_url: raw_url,
})
}
}
#[deprecated(since="0.4.1", note="use `into` from the `Into` trait instead")]
pub fn into_generic_url(self) -> url::Url {
self.generic_url
}
pub fn scheme(&self) -> &str {
self.generic_url.scheme()
}
pub fn host(&self) -> Host<&str> {
self.generic_url.host().unwrap()
}
pub fn port(&self) -> u16 {
self.generic_url.port_or_known_default().unwrap()
}
pub fn path(&self) -> Vec<&str> {
self.generic_url.path_segments().unwrap().collect()
}
pub fn username(&self) -> Option<&str> {
match self.generic_url.username() {
"" => None,
username => Some(username)
}
}
pub fn password(&self) -> Option<&str> {
match self.generic_url.password() {
None => None,
Some(x) if x.is_empty() => None,
Some(password) => Some(password)
}
}
pub fn query(&self) -> Option<&str> {
self.generic_url.query()
}
pub fn fragment(&self) -> Option<&str> {
self.generic_url.fragment()
}
}
impl fmt::Display for Url {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
try!(self.generic_url.fmt(formatter));
Ok(())
}
}
impl Into<url::Url> for Url {
fn into(self) -> url::Url { self.generic_url }
}
impl AsRef<url::Url> for Url {
fn as_ref(&self) -> &url::Url { &self.generic_url }
}
impl AsMut<url::Url> for Url {
fn as_mut(&mut self) -> &mut url::Url { &mut self.generic_url }
}
impl FromStr for Url {
type Err = String;
#[inline]
fn from_str(input: &str) -> Result<Url, Self::Err> {
Url::parse(input)
}
}
#[cfg(test)]
mod test {
use super::Url;
#[test]
fn test_default_port() {
assert_eq!(Url::parse("http://example.com/wow").unwrap().port(), 80u16);
assert_eq!(Url::parse("https://example.com/wow").unwrap().port(), 443u16);
}
#[test]
fn test_explicit_port() {
assert_eq!(Url::parse("http://localhost:3097").unwrap().port(), 3097u16);
}
#[test]
fn test_empty_username() {
assert!(Url::parse("http://@example.com").unwrap().username().is_none());
assert!(Url::parse("http://:password@example.com").unwrap().username().is_none());
}
#[test]
fn test_not_empty_username() {
let url = Url::parse("http://john:pass@example.com").unwrap();
assert_eq!(url.username().unwrap(), "john");
let url = Url::parse("http://john:@example.com").unwrap();
assert_eq!(url.username().unwrap(), "john");
}
#[test]
fn test_empty_password() {
assert!(Url::parse("http://michael@example.com").unwrap().password().is_none());
assert!(Url::parse("http://:@example.com").unwrap().password().is_none());
}
#[test]
fn test_not_empty_password() {
let url = Url::parse("http://michael:pass@example.com").unwrap();
assert_eq!(url.password().unwrap(), "pass");
let url = Url::parse("http://:pass@example.com").unwrap();
assert_eq!(url.password().unwrap(), "pass");
}
#[test]
fn test_formatting() {
assert_eq!(Url::parse("http://michael@example.com/path/?q=wow").unwrap().to_string(),
"http://michael@example.com/path/?q=wow".to_string());
}
#[test]
fn test_conversion() {
let url_str = "https://user:password@iron.com:8080/path?q=wow#fragment";
let url = Url::parse(url_str).unwrap();
let raw_url: ::url::Url = url.clone().into();
assert_eq!(::url::Url::parse(url_str).unwrap(), raw_url);
let new_url = Url::from_generic_url(raw_url).unwrap();
assert_eq!(url, new_url);
}
#[test]
fn test_https_non_default_port() {
let parsed = Url::parse("https://example.com:8080").unwrap().to_string();
assert_eq!(parsed, "https://example.com:8080/");
}
#[test]
fn test_https_default_port() {
let parsed = Url::parse("https://example.com:443").unwrap().to_string();
assert_eq!(parsed, "https://example.com/");
}
#[test]
fn test_from_str_positive() {
let u = "http://example.com".parse::<Url>();
assert!(u.is_ok());
assert_eq!(u.unwrap(), Url::parse("http://example.com").unwrap());
}
#[test]
fn test_from_str_negative() {
let u = "not a url".parse::<Url>();
assert!(u.is_err());
}
}