miniurl/
lib.rs

1//!miniurl is url parser lib for rust,is simple and easy.
2//! # Examples
3//!
4//! ```
5//! use miniurl::Url;
6//!
7//! let url = Url::parse("http://admin:password@google.com/foo?a=1&b=2#top");
8//! assert_eq!(url.scheme, "http");
9//! assert_eq!(url.netloc, "admin:password@google.com");
10//! assert_eq!(url.path, "/foo");
11//! assert_eq!(url.query, Some("a=1&b=2".to_string()));
12//! assert_eq!(url.fragment, Some("top".to_string()));
13//! assert_eq!(url.username, Some("admin".to_string()));
14//! assert_eq!(url.password, Some("password".to_string()));
15//! assert_eq!(url.host, Some("google.com".to_string()));
16//! assert_eq!(url.port, 80);
17//! ```
18//!
19
20#![doc(html_root_url = "https://docs.rs/miniurl")]
21
22
23const SCHEMA_CHARS : &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
24                                     abcdefghijklmnopqrstuvwxyz\
25                                     0123456789\
26                                     +-.";
27
28
29///url object
30#[derive(PartialEq, Eq, Clone, Debug, Hash, PartialOrd, Ord)]
31pub struct Url {
32    pub scheme: String,
33    pub netloc: String,
34    pub path: String,
35    pub query: Option<String>,
36    pub fragment: Option<String>,
37    pub username: Option<String>,
38    pub password: Option<String>,
39    pub host: Option<String>,
40    pub port: u16,
41}
42
43
44impl Url {
45    /// Creates a new `Url` initialized with the empty string or None value.
46    pub fn new() -> Url {
47        Url {
48            scheme: String::new(),
49            netloc: String::new(),
50            path: String::new(),
51            query: None,
52            fragment: None,
53            username: None,
54            password: None,
55            host: None,
56            port: 0,
57        }
58    }
59
60    /// Parses a URL.
61    ///
62    /// # Examples
63    ///
64    /// ```
65    /// use miniurl::Url;
66    ///
67    /// let url = Url::parse("http://admin:password@google.com/foo?a=1&b=2#top");
68    /// assert_eq!(url.scheme, "http");
69    /// assert_eq!(url.netloc, "admin:password@google.com");
70    /// assert_eq!(url.path, "/foo");
71    /// assert_eq!(url.query, Some("a=1&b=2".to_string()));
72    /// assert_eq!(url.fragment, Some("top".to_string()));
73    /// assert_eq!(url.username, Some("admin".to_string()));
74    /// assert_eq!(url.password, Some("password".to_string()));
75    /// assert_eq!(url.host, Some("google.com".to_string()));
76    /// assert_eq!(url.port, 80);
77    /// ```
78    ///
79    pub fn parse<S: AsRef<str>>(s: S) -> Url {
80        let s = s.as_ref();
81        let (scheme, extra) = match s.find(':') {
82            Some(pos) => {
83                let (a, b) = s.split_at(pos);
84                let mut is_scheme = true;
85                for c in a.chars() {
86                    if !SCHEMA_CHARS.contains(c) {
87                        is_scheme = false;
88                        break;
89                    }
90                }
91                let (_a, _b) = if is_scheme { (a, &b[1..]) } else { ("", s) };
92                match _b.parse::<u16>() {
93                    Ok(_) => ("", s),   // It is not a scheme because ':'
94                    // after the scheme is port number.
95                    Err(_) => (_a, _b),
96                }
97            },
98            None => ("", s),
99        };
100        let (netloc, extra) = match extra.starts_with("//") {
101            true => {
102                let _extra = &extra[2..];
103                let mut a = _extra;
104                let mut b = "";
105                let mut delim = !0 as usize;
106                for c in "/?#".chars() {
107                    match _extra.find(c) {
108                        Some(pos) => {
109                            if delim >= pos {
110                                delim = pos;
111                                let pair = _extra.split_at(pos);
112                                a = pair.0;
113                                b = pair.1;
114                            }
115                        },
116                        None => continue,
117                    }
118                }
119                (a, b)
120            },
121            false => ("", extra),
122        };
123        let (extra, fragment) = match extra.rfind('#') {
124            Some(pos) => {
125                let (a, b) = extra.split_at(pos);
126                (a, &b[1..])
127            },
128            None => (extra, ""),
129        };
130        let (path, query) = match extra.find('?') {
131            Some(pos) => {
132                let (a, b) = extra.split_at(pos);
133                (a, &b[1..])
134            },
135            None => (extra, ""),
136        };
137        let (userinfo, hostinfo) = match netloc.find('@') {
138            Some(pos) => {
139                let (a, b) = netloc.split_at(pos);
140                (a, &b[1..])
141            },
142            None => ("", netloc),
143        };
144        let (username, password) = match userinfo.find(':') {
145            Some(pos) => {
146                let (a, b) = userinfo.split_at(pos);
147                (a, &b[1..])
148            },
149            None => (userinfo, ""),
150        };
151        let (hostname, port) = match hostinfo.rfind(|c| c == ':' || c == ']') {
152            Some(pos) => {
153                let (a, b) = hostinfo.split_at(pos);
154                let _b = &b[1..];
155                match _b.parse::<u16>() {
156                    Ok(number) => (a, number),
157                    Err(_) => if scheme.to_string().to_lowercase() == "http" { (a, 80) } else { (a, 443) },
158                }
159            },
160            None => if scheme.to_string().to_lowercase() == "http" { (hostinfo, 80) } else { (hostinfo, 443) },
161        };
162        let hostname = hostname.trim_matches(|c| c == '[' || c == ']');
163        Url {
164            scheme: scheme.to_string().to_lowercase(),
165            netloc: netloc.to_string(),
166            path: if path.is_empty() {"/".to_owned()} else{path.to_string()},
167            query: if query.is_empty() { None } else { Some(query.to_string()) },
168            fragment: if fragment.is_empty() { None } else { Some(fragment.to_string()) },
169            username: if username.is_empty() { None } else { Some(username.to_string()) },
170            password: if password.is_empty() { None } else { Some(password.to_string()) },
171            host: if hostname.is_empty() { None } else { Some(hostname.to_string().to_lowercase()) },
172            port: port,
173        }
174    }
175
176    /// Returns a URL string from a `Url` object.
177    ///
178    /// # Examples
179    ///
180    /// ```
181    /// use miniurl::Url;
182    ///
183    /// let ori_url = "http://www.google.com:80/?a=1&b=2";
184    /// let url = Url::parse(ori_url);
185    /// assert_eq!(url.as_string(),ori_url.to_string());
186    /// ```
187    ///
188    pub fn as_string(&self) -> String {
189        let mut result = format!("{}://{}{}", self.scheme, self.netloc, self.path);
190        if let Some(ref q) = self.query {
191            result.push_str(&format!("?{}", q));
192        }
193        if let Some(ref f) = self.fragment {
194            result.push_str(&format!("#{}", f));
195        }
196        return result;
197    }
198
199    /// Returns a request string
200    ///
201    /// # Examples
202    ///
203    /// ```
204    /// use miniurl::Url;
205    ///
206    /// let ori_url = "http://www.google.com:80/?a=1&b=2";
207    /// let url = Url::parse(ori_url);
208    /// assert_eq!("/?a=1&b=2".to_string(),url.request_string());
209    /// ```
210    ///
211    pub fn request_string(&self) -> String {
212        let mut result = format!("{}",self.path);
213        if let Some(ref q) = self.query {
214            result.push_str(&format!("?{}", q));
215        }
216        if let Some(ref f) = self.fragment {
217            result.push_str(&format!("#{}", f));
218        }
219        return result;
220    }
221
222}
223
224
225#[cfg(test)]
226mod tests {
227    use super::*;
228    #[test]
229    fn check_url_parse() {
230        let ori_url = "http://admin:password@google.com/foo?a=1&b=2#top";
231        let url = Url::parse(ori_url);
232        assert_eq!(url.scheme, "http");
233        assert_eq!(url.netloc, "admin:password@google.com");
234        assert_eq!(url.path, "/foo");
235        assert_eq!(url.query, Some("a=1&b=2".to_string()));
236        assert_eq!(url.fragment, Some("top".to_string()));
237        assert_eq!(url.username, Some("admin".to_string()));
238        assert_eq!(url.password, Some("password".to_string()));
239        assert_eq!(url.host, Some("google.com".to_string()));
240        assert_eq!(url.port, 80);
241
242        assert_eq!(ori_url.to_owned(),url.as_string());
243        assert_eq!("/foo?a=1&b=2#top".to_owned(),url.request_string());
244    }
245
246    #[test]
247    fn check_url_1(){
248        let ori_url = "http://www.google.com";
249        let url= Url::parse(ori_url);
250        println!("{:?}",url.path);
251    }
252}