use url::{Host, RelativeSchemeData};
use url::{whatwg_scheme_type_mapper};
use url::{mod, SchemeData, SchemeType};
use url::format::{PathFormatter, UserInfoFormatter};
use std::fmt::{mod, Show};
use {serialize};
#[deriving(PartialEq, Eq, Clone)]
pub struct Url {
pub scheme: String,
pub host: Host,
pub port: u16,
pub path: Vec<String>,
pub username: Option<String>,
pub password: Option<String>,
pub query: Option<String>,
pub fragment: Option<String>
}
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> {
match raw_url.scheme_data {
SchemeData::Relative(data) => {
let port: u16 = match data.port {
Some(port) => port,
None => {
match whatwg_scheme_type_mapper(raw_url.scheme.as_slice()) {
SchemeType::Relative(port) => port,
_ => return Err(format!("Invalid relative scheme: `{}`", raw_url.scheme))
}
}
};
let username = match data.username.as_slice() {
"" => None,
_ => Some(data.username)
};
let password = match data.password {
None => None,
Some(ref x) if x.as_slice().is_empty() => None,
Some(password) => Some(password)
};
Ok(Url {
scheme: raw_url.scheme,
host: data.host,
port: port,
path: data.path,
username: username,
password: password,
query: raw_url.query,
fragment: raw_url.fragment
})
},
_ => Err(format!("Not a relative scheme: `{}`", raw_url.scheme))
}
}
pub fn into_generic_url(self) -> url::Url {
let default_port = whatwg_scheme_type_mapper(self.scheme[]).default_port();
url::Url {
scheme: self.scheme,
scheme_data: SchemeData::Relative(
RelativeSchemeData {
username: self.username.unwrap_or("".to_string()),
password: self.password,
host: self.host,
port: Some(self.port),
default_port: default_port,
path: self.path
}
),
query: self.query,
fragment: self.fragment
}
}
}
impl Show for Url {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
try!(self.scheme.fmt(formatter));
try!("://".fmt(formatter));
try!(UserInfoFormatter {
username: self.username.as_ref().map(|s| s.as_slice()).unwrap_or(""),
password: self.password.as_ref().map(|s| s.as_slice())
}.fmt(formatter));
try!(self.host.fmt(formatter));
try!(":".fmt(formatter));
try!(self.port.fmt(formatter));
try!(PathFormatter { path: self.path.as_slice() }.fmt(formatter));
match self.query {
Some(ref query) => {
try!("?".fmt(formatter));
try!(query.fmt(formatter));
},
None => ()
}
match self.fragment {
Some(ref fragment) => {
try!("#".fmt(formatter));
try!(fragment.fmt(formatter));
},
None => ()
}
Ok(())
}
}
impl<E, S: serialize::Encoder<E>> serialize::Encodable<S, E> for Url {
fn encode(&self, encoder: &mut S) -> Result<(), E> {
encoder.emit_str(self.to_string().as_slice())
}
}
impl<E, D: serialize::Decoder<E>> serialize::Decodable<D, E> for Url {
fn decode(decoder: &mut D) -> Result<Url, E> {
Url::parse(try!(decoder.read_str()).as_slice()).map_err(|error| {
decoder.error(format!("URL parsing error: {}", error).as_slice())
})
}
}
#[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_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_formatting() {
assert_eq!(Url::parse("http://michael@example.com/path/?q=wow").unwrap().to_string(),
"http://michael@example.com:80/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.clone().into_generic_url();
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);
}
}