miniurl 0.1.3

miniurl is a url parser lib for rust
Documentation
//!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<String>,
    pub fragment: Option<String>,
    pub username: Option<String>,
    pub password: Option<String>,
    pub host: Option<String>,
    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: 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),   // 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::<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,
        }
    }

    /// 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);
    }
}