1#![doc(html_root_url = "https://docs.rs/miniurl")]
21
22
23const SCHEMA_CHARS : &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
24 abcdefghijklmnopqrstuvwxyz\
25 0123456789\
26 +-.";
27
28
29#[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 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 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), 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 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 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}