#![doc(html_root_url = "https://docs.rs/miniurl")]
const SCHEMA_CHARS : &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789\
+-.";
#[derive(PartialEq, Eq, Clone, Debug, Hash, PartialOrd, Ord)]
pub struct Url {
pub scheme: String,
pub netloc: String,
pub path: String,
pub query: Option<String>,
pub fragment: Option<String>,
pub username: Option<String>,
pub password: Option<String>,
pub host: Option<String>,
pub port: u16,
}
impl Url {
pub fn new() -> Url {
Url {
scheme: String::new(),
netloc: String::new(),
path: String::new(),
query: None,
fragment: None,
username: None,
password: None,
host: None,
port: 0,
}
}
pub fn parse<S: AsRef<str>>(s: S) -> Url {
let s = s.as_ref();
let (scheme, extra) = match s.find(':') {
Some(pos) => {
let (a, b) = s.split_at(pos);
let mut is_scheme = true;
for c in a.chars() {
if !SCHEMA_CHARS.contains(c) {
is_scheme = false;
break;
}
}
let (_a, _b) = if is_scheme { (a, &b[1..]) } else { ("", s) };
match _b.parse::<u16>() {
Ok(_) => ("", s), Err(_) => (_a, _b),
}
},
None => ("", s),
};
let (netloc, extra) = match extra.starts_with("//") {
true => {
let _extra = &extra[2..];
let mut a = _extra;
let mut b = "";
let mut delim = !0 as usize;
for c in "/?#".chars() {
match _extra.find(c) {
Some(pos) => {
if delim >= pos {
delim = pos;
let pair = _extra.split_at(pos);
a = pair.0;
b = pair.1;
}
},
None => continue,
}
}
(a, b)
},
false => ("", extra),
};
let (extra, fragment) = match extra.rfind('#') {
Some(pos) => {
let (a, b) = extra.split_at(pos);
(a, &b[1..])
},
None => (extra, ""),
};
let (path, query) = match extra.find('?') {
Some(pos) => {
let (a, b) = extra.split_at(pos);
(a, &b[1..])
},
None => (extra, ""),
};
let (userinfo, hostinfo) = match netloc.find('@') {
Some(pos) => {
let (a, b) = netloc.split_at(pos);
(a, &b[1..])
},
None => ("", netloc),
};
let (username, password) = match userinfo.find(':') {
Some(pos) => {
let (a, b) = userinfo.split_at(pos);
(a, &b[1..])
},
None => (userinfo, ""),
};
let (hostname, port) = match hostinfo.rfind(|c| c == ':' || c == ']') {
Some(pos) => {
let (a, b) = hostinfo.split_at(pos);
let _b = &b[1..];
match _b.parse::<u16>() {
Ok(number) => (a, number),
Err(_) => if scheme.to_string().to_lowercase() == "http" { (a, 80) } else { (a, 443) },
}
},
None => if scheme.to_string().to_lowercase() == "http" { (hostinfo, 80) } else { (hostinfo, 443) },
};
let hostname = hostname.trim_matches(|c| c == '[' || c == ']');
Url {
scheme: scheme.to_string().to_lowercase(),
netloc: netloc.to_string(),
path: if path.is_empty() {"/".to_owned()} else{path.to_string()},
query: if query.is_empty() { None } else { Some(query.to_string()) },
fragment: if fragment.is_empty() { None } else { Some(fragment.to_string()) },
username: if username.is_empty() { None } else { Some(username.to_string()) },
password: if password.is_empty() { None } else { Some(password.to_string()) },
host: if hostname.is_empty() { None } else { Some(hostname.to_string().to_lowercase()) },
port: port,
}
}
pub fn as_string(&self) -> String {
let mut result = format!("{}://{}{}", self.scheme, self.netloc, self.path);
if let Some(ref q) = self.query {
result.push_str(&format!("?{}", q));
}
if let Some(ref f) = self.fragment {
result.push_str(&format!("#{}", f));
}
return result;
}
pub fn request_string(&self) -> String {
let mut result = format!("{}",self.path);
if let Some(ref q) = self.query {
result.push_str(&format!("?{}", q));
}
if let Some(ref f) = self.fragment {
result.push_str(&format!("#{}", f));
}
return result;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_url_parse() {
let ori_url = "http://admin:password@google.com/foo?a=1&b=2#top";
let url = Url::parse(ori_url);
assert_eq!(url.scheme, "http");
assert_eq!(url.netloc, "admin:password@google.com");
assert_eq!(url.path, "/foo");
assert_eq!(url.query, Some("a=1&b=2".to_string()));
assert_eq!(url.fragment, Some("top".to_string()));
assert_eq!(url.username, Some("admin".to_string()));
assert_eq!(url.password, Some("password".to_string()));
assert_eq!(url.host, Some("google.com".to_string()));
assert_eq!(url.port, 80);
assert_eq!(ori_url.to_owned(),url.as_string());
assert_eq!("/foo?a=1&b=2#top".to_owned(),url.request_string());
}
#[test]
fn check_url_1(){
let ori_url = "http://www.google.com";
let url= Url::parse(ori_url);
println!("{:?}",url.path);
}
}