//!miniurl is url parser lib for rust,is simple and easy. //! # Examples //! //! ``` //! use miniurl::Url; //! //! let url = Url::parse("http://admin:password@google.com/foo?a=1&b=2#top"); //! 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); //! ``` //! #![doc(html_root_url = "https://docs.rs/miniurl")] const SCHEMA_CHARS : &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz\ 0123456789\ +-."; ///url object #[derive(PartialEq, Eq, Clone, Debug, Hash, PartialOrd, Ord)] pub struct Url { pub scheme: String, pub netloc: String, pub path: String, pub query: Option, pub fragment: Option, pub username: Option, pub password: Option, pub host: Option, pub port: u16, } impl Url { /// Creates a new `Url` initialized with the empty string or None value. 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, } } /// Parses a URL. /// /// # Examples /// /// ``` /// use miniurl::Url; /// /// let url = Url::parse("http://admin:password@google.com/foo?a=1&b=2#top"); /// 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); /// ``` /// pub fn parse>(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::() { Ok(_) => ("", s), // It is not a scheme because ':' // after the scheme is port number. 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::() { 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, } } /// Returns a URL string from a `Url` object. /// /// # Examples /// /// ``` /// use miniurl::Url; /// /// let ori_url = "http://www.google.com:80/?a=1&b=2"; /// let url = Url::parse(ori_url); /// assert_eq!(url.as_string(),ori_url.to_string()); /// ``` /// 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; } /// Returns a request string /// /// # Examples /// /// ``` /// use miniurl::Url; /// /// let ori_url = "http://www.google.com:80/?a=1&b=2"; /// let url = Url::parse(ori_url); /// assert_eq!("/?a=1&b=2".to_string(),url.request_string()); /// ``` /// 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); } }