(index<- ) ./liburl/lib.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Types/fns concerning URLs (see RFC 3986)
12
13 #![crate_id = "url#0.11-pre"]
14 #![crate_type = "rlib"]
15 #![crate_type = "dylib"]
16 #![license = "MIT/ASL2"]
17 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
18 html_favicon_url = "http://www.rust-lang.org/favicon.ico",
19 html_root_url = "http://static.rust-lang.org/doc/master")]
20 #![feature(default_type_params)]
21
22 extern crate collections;
23
24 use collections::HashMap;
25 use std::cmp::Eq;
26 use std::fmt;
27 use std::from_str::FromStr;
28 use std::hash::Hash;
29 use std::io::BufReader;
30 use std::strbuf::StrBuf;
31 use std::uint;
32
33 /// A Uniform Resource Locator (URL). A URL is a form of URI (Uniform Resource
34 /// Identifier) that includes network location information, such as hostname or
35 /// port number.
36 ///
37 /// # Example
38 ///
39 /// ```rust
40 /// use url::{Url, UserInfo};
41 ///
42 /// let url = Url { scheme: "https".to_owned(),
43 /// user: Some(UserInfo { user: "username".to_owned(), pass: None }),
44 /// host: "example.com".to_owned(),
45 /// port: Some("8080".to_owned()),
46 /// path: "/foo/bar".to_owned(),
47 /// query: vec!(("baz".to_owned(), "qux".to_owned())),
48 /// fragment: Some("quz".to_owned()) };
49 /// // https://username@example.com:8080/foo/bar?baz=qux#quz
50 /// ```
51 #[deriving(Clone, Eq, TotalEq)]
52 pub struct Url {
53 /// The scheme part of a URL, such as `https` in the above example.
54 pub scheme: ~str,
55 /// A URL subcomponent for user authentication. `username` in the above example.
56 pub user: Option<UserInfo>,
57 /// A domain name or IP address. For example, `example.com`.
58 pub host: ~str,
59 /// A TCP port number, for example `8080`.
60 pub port: Option<~str>,
61 /// The path component of a URL, for example `/foo/bar`.
62 pub path: ~str,
63 /// The query component of a URL. `vec!(("baz".to_owned(), "qux".to_owned()))` represents the
64 /// fragment `baz=qux` in the above example.
65 pub query: Query,
66 /// The fragment component, such as `quz`. Doesn't include the leading `#` character.
67 pub fragment: Option<~str>
68 }
69
70 #[deriving(Clone, Eq)]
71 pub struct Path {
72 /// The path component of a URL, for example `/foo/bar`.
73 pub path: ~str,
74 /// The query component of a URL. `vec!(("baz".to_owned(), "qux".to_owned()))` represents the
75 /// fragment `baz=qux` in the above example.
76 pub query: Query,
77 /// The fragment component, such as `quz`. Doesn't include the leading `#` character.
78 pub fragment: Option<~str>
79 }
80
81 /// An optional subcomponent of a URI authority component.
82 #[deriving(Clone, Eq, TotalEq)]
83 pub struct UserInfo {
84 /// The user name.
85 pub user: ~str,
86 /// Password or other scheme-specific authentication information.
87 pub pass: Option<~str>
88 }
89
90 /// Represents the query component of a URI.
91 pub type Query = Vec<(~str, ~str)>;
92
93 impl Url {
94 pub fn new(scheme: ~str,
95 user: Option<UserInfo>,
96 host: ~str,
97 port: Option<~str>,
98 path: ~str,
99 query: Query,
100 fragment: Option<~str>)
101 -> Url {
102 Url {
103 scheme: scheme,
104 user: user,
105 host: host,
106 port: port,
107 path: path,
108 query: query,
109 fragment: fragment,
110 }
111 }
112 }
113
114 impl Path {
115 pub fn new(path: ~str,
116 query: Query,
117 fragment: Option<~str>)
118 -> Path {
119 Path {
120 path: path,
121 query: query,
122 fragment: fragment,
123 }
124 }
125 }
126
127 impl UserInfo {
128 #[inline]
129 pub fn new(user: ~str, pass: Option<~str>) -> UserInfo {
130 UserInfo { user: user, pass: pass }
131 }
132 }
133
134 fn encode_inner(s: &str, full_url: bool) -> ~str {
135 let mut rdr = BufReader::new(s.as_bytes());
136 let mut out = StrBuf::new();
137
138 loop {
139 let mut buf = [0];
140 let ch = match rdr.read(buf) {
141 Err(..) => break,
142 Ok(..) => buf[0] as char,
143 };
144
145 match ch {
146 // unreserved:
147 'A' .. 'Z' |
148 'a' .. 'z' |
149 '0' .. '9' |
150 '-' | '.' | '_' | '~' => {
151 out.push_char(ch);
152 }
153 _ => {
154 if full_url {
155 match ch {
156 // gen-delims:
157 ':' | '/' | '?' | '#' | '[' | ']' | '@' |
158
159 // sub-delims:
160 '!' | '$' | '&' | '"' | '(' | ')' | '*' |
161 '+' | ',' | ';' | '=' => {
162 out.push_char(ch);
163 }
164
165 _ => out.push_str(format!("%{:X}", ch as uint))
166 }
167 } else {
168 out.push_str(format!("%{:X}", ch as uint));
169 }
170 }
171 }
172 }
173
174 out.into_owned()
175 }
176
177 /**
178 * Encodes a URI by replacing reserved characters with percent-encoded
179 * character sequences.
180 *
181 * This function is compliant with RFC 3986.
182 *
183 * # Example
184 *
185 * ```rust
186 * use url::encode;
187 *
188 * let url = encode("https://example.com/Rust (programming language)");
189 * println!("{}", url); // https://example.com/Rust%20(programming%20language)
190 * ```
191 */
192 pub fn encode(s: &str) -> ~str {
193 encode_inner(s, true)
194 }
195
196 /**
197 * Encodes a URI component by replacing reserved characters with percent-
198 * encoded character sequences.
199 *
200 * This function is compliant with RFC 3986.
201 */
202
203 pub fn encode_component(s: &str) -> ~str {
204 encode_inner(s, false)
205 }
206
207 fn decode_inner(s: &str, full_url: bool) -> ~str {
208 let mut rdr = BufReader::new(s.as_bytes());
209 let mut out = StrBuf::new();
210
211 loop {
212 let mut buf = [0];
213 let ch = match rdr.read(buf) {
214 Err(..) => break,
215 Ok(..) => buf[0] as char
216 };
217 match ch {
218 '%' => {
219 let mut bytes = [0, 0];
220 match rdr.read(bytes) {
221 Ok(2) => {}
222 _ => fail!() // FIXME: malformed url?
223 }
224 let ch = uint::parse_bytes(bytes, 16u).unwrap() as u8 as char;
225
226 if full_url {
227 // Only decode some characters:
228 match ch {
229 // gen-delims:
230 ':' | '/' | '?' | '#' | '[' | ']' | '@' |
231
232 // sub-delims:
233 '!' | '$' | '&' | '"' | '(' | ')' | '*' |
234 '+' | ',' | ';' | '=' => {
235 out.push_char('%');
236 out.push_char(bytes[0u] as char);
237 out.push_char(bytes[1u] as char);
238 }
239
240 ch => out.push_char(ch)
241 }
242 } else {
243 out.push_char(ch);
244 }
245 }
246 ch => out.push_char(ch)
247 }
248 }
249
250 out.into_owned()
251 }
252
253 /**
254 * Decodes a percent-encoded string representing a URI.
255 *
256 * This will only decode escape sequences generated by `encode`.
257 *
258 * # Example
259 *
260 * ```rust
261 * use url::decode;
262 *
263 * let url = decode("https://example.com/Rust%20(programming%20language)");
264 * println!("{}", url); // https://example.com/Rust (programming language)
265 * ```
266 */
267 pub fn decode(s: &str) -> ~str {
268 decode_inner(s, true)
269 }
270
271 /**
272 * Decode a string encoded with percent encoding.
273 */
274 pub fn decode_component(s: &str) -> ~str {
275 decode_inner(s, false)
276 }
277
278 fn encode_plus(s: &str) -> ~str {
279 let mut rdr = BufReader::new(s.as_bytes());
280 let mut out = StrBuf::new();
281
282 loop {
283 let mut buf = [0];
284 let ch = match rdr.read(buf) {
285 Ok(..) => buf[0] as char,
286 Err(..) => break,
287 };
288 match ch {
289 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '_' | '.' | '-' => {
290 out.push_char(ch);
291 }
292 ' ' => out.push_char('+'),
293 _ => out.push_str(format!("%{:X}", ch as uint))
294 }
295 }
296
297 out.into_owned()
298 }
299
300 /**
301 * Encode a hashmap to the 'application/x-www-form-urlencoded' media type.
302 */
303 pub fn encode_form_urlencoded(m: &HashMap<~str, Vec<~str>>) -> ~str {
304 let mut out = StrBuf::new();
305 let mut first = true;
306
307 for (key, values) in m.iter() {
308 let key = encode_plus(*key);
309
310 for value in values.iter() {
311 if first {
312 first = false;
313 } else {
314 out.push_char('&');
315 first = false;
316 }
317
318 out.push_str(format!("{}={}", key, encode_plus(*value)));
319 }
320 }
321
322 out.into_owned()
323 }
324
325 /**
326 * Decode a string encoded with the 'application/x-www-form-urlencoded' media
327 * type into a hashmap.
328 */
329 #[allow(experimental)]
330 pub fn decode_form_urlencoded(s: &[u8]) -> HashMap<~str, Vec<~str>> {
331 let mut rdr = BufReader::new(s);
332 let mut m: HashMap<~str,Vec<~str>> = HashMap::new();
333 let mut key = StrBuf::new();
334 let mut value = StrBuf::new();
335 let mut parsing_key = true;
336
337 loop {
338 let mut buf = [0];
339 let ch = match rdr.read(buf) {
340 Ok(..) => buf[0] as char,
341 Err(..) => break,
342 };
343 match ch {
344 '&' | ';' => {
345 if key.len() > 0 && value.len() > 0 {
346 let mut values = match m.pop_equiv(&key.as_slice()) {
347 Some(values) => values,
348 None => vec!(),
349 };
350
351 values.push(value.into_owned());
352 m.insert(key.into_owned(), values);
353 }
354
355 parsing_key = true;
356 key = StrBuf::new();
357 value = StrBuf::new();
358 }
359 '=' => parsing_key = false,
360 ch => {
361 let ch = match ch {
362 '%' => {
363 let mut bytes = [0, 0];
364 match rdr.read(bytes) {
365 Ok(2) => {}
366 _ => fail!() // FIXME: malformed?
367 }
368 uint::parse_bytes(bytes, 16u).unwrap() as u8 as char
369 }
370 '+' => ' ',
371 ch => ch
372 };
373
374 if parsing_key {
375 key.push_char(ch)
376 } else {
377 value.push_char(ch)
378 }
379 }
380 }
381 }
382
383 if key.len() > 0 && value.len() > 0 {
384 let mut values = match m.pop_equiv(&key.as_slice()) {
385 Some(values) => values,
386 None => vec!(),
387 };
388
389 values.push(value.into_owned());
390 m.insert(key.into_owned(), values);
391 }
392
393 m
394 }
395
396
397 fn split_char_first(s: &str, c: char) -> (~str, ~str) {
398 let len = s.len();
399 let mut index = len;
400 let mut mat = 0;
401 let mut rdr = BufReader::new(s.as_bytes());
402 loop {
403 let mut buf = [0];
404 let ch = match rdr.read(buf) {
405 Ok(..) => buf[0] as char,
406 Err(..) => break,
407 };
408 if ch == c {
409 // found a match, adjust markers
410 index = (rdr.tell().unwrap() as uint) - 1;
411 mat = 1;
412 break;
413 }
414 }
415 if index+mat == len {
416 return (s.slice(0, index).to_owned(), "".to_owned());
417 } else {
418 return (s.slice(0, index).to_owned(),
419 s.slice(index + mat, s.len()).to_owned());
420 }
421 }
422
423 impl fmt::Show for UserInfo {
424 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
425 match self.pass {
426 Some(ref pass) => write!(f.buf, "{}:{}@", self.user, *pass),
427 None => write!(f.buf, "{}@", self.user),
428 }
429 }
430 }
431
432 fn query_from_str(rawquery: &str) -> Query {
433 let mut query: Query = vec!();
434 if !rawquery.is_empty() {
435 for p in rawquery.split('&') {
436 let (k, v) = split_char_first(p, '=');
437 query.push((decode_component(k), decode_component(v)));
438 };
439 }
440 return query;
441 }
442
443 /**
444 * Converts an instance of a URI `Query` type to a string.
445 *
446 * # Example
447 *
448 * ```rust
449 * let query = vec!(("title".to_owned(), "The Village".to_owned()),
450 ("north".to_owned(), "52.91".to_owned()),
451 ("west".to_owned(), "4.10".to_owned()));
452 * println!("{}", url::query_to_str(&query)); // title=The%20Village&north=52.91&west=4.10
453 * ```
454 */
455 #[allow(unused_must_use)]
456 pub fn query_to_str(query: &Query) -> ~str {
457 use std::io::MemWriter;
458 use std::str;
459
460 let mut writer = MemWriter::new();
461 for (i, &(ref k, ref v)) in query.iter().enumerate() {
462 if i != 0 { write!(&mut writer, "&"); }
463 write!(&mut writer, "{}={}", encode_component(*k),
464 encode_component(*v));
465 }
466 str::from_utf8_lossy(writer.unwrap().as_slice()).into_owned()
467 }
468
469 /**
470 * Returns a tuple of the URI scheme and the rest of the URI, or a parsing error.
471 *
472 * Does not include the separating `:` character.
473 *
474 * # Example
475 *
476 * ```rust
477 * use url::get_scheme;
478 *
479 * let scheme = match get_scheme("https://example.com/") {
480 * Ok((sch, _)) => sch,
481 * Err(_) => "(None)".to_owned(),
482 * };
483 * println!("Scheme in use: {}.", scheme); // Scheme in use: https.
484 * ```
485 */
486 pub fn get_scheme(rawurl: &str) -> Result<(~str, ~str), ~str> {
487 for (i,c) in rawurl.chars().enumerate() {
488 match c {
489 'A' .. 'Z' | 'a' .. 'z' => continue,
490 '0' .. '9' | '+' | '-' | '.' => {
491 if i == 0 {
492 return Err("url: Scheme must begin with a letter.".to_owned());
493 }
494 continue;
495 }
496 ':' => {
497 if i == 0 {
498 return Err("url: Scheme cannot be empty.".to_owned());
499 } else {
500 return Ok((rawurl.slice(0,i).to_owned(),
501 rawurl.slice(i+1,rawurl.len()).to_owned()));
502 }
503 }
504 _ => {
505 return Err("url: Invalid character in scheme.".to_owned());
506 }
507 }
508 };
509 return Err("url: Scheme must be terminated with a colon.".to_owned());
510 }
511
512 #[deriving(Clone, Eq)]
513 enum Input {
514 Digit, // all digits
515 Hex, // digits and letters a-f
516 Unreserved // all other legal characters
517 }
518
519 // returns userinfo, host, port, and unparsed part, or an error
520 fn get_authority(rawurl: &str) ->
521 Result<(Option<UserInfo>, ~str, Option<~str>, ~str), ~str> {
522 if !rawurl.starts_with("//") {
523 // there is no authority.
524 return Ok((None, "".to_owned(), None, rawurl.to_str()));
525 }
526
527 enum State {
528 Start, // starting state
529 PassHostPort, // could be in user or port
530 Ip6Port, // either in ipv6 host or port
531 Ip6Host, // are in an ipv6 host
532 InHost, // are in a host - may be ipv6, but don't know yet
533 InPort // are in port
534 }
535
536 let len = rawurl.len();
537 let mut st = Start;
538 let mut input = Digit; // most restricted, start here.
539
540 let mut userinfo = None;
541 let mut host = "".to_owned();
542 let mut port = None;
543
544 let mut colon_count = 0;
545 let mut pos = 0;
546 let mut begin = 2;
547 let mut end = len;
548
549 for (i,c) in rawurl.chars().enumerate() {
550 if i < 2 { continue; } // ignore the leading //
551
552 // deal with input class first
553 match c {
554 '0' .. '9' => (),
555 'A' .. 'F' | 'a' .. 'f' => {
556 if input == Digit {
557 input = Hex;
558 }
559 }
560 'G' .. 'Z' | 'g' .. 'z' | '-' | '.' | '_' | '~' | '%' |
561 '&' |'\'' | '(' | ')' | '+' | '!' | '*' | ',' | ';' | '=' => {
562 input = Unreserved;
563 }
564 ':' | '@' | '?' | '#' | '/' => {
565 // separators, don't change anything
566 }
567 _ => {
568 return Err("Illegal character in authority".to_owned());
569 }
570 }
571
572 // now process states
573 match c {
574 ':' => {
575 colon_count += 1;
576 match st {
577 Start => {
578 pos = i;
579 st = PassHostPort;
580 }
581 PassHostPort => {
582 // multiple colons means ipv6 address.
583 if input == Unreserved {
584 return Err(
585 "Illegal characters in IPv6 address.".to_owned());
586 }
587 st = Ip6Host;
588 }
589 InHost => {
590 pos = i;
591 if input == Unreserved {
592 // must be port
593 host = rawurl.slice(begin, i).to_owned();
594 st = InPort;
595 } else {
596 // can't be sure whether this is an ipv6 address or a port
597 st = Ip6Port;
598 }
599 }
600 Ip6Port => {
601 if input == Unreserved {
602 return Err("Illegal characters in authority.".to_owned());
603 }
604 st = Ip6Host;
605 }
606 Ip6Host => {
607 if colon_count > 7 {
608 host = rawurl.slice(begin, i).to_owned();
609 pos = i;
610 st = InPort;
611 }
612 }
613 _ => {
614 return Err("Invalid ':' in authority.".to_owned());
615 }
616 }
617 input = Digit; // reset input class
618 }
619
620 '@' => {
621 input = Digit; // reset input class
622 colon_count = 0; // reset count
623 match st {
624 Start => {
625 let user = rawurl.slice(begin, i).to_owned();
626 userinfo = Some(UserInfo::new(user, None));
627 st = InHost;
628 }
629 PassHostPort => {
630 let user = rawurl.slice(begin, pos).to_owned();
631 let pass = rawurl.slice(pos+1, i).to_owned();
632 userinfo = Some(UserInfo::new(user, Some(pass)));
633 st = InHost;
634 }
635 _ => {
636 return Err("Invalid '@' in authority.".to_owned());
637 }
638 }
639 begin = i+1;
640 }
641
642 '?' | '#' | '/' => {
643 end = i;
644 break;
645 }
646 _ => ()
647 }
648 }
649
650 // finish up
651 match st {
652 Start => {
653 host = rawurl.slice(begin, end).to_owned();
654 }
655 PassHostPort | Ip6Port => {
656 if input != Digit {
657 return Err("Non-digit characters in port.".to_owned());
658 }
659 host = rawurl.slice(begin, pos).to_owned();
660 port = Some(rawurl.slice(pos+1, end).to_owned());
661 }
662 Ip6Host | InHost => {
663 host = rawurl.slice(begin, end).to_owned();
664 }
665 InPort => {
666 if input != Digit {
667 return Err("Non-digit characters in port.".to_owned());
668 }
669 port = Some(rawurl.slice(pos+1, end).to_owned());
670 }
671 }
672
673 let rest = rawurl.slice(end, len).to_owned();
674 return Ok((userinfo, host, port, rest));
675 }
676
677
678 // returns the path and unparsed part of url, or an error
679 fn get_path(rawurl: &str, authority: bool) ->
680 Result<(~str, ~str), ~str> {
681 let len = rawurl.len();
682 let mut end = len;
683 for (i,c) in rawurl.chars().enumerate() {
684 match c {
685 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '&' |'\'' | '(' | ')' | '.'
686 | '@' | ':' | '%' | '/' | '+' | '!' | '*' | ',' | ';' | '='
687 | '_' | '-' | '~' => {
688 continue;
689 }
690 '?' | '#' => {
691 end = i;
692 break;
693 }
694 _ => return Err("Invalid character in path.".to_owned())
695 }
696 }
697
698 if authority {
699 if end != 0 && !rawurl.starts_with("/") {
700 return Err("Non-empty path must begin with\
701 '/' in presence of authority.".to_owned());
702 }
703 }
704
705 return Ok((decode_component(rawurl.slice(0, end)),
706 rawurl.slice(end, len).to_owned()));
707 }
708
709 // returns the parsed query and the fragment, if present
710 fn get_query_fragment(rawurl: &str) ->
711 Result<(Query, Option<~str>), ~str> {
712 if !rawurl.starts_with("?") {
713 if rawurl.starts_with("#") {
714 let f = decode_component(rawurl.slice(
715 1,
716 rawurl.len()));
717 return Ok((vec!(), Some(f)));
718 } else {
719 return Ok((vec!(), None));
720 }
721 }
722 let (q, r) = split_char_first(rawurl.slice(1, rawurl.len()), '#');
723 let f = if r.len() != 0 {
724 Some(decode_component(r)) } else { None };
725 return Ok((query_from_str(q), f));
726 }
727
728 /**
729 * Parses a URL, converting it from a string to `Url` representation.
730 *
731 * # Arguments
732 *
733 * `rawurl` - a string representing the full URL, including scheme.
734 *
735 * # Returns
736 *
737 * A `Url` struct type representing the URL.
738 */
739 pub fn from_str(rawurl: &str) -> Result<Url, ~str> {
740 // scheme
741 let (scheme, rest) = match get_scheme(rawurl) {
742 Ok(val) => val,
743 Err(e) => return Err(e),
744 };
745
746 // authority
747 let (userinfo, host, port, rest) = match get_authority(rest) {
748 Ok(val) => val,
749 Err(e) => return Err(e),
750 };
751
752 // path
753 let has_authority = if host == "".to_owned() { false } else { true };
754 let (path, rest) = match get_path(rest, has_authority) {
755 Ok(val) => val,
756 Err(e) => return Err(e),
757 };
758
759 // query and fragment
760 let (query, fragment) = match get_query_fragment(rest) {
761 Ok(val) => val,
762 Err(e) => return Err(e),
763 };
764
765 Ok(Url::new(scheme, userinfo, host, port, path, query, fragment))
766 }
767
768 pub fn path_from_str(rawpath: &str) -> Result<Path, ~str> {
769 let (path, rest) = match get_path(rawpath, false) {
770 Ok(val) => val,
771 Err(e) => return Err(e)
772 };
773
774 // query and fragment
775 let (query, fragment) = match get_query_fragment(rest) {
776 Ok(val) => val,
777 Err(e) => return Err(e),
778 };
779
780 Ok(Path{ path: path, query: query, fragment: fragment })
781 }
782
783 impl FromStr for Url {
784 fn from_str(s: &str) -> Option<Url> {
785 match from_str(s) {
786 Ok(url) => Some(url),
787 Err(_) => None
788 }
789 }
790 }
791
792 impl FromStr for Path {
793 fn from_str(s: &str) -> Option<Path> {
794 match path_from_str(s) {
795 Ok(path) => Some(path),
796 Err(_) => None
797 }
798 }
799 }
800
801 impl fmt::Show for Url {
802 /**
803 * Converts a URL from `Url` to string representation.
804 *
805 * # Arguments
806 *
807 * `url` - a URL.
808 *
809 * # Returns
810 *
811 * A string that contains the formatted URL. Note that this will usually
812 * be an inverse of `from_str` but might strip out unneeded separators;
813 * for example, "http://somehost.com?", when parsed and formatted, will
814 * result in just "http://somehost.com".
815 */
816 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
817 try!(write!(f.buf, "{}:", self.scheme));
818
819 if !self.host.is_empty() {
820 try!(write!(f.buf, "//"));
821 match self.user {
822 Some(ref user) => try!(write!(f.buf, "{}", *user)),
823 None => {}
824 }
825 match self.port {
826 Some(ref port) => try!(write!(f.buf, "{}:{}", self.host,
827 *port)),
828 None => try!(write!(f.buf, "{}", self.host)),
829 }
830 }
831
832 try!(write!(f.buf, "{}", self.path));
833
834 if !self.query.is_empty() {
835 try!(write!(f.buf, "?{}", query_to_str(&self.query)));
836 }
837
838 match self.fragment {
839 Some(ref fragment) => write!(f.buf, "\\#{}",
840 encode_component(*fragment)),
841 None => Ok(()),
842 }
843 }
844 }
845
846 impl fmt::Show for Path {
847 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
848 try!(write!(f.buf, "{}", self.path));
849 if !self.query.is_empty() {
850 try!(write!(f.buf, "?{}", self.query))
851 }
852
853 match self.fragment {
854 Some(ref fragment) => {
855 write!(f.buf, "\\#{}", encode_component(*fragment))
856 }
857 None => Ok(())
858 }
859 }
860 }
861
862 impl<S: Writer> Hash<S> for Url {
863 fn hash(&self, state: &mut S) {
864 self.to_str().hash(state)
865 }
866 }
867
868 impl<S: Writer> Hash<S> for Path {
869 fn hash(&self, state: &mut S) {
870 self.to_str().hash(state)
871 }
872 }
873
874 // Put a few tests outside of the 'test' module so they can test the internal
875 // functions and those functions don't need 'pub'
876
877 #[test]
878 fn test_split_char_first() {
879 let (u,v) = split_char_first("hello, sweet world", ',');
880 assert_eq!(u, "hello".to_owned());
881 assert_eq!(v, " sweet world".to_owned());
882
883 let (u,v) = split_char_first("hello sweet world", ',');
884 assert_eq!(u, "hello sweet world".to_owned());
885 assert_eq!(v, "".to_owned());
886 }
887
888 #[test]
889 fn test_get_authority() {
890 let (u, h, p, r) = get_authority(
891 "//user:pass@rust-lang.org/something").unwrap();
892 assert_eq!(u, Some(UserInfo::new("user".to_owned(), Some("pass".to_owned()))));
893 assert_eq!(h, "rust-lang.org".to_owned());
894 assert!(p.is_none());
895 assert_eq!(r, "/something".to_owned());
896
897 let (u, h, p, r) = get_authority(
898 "//rust-lang.org:8000?something").unwrap();
899 assert!(u.is_none());
900 assert_eq!(h, "rust-lang.org".to_owned());
901 assert_eq!(p, Some("8000".to_owned()));
902 assert_eq!(r, "?something".to_owned());
903
904 let (u, h, p, r) = get_authority(
905 "//rust-lang.org#blah").unwrap();
906 assert!(u.is_none());
907 assert_eq!(h, "rust-lang.org".to_owned());
908 assert!(p.is_none());
909 assert_eq!(r, "#blah".to_owned());
910
911 // ipv6 tests
912 let (_, h, _, _) = get_authority(
913 "//2001:0db8:85a3:0042:0000:8a2e:0370:7334#blah").unwrap();
914 assert_eq!(h, "2001:0db8:85a3:0042:0000:8a2e:0370:7334".to_owned());
915
916 let (_, h, p, _) = get_authority(
917 "//2001:0db8:85a3:0042:0000:8a2e:0370:7334:8000#blah").unwrap();
918 assert_eq!(h, "2001:0db8:85a3:0042:0000:8a2e:0370:7334".to_owned());
919 assert_eq!(p, Some("8000".to_owned()));
920
921 let (u, h, p, _) = get_authority(
922 "//us:p@2001:0db8:85a3:0042:0000:8a2e:0370:7334:8000#blah"
923 ).unwrap();
924 assert_eq!(u, Some(UserInfo::new("us".to_owned(), Some("p".to_owned()))));
925 assert_eq!(h, "2001:0db8:85a3:0042:0000:8a2e:0370:7334".to_owned());
926 assert_eq!(p, Some("8000".to_owned()));
927
928 // invalid authorities;
929 assert!(get_authority("//user:pass@rust-lang:something").is_err());
930 assert!(get_authority("//user@rust-lang:something:/path").is_err());
931 assert!(get_authority(
932 "//2001:0db8:85a3:0042:0000:8a2e:0370:7334:800a").is_err());
933 assert!(get_authority(
934 "//2001:0db8:85a3:0042:0000:8a2e:0370:7334:8000:00").is_err());
935
936 // these parse as empty, because they don't start with '//'
937 let (_, h, _, _) = get_authority("user:pass@rust-lang").unwrap();
938 assert_eq!(h, "".to_owned());
939 let (_, h, _, _) = get_authority("rust-lang.org").unwrap();
940 assert_eq!(h, "".to_owned());
941 }
942
943 #[test]
944 fn test_get_path() {
945 let (p, r) = get_path("/something+%20orother", true).unwrap();
946 assert_eq!(p, "/something+ orother".to_owned());
947 assert_eq!(r, "".to_owned());
948 let (p, r) = get_path("test@email.com#fragment", false).unwrap();
949 assert_eq!(p, "test@email.com".to_owned());
950 assert_eq!(r, "#fragment".to_owned());
951 let (p, r) = get_path("/gen/:addr=?q=v", false).unwrap();
952 assert_eq!(p, "/gen/:addr=".to_owned());
953 assert_eq!(r, "?q=v".to_owned());
954
955 //failure cases
956 assert!(get_path("something?q", true).is_err());
957 }
958
959 #[cfg(test)]
960 mod tests {
961 use {encode_form_urlencoded, decode_form_urlencoded,
962 decode, encode, from_str, encode_component, decode_component,
963 path_from_str, UserInfo, get_scheme};
964
965 use collections::HashMap;
966
967 #[test]
968 fn test_url_parse() {
969 let url = "http://user:pass@rust-lang.org:8080/doc/~u?s=v#something".to_owned();
970
971 let up = from_str(url);
972 let u = up.unwrap();
973 assert_eq!(&u.scheme, &"http".to_owned());
974 assert_eq!(&u.user, &Some(UserInfo::new("user".to_owned(), Some("pass".to_owned()))));
975 assert_eq!(&u.host, &"rust-lang.org".to_owned());
976 assert_eq!(&u.port, &Some("8080".to_owned()));
977 assert_eq!(&u.path, &"/doc/~u".to_owned());
978 assert_eq!(&u.query, &vec!(("s".to_owned(), "v".to_owned())));
979 assert_eq!(&u.fragment, &Some("something".to_owned()));
980 }
981
982 #[test]
983 fn test_path_parse() {
984 let path = "/doc/~u?s=v#something".to_owned();
985
986 let up = path_from_str(path);
987 let u = up.unwrap();
988 assert_eq!(&u.path, &"/doc/~u".to_owned());
989 assert_eq!(&u.query, &vec!(("s".to_owned(), "v".to_owned())));
990 assert_eq!(&u.fragment, &Some("something".to_owned()));
991 }
992
993 #[test]
994 fn test_url_parse_host_slash() {
995 let urlstr = "http://0.42.42.42/".to_owned();
996 let url = from_str(urlstr).unwrap();
997 assert!(url.host == "0.42.42.42".to_owned());
998 assert!(url.path == "/".to_owned());
999 }
1000
1001 #[test]
1002 fn test_path_parse_host_slash() {
1003 let pathstr = "/".to_owned();
1004 let path = path_from_str(pathstr).unwrap();
1005 assert!(path.path == "/".to_owned());
1006 }
1007
1008 #[test]
1009 fn test_url_host_with_port() {
1010 let urlstr = "scheme://host:1234".to_owned();
1011 let url = from_str(urlstr).unwrap();
1012 assert_eq!(&url.scheme, &"scheme".to_owned());
1013 assert_eq!(&url.host, &"host".to_owned());
1014 assert_eq!(&url.port, &Some("1234".to_owned()));
1015 assert_eq!(&url.path, &"".to_owned()); // is empty path really correct? Other tests think so
1016 let urlstr = "scheme://host:1234/".to_owned();
1017 let url = from_str(urlstr).unwrap();
1018 assert_eq!(&url.scheme, &"scheme".to_owned());
1019 assert_eq!(&url.host, &"host".to_owned());
1020 assert_eq!(&url.port, &Some("1234".to_owned()));
1021 assert_eq!(&url.path, &"/".to_owned());
1022 }
1023
1024 #[test]
1025 fn test_url_with_underscores() {
1026 let urlstr = "http://dotcom.com/file_name.html".to_owned();
1027 let url = from_str(urlstr).unwrap();
1028 assert!(url.path == "/file_name.html".to_owned());
1029 }
1030
1031 #[test]
1032 fn test_path_with_underscores() {
1033 let pathstr = "/file_name.html".to_owned();
1034 let path = path_from_str(pathstr).unwrap();
1035 assert!(path.path == "/file_name.html".to_owned());
1036 }
1037
1038 #[test]
1039 fn test_url_with_dashes() {
1040 let urlstr = "http://dotcom.com/file-name.html".to_owned();
1041 let url = from_str(urlstr).unwrap();
1042 assert!(url.path == "/file-name.html".to_owned());
1043 }
1044
1045 #[test]
1046 fn test_path_with_dashes() {
1047 let pathstr = "/file-name.html".to_owned();
1048 let path = path_from_str(pathstr).unwrap();
1049 assert!(path.path == "/file-name.html".to_owned());
1050 }
1051
1052 #[test]
1053 fn test_no_scheme() {
1054 assert!(get_scheme("noschemehere.html").is_err());
1055 }
1056
1057 #[test]
1058 fn test_invalid_scheme_errors() {
1059 assert!(from_str("99://something").is_err());
1060 assert!(from_str("://something").is_err());
1061 }
1062
1063 #[test]
1064 fn test_full_url_parse_and_format() {
1065 let url = "http://user:pass@rust-lang.org/doc?s=v#something".to_owned();
1066 assert_eq!(from_str(url).unwrap().to_str(), url);
1067 }
1068
1069 #[test]
1070 fn test_userless_url_parse_and_format() {
1071 let url = "http://rust-lang.org/doc?s=v#something".to_owned();
1072 assert_eq!(from_str(url).unwrap().to_str(), url);
1073 }
1074
1075 #[test]
1076 fn test_queryless_url_parse_and_format() {
1077 let url = "http://user:pass@rust-lang.org/doc#something".to_owned();
1078 assert_eq!(from_str(url).unwrap().to_str(), url);
1079 }
1080
1081 #[test]
1082 fn test_empty_query_url_parse_and_format() {
1083 let url = "http://user:pass@rust-lang.org/doc?#something".to_owned();
1084 let should_be = "http://user:pass@rust-lang.org/doc#something".to_owned();
1085 assert_eq!(from_str(url).unwrap().to_str(), should_be);
1086 }
1087
1088 #[test]
1089 fn test_fragmentless_url_parse_and_format() {
1090 let url = "http://user:pass@rust-lang.org/doc?q=v".to_owned();
1091 assert_eq!(from_str(url).unwrap().to_str(), url);
1092 }
1093
1094 #[test]
1095 fn test_minimal_url_parse_and_format() {
1096 let url = "http://rust-lang.org/doc".to_owned();
1097 assert_eq!(from_str(url).unwrap().to_str(), url);
1098 }
1099
1100 #[test]
1101 fn test_url_with_port_parse_and_format() {
1102 let url = "http://rust-lang.org:80/doc".to_owned();
1103 assert_eq!(from_str(url).unwrap().to_str(), url);
1104 }
1105
1106 #[test]
1107 fn test_scheme_host_only_url_parse_and_format() {
1108 let url = "http://rust-lang.org".to_owned();
1109 assert_eq!(from_str(url).unwrap().to_str(), url);
1110 }
1111
1112 #[test]
1113 fn test_pathless_url_parse_and_format() {
1114 let url = "http://user:pass@rust-lang.org?q=v#something".to_owned();
1115 assert_eq!(from_str(url).unwrap().to_str(), url);
1116 }
1117
1118 #[test]
1119 fn test_scheme_host_fragment_only_url_parse_and_format() {
1120 let url = "http://rust-lang.org#something".to_owned();
1121 assert_eq!(from_str(url).unwrap().to_str(), url);
1122 }
1123
1124 #[test]
1125 fn test_url_component_encoding() {
1126 let url = "http://rust-lang.org/doc%20uments?ba%25d%20=%23%26%2B".to_owned();
1127 let u = from_str(url).unwrap();
1128 assert!(u.path == "/doc uments".to_owned());
1129 assert!(u.query == vec!(("ba%d ".to_owned(), "#&+".to_owned())));
1130 }
1131
1132 #[test]
1133 fn test_path_component_encoding() {
1134 let path = "/doc%20uments?ba%25d%20=%23%26%2B".to_owned();
1135 let p = path_from_str(path).unwrap();
1136 assert!(p.path == "/doc uments".to_owned());
1137 assert!(p.query == vec!(("ba%d ".to_owned(), "#&+".to_owned())));
1138 }
1139
1140 #[test]
1141 fn test_url_without_authority() {
1142 let url = "mailto:test@email.com".to_owned();
1143 assert_eq!(from_str(url).unwrap().to_str(), url);
1144 }
1145
1146 #[test]
1147 fn test_encode() {
1148 assert_eq!(encode(""), "".to_owned());
1149 assert_eq!(encode("http://example.com"), "http://example.com".to_owned());
1150 assert_eq!(encode("foo bar% baz"), "foo%20bar%25%20baz".to_owned());
1151 assert_eq!(encode(" "), "%20".to_owned());
1152 assert_eq!(encode("!"), "!".to_owned());
1153 assert_eq!(encode("\""), "\"".to_owned());
1154 assert_eq!(encode("#"), "#".to_owned());
1155 assert_eq!(encode("$"), "$".to_owned());
1156 assert_eq!(encode("%"), "%25".to_owned());
1157 assert_eq!(encode("&"), "&".to_owned());
1158 assert_eq!(encode("'"), "%27".to_owned());
1159 assert_eq!(encode("("), "(".to_owned());
1160 assert_eq!(encode(")"), ")".to_owned());
1161 assert_eq!(encode("*"), "*".to_owned());
1162 assert_eq!(encode("+"), "+".to_owned());
1163 assert_eq!(encode(","), ",".to_owned());
1164 assert_eq!(encode("/"), "/".to_owned());
1165 assert_eq!(encode(":"), ":".to_owned());
1166 assert_eq!(encode(";"), ";".to_owned());
1167 assert_eq!(encode("="), "=".to_owned());
1168 assert_eq!(encode("?"), "?".to_owned());
1169 assert_eq!(encode("@"), "@".to_owned());
1170 assert_eq!(encode("["), "[".to_owned());
1171 assert_eq!(encode("]"), "]".to_owned());
1172 }
1173
1174 #[test]
1175 fn test_encode_component() {
1176 assert_eq!(encode_component(""), "".to_owned());
1177 assert!(encode_component("http://example.com") ==
1178 "http%3A%2F%2Fexample.com".to_owned());
1179 assert!(encode_component("foo bar% baz") ==
1180 "foo%20bar%25%20baz".to_owned());
1181 assert_eq!(encode_component(" "), "%20".to_owned());
1182 assert_eq!(encode_component("!"), "%21".to_owned());
1183 assert_eq!(encode_component("#"), "%23".to_owned());
1184 assert_eq!(encode_component("$"), "%24".to_owned());
1185 assert_eq!(encode_component("%"), "%25".to_owned());
1186 assert_eq!(encode_component("&"), "%26".to_owned());
1187 assert_eq!(encode_component("'"), "%27".to_owned());
1188 assert_eq!(encode_component("("), "%28".to_owned());
1189 assert_eq!(encode_component(")"), "%29".to_owned());
1190 assert_eq!(encode_component("*"), "%2A".to_owned());
1191 assert_eq!(encode_component("+"), "%2B".to_owned());
1192 assert_eq!(encode_component(","), "%2C".to_owned());
1193 assert_eq!(encode_component("/"), "%2F".to_owned());
1194 assert_eq!(encode_component(":"), "%3A".to_owned());
1195 assert_eq!(encode_component(";"), "%3B".to_owned());
1196 assert_eq!(encode_component("="), "%3D".to_owned());
1197 assert_eq!(encode_component("?"), "%3F".to_owned());
1198 assert_eq!(encode_component("@"), "%40".to_owned());
1199 assert_eq!(encode_component("["), "%5B".to_owned());
1200 assert_eq!(encode_component("]"), "%5D".to_owned());
1201 }
1202
1203 #[test]
1204 fn test_decode() {
1205 assert_eq!(decode(""), "".to_owned());
1206 assert_eq!(decode("abc/def 123"), "abc/def 123".to_owned());
1207 assert_eq!(decode("abc%2Fdef%20123"), "abc%2Fdef 123".to_owned());
1208 assert_eq!(decode("%20"), " ".to_owned());
1209 assert_eq!(decode("%21"), "%21".to_owned());
1210 assert_eq!(decode("%22"), "%22".to_owned());
1211 assert_eq!(decode("%23"), "%23".to_owned());
1212 assert_eq!(decode("%24"), "%24".to_owned());
1213 assert_eq!(decode("%25"), "%".to_owned());
1214 assert_eq!(decode("%26"), "%26".to_owned());
1215 assert_eq!(decode("%27"), "'".to_owned());
1216 assert_eq!(decode("%28"), "%28".to_owned());
1217 assert_eq!(decode("%29"), "%29".to_owned());
1218 assert_eq!(decode("%2A"), "%2A".to_owned());
1219 assert_eq!(decode("%2B"), "%2B".to_owned());
1220 assert_eq!(decode("%2C"), "%2C".to_owned());
1221 assert_eq!(decode("%2F"), "%2F".to_owned());
1222 assert_eq!(decode("%3A"), "%3A".to_owned());
1223 assert_eq!(decode("%3B"), "%3B".to_owned());
1224 assert_eq!(decode("%3D"), "%3D".to_owned());
1225 assert_eq!(decode("%3F"), "%3F".to_owned());
1226 assert_eq!(decode("%40"), "%40".to_owned());
1227 assert_eq!(decode("%5B"), "%5B".to_owned());
1228 assert_eq!(decode("%5D"), "%5D".to_owned());
1229 }
1230
1231 #[test]
1232 fn test_decode_component() {
1233 assert_eq!(decode_component(""), "".to_owned());
1234 assert_eq!(decode_component("abc/def 123"), "abc/def 123".to_owned());
1235 assert_eq!(decode_component("abc%2Fdef%20123"), "abc/def 123".to_owned());
1236 assert_eq!(decode_component("%20"), " ".to_owned());
1237 assert_eq!(decode_component("%21"), "!".to_owned());
1238 assert_eq!(decode_component("%22"), "\"".to_owned());
1239 assert_eq!(decode_component("%23"), "#".to_owned());
1240 assert_eq!(decode_component("%24"), "$".to_owned());
1241 assert_eq!(decode_component("%25"), "%".to_owned());
1242 assert_eq!(decode_component("%26"), "&".to_owned());
1243 assert_eq!(decode_component("%27"), "'".to_owned());
1244 assert_eq!(decode_component("%28"), "(".to_owned());
1245 assert_eq!(decode_component("%29"), ")".to_owned());
1246 assert_eq!(decode_component("%2A"), "*".to_owned());
1247 assert_eq!(decode_component("%2B"), "+".to_owned());
1248 assert_eq!(decode_component("%2C"), ",".to_owned());
1249 assert_eq!(decode_component("%2F"), "/".to_owned());
1250 assert_eq!(decode_component("%3A"), ":".to_owned());
1251 assert_eq!(decode_component("%3B"), ";".to_owned());
1252 assert_eq!(decode_component("%3D"), "=".to_owned());
1253 assert_eq!(decode_component("%3F"), "?".to_owned());
1254 assert_eq!(decode_component("%40"), "@".to_owned());
1255 assert_eq!(decode_component("%5B"), "[".to_owned());
1256 assert_eq!(decode_component("%5D"), "]".to_owned());
1257 }
1258
1259 #[test]
1260 fn test_encode_form_urlencoded() {
1261 let mut m = HashMap::new();
1262 assert_eq!(encode_form_urlencoded(&m), "".to_owned());
1263
1264 m.insert("".to_owned(), vec!());
1265 m.insert("foo".to_owned(), vec!());
1266 assert_eq!(encode_form_urlencoded(&m), "".to_owned());
1267
1268 let mut m = HashMap::new();
1269 m.insert("foo".to_owned(), vec!("bar".to_owned(), "123".to_owned()));
1270 assert_eq!(encode_form_urlencoded(&m), "foo=bar&foo=123".to_owned());
1271
1272 let mut m = HashMap::new();
1273 m.insert("foo bar".to_owned(), vec!("abc".to_owned(), "12 = 34".to_owned()));
1274 assert!(encode_form_urlencoded(&m) ==
1275 "foo+bar=abc&foo+bar=12+%3D+34".to_owned());
1276 }
1277
1278 #[test]
1279 fn test_decode_form_urlencoded() {
1280 assert_eq!(decode_form_urlencoded([]).len(), 0);
1281
1282 let s = "a=1&foo+bar=abc&foo+bar=12+%3D+34".as_bytes();
1283 let form = decode_form_urlencoded(s);
1284 assert_eq!(form.len(), 2);
1285 assert_eq!(form.get(&"a".to_owned()), &vec!("1".to_owned()));
1286 assert_eq!(form.get(&"foo bar".to_owned()), &vec!("abc".to_owned(), "12 = 34".to_owned()));
1287 }
1288 }
liburl/lib.rs:70:23-70:23 -struct- definition:
pub struct Path {
/// The path component of a URL, for example `/foo/bar`.
pub path: ~str,
references:- 20780: Ok(Path{ path: path, query: query, fragment: fragment })
781: }
--
792: impl FromStr for Path {
793: fn from_str(s: &str) -> Option<Path> {
--
868: impl<S: Writer> Hash<S> for Path {
869: fn hash(&self, state: &mut S) {
liburl/lib.rs:202:1-202:1 -fn- definition:
pub fn encode_component(s: &str) -> ~str {
encode_inner(s, false)
}
references:- 4839: Some(ref fragment) => write!(f.buf, "\\#{}",
840: encode_component(*fragment)),
841: None => Ok(()),
--
854: Some(ref fragment) => {
855: write!(f.buf, "\\#{}", encode_component(*fragment))
856: }
liburl/lib.rs:82:32-82:32 -struct- definition:
pub struct UserInfo {
/// The user name.
pub user: ~str,
references:- 2081: /// An optional subcomponent of a URI authority component.
83: pub struct UserInfo {
--
129: pub fn new(user: ~str, pass: Option<~str>) -> UserInfo {
130: UserInfo { user: user, pass: pass }
131: }
--
520: fn get_authority(rawurl: &str) ->
521: Result<(Option<UserInfo>, ~str, Option<~str>, ~str), ~str> {
522: if !rawurl.starts_with("//") {
liburl/lib.rs:396:1-396:1 -fn- definition:
fn split_char_first(s: &str, c: char) -> (~str, ~str) {
let len = s.len();
let mut index = len;
references:- 2721: }
722: let (q, r) = split_char_first(rawurl.slice(1, rawurl.len()), '#');
723: let f = if r.len() != 0 {
liburl/lib.rs:133:1-133:1 -fn- definition:
fn encode_inner(s: &str, full_url: bool) -> ~str {
let mut rdr = BufReader::new(s.as_bytes());
let mut out = StrBuf::new();
references:- 2203: pub fn encode_component(s: &str) -> ~str {
204: encode_inner(s, false)
205: }
liburl/lib.rs:206:1-206:1 -fn- definition:
fn decode_inner(s: &str, full_url: bool) -> ~str {
let mut rdr = BufReader::new(s.as_bytes());
let mut out = StrBuf::new();
references:- 2267: pub fn decode(s: &str) -> ~str {
268: decode_inner(s, true)
269: }
--
274: pub fn decode_component(s: &str) -> ~str {
275: decode_inner(s, false)
276: }
liburl/lib.rs:709:57-709:57 -fn- definition:
// returns the parsed query and the fragment, if present
fn get_query_fragment(rawurl: &str) ->
Result<(Query, Option<~str>), ~str> {
references:- 2759: // query and fragment
760: let (query, fragment) = match get_query_fragment(rest) {
761: Ok(val) => val,
--
774: // query and fragment
775: let (query, fragment) = match get_query_fragment(rest) {
776: Ok(val) => val,
liburl/lib.rs:90:45-90:45 -NK_AS_STR_TODO- definition:
/// Represents the query component of a URI.
pub type Query = Vec<(~str, ~str)>;
impl Url {
references:- 898: path: ~str,
99: query: Query,
100: fragment: Option<~str>)
--
432: fn query_from_str(rawquery: &str) -> Query {
433: let mut query: Query = vec!();
--
710: fn get_query_fragment(rawurl: &str) ->
711: Result<(Query, Option<~str>), ~str> {
712: if !rawurl.starts_with("?") {
liburl/lib.rs:512:23-512:23 -enum- definition:
enum Input {
Digit, // all digits
Hex, // digits and letters a-f
references:- 5513: enum Input {
liburl/lib.rs:678:58-678:58 -fn- definition:
// returns the path and unparsed part of url, or an error
fn get_path(rawurl: &str, authority: bool) ->
Result<(~str, ~str), ~str> {
references:- 2768: pub fn path_from_str(rawpath: &str) -> Result<Path, ~str> {
769: let (path, rest) = match get_path(rawpath, false) {
770: Ok(val) => val,
liburl/lib.rs:51:32-51:32 -struct- definition:
pub struct Url {
/// The scheme part of a URL, such as `https` in the above example.
pub scheme: ~str,
references:- 2150: /// ```
52: pub struct Url {
--
101: -> Url {
102: Url {
103: scheme: scheme,
--
783: impl FromStr for Url {
784: fn from_str(s: &str) -> Option<Url> {
785: match from_str(s) {
--
801: impl fmt::Show for Url {
802: /**
--
862: impl<S: Writer> Hash<S> for Url {
863: fn hash(&self, state: &mut S) {
liburl/lib.rs:273:4-273:4 -fn- definition:
*/
pub fn decode_component(s: &str) -> ~str {
decode_inner(s, false)
references:- 5713: if rawurl.starts_with("#") {
714: let f = decode_component(rawurl.slice(
715: 1,
--
723: let f = if r.len() != 0 {
724: Some(decode_component(r)) } else { None };
725: return Ok((query_from_str(q), f));
liburl/lib.rs:277:1-277:1 -fn- definition:
fn encode_plus(s: &str) -> ~str {
let mut rdr = BufReader::new(s.as_bytes());
let mut out = StrBuf::new();
references:- 2318: out.push_str(format!("{}={}", key, encode_plus(*value)));
319: }