|
| 1 | +// Copyright 2013-2014 Simon Sapin. |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 4 | +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 5 | +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| 6 | +// option. This file may not be copied, modified, or distributed |
| 7 | +// except according to those terms. |
| 8 | + |
| 9 | +//! Formatting utilities for URLs. |
| 10 | +//! |
| 11 | +//! These formatters can be used to coerce various URL parts into strings. |
| 12 | +//! |
| 13 | +//! You can use `<formatter>.to_string()`, as the formatters implement `Show`. |
| 14 | +
|
| 15 | +use std::fmt::{Show, Formatter, FormatError}; |
| 16 | +use super::Url; |
| 17 | + |
| 18 | +/// Formatter and serializer for URL path data. |
| 19 | +pub struct PathFormatter<'a, T> { |
| 20 | + /// The path as a slice of string-like objects (String or &str). |
| 21 | + pub path: &'a [T] |
| 22 | +} |
| 23 | + |
| 24 | +impl<'a, T: Str + Show> Show for PathFormatter<'a, T> { |
| 25 | + fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> { |
| 26 | + if self.path.is_empty() { |
| 27 | + formatter.write(b"/") |
| 28 | + } else { |
| 29 | + for path_part in self.path.iter() { |
| 30 | + try!("/".fmt(formatter)); |
| 31 | + try!(path_part.fmt(formatter)); |
| 32 | + } |
| 33 | + Ok(()) |
| 34 | + } |
| 35 | + } |
| 36 | +} |
| 37 | + |
| 38 | + |
| 39 | +/// Formatter and serializer for URL username and password data. |
| 40 | +pub struct UserInfoFormatter<'a> { |
| 41 | + /// URL username as a string slice. |
| 42 | + pub username: &'a str, |
| 43 | + |
| 44 | + /// URL password as an optional string slice. |
| 45 | + /// |
| 46 | + /// You can convert an `Option<String>` with `.as_ref().map(|s| s.as_slice())`. |
| 47 | + pub password: Option<&'a str> |
| 48 | +} |
| 49 | + |
| 50 | +impl<'a> Show for UserInfoFormatter<'a> { |
| 51 | + fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> { |
| 52 | + if !self.username.is_empty() || self.password.is_some() { |
| 53 | + try!(formatter.write(self.username.as_bytes())); |
| 54 | + match self.password { |
| 55 | + None => (), |
| 56 | + Some(password) => { |
| 57 | + try!(formatter.write(b":")); |
| 58 | + try!(formatter.write(password.as_bytes())); |
| 59 | + } |
| 60 | + } |
| 61 | + try!(formatter.write(b"@")); |
| 62 | + } |
| 63 | + Ok(()) |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | + |
| 68 | +/// Formatter for URLs which ignores the fragment field. |
| 69 | +pub struct UrlNoFragmentFormatter<'a> { |
| 70 | + pub url: &'a Url |
| 71 | +} |
| 72 | + |
| 73 | +impl<'a> Show for UrlNoFragmentFormatter<'a> { |
| 74 | + fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> { |
| 75 | + try!(formatter.write(self.url.scheme.as_bytes())); |
| 76 | + try!(formatter.write(b":")); |
| 77 | + try!(self.url.scheme_data.fmt(formatter)); |
| 78 | + match self.url.query { |
| 79 | + None => (), |
| 80 | + Some(ref query) => { |
| 81 | + try!(formatter.write(b"?")); |
| 82 | + try!(formatter.write(query.as_bytes())); |
| 83 | + } |
| 84 | + } |
| 85 | + Ok(()) |
| 86 | + } |
| 87 | +} |
| 88 | + |
| 89 | + |
| 90 | +/// Formatting Tests |
| 91 | +#[cfg(test)] |
| 92 | +mod tests { |
| 93 | + use super::super::Url; |
| 94 | + use super::{PathFormatter, UserInfoFormatter}; |
| 95 | + |
| 96 | + #[test] |
| 97 | + fn path_formatting() { |
| 98 | + let data = [ |
| 99 | + (vec![], "/"), |
| 100 | + (vec![""], "/"), |
| 101 | + (vec!["test", "path"], "/test/path"), |
| 102 | + (vec!["test", "path", ""], "/test/path/") |
| 103 | + ]; |
| 104 | + for &(ref path, result) in data.iter() { |
| 105 | + assert_eq!(PathFormatter { |
| 106 | + path: path.as_slice() |
| 107 | + }.to_string(), result.to_string()); |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + #[test] |
| 112 | + fn userinfo_formatting() { |
| 113 | + // Test data as (username, password, result) tuples. |
| 114 | + let data = [ |
| 115 | + ("", None, ""), |
| 116 | + ("", Some(""), ":@"), |
| 117 | + ("", Some("password"), ":password@"), |
| 118 | + ("username", None, "username@"), |
| 119 | + ("username", Some(""), "username:@"), |
| 120 | + ("username", Some("password"), "username:password@") |
| 121 | + ]; |
| 122 | + for &(username, password, result) in data.iter() { |
| 123 | + assert_eq!(UserInfoFormatter { |
| 124 | + username: username, |
| 125 | + password: password |
| 126 | + }.to_string(), result.to_string()); |
| 127 | + } |
| 128 | + } |
| 129 | + |
| 130 | + #[test] |
| 131 | + fn relative_scheme_url_formatting() { |
| 132 | + let data = [ |
| 133 | + ("http://example.com/", "http://example.com/"), |
| 134 | + ("http://addslash.com", "http://addslash.com/"), |
| 135 | + ("http://@emptyuser.com/", "http://emptyuser.com/"), |
| 136 | + ("http://:@emptypass.com/", "http://:@emptypass.com/"), |
| 137 | + |
| 138 | + ("http://user:[email protected]/", "http://user:[email protected]/"), |
| 139 | + ("http://slashquery.com/path/?q=something", "http://slashquery.com/path/?q=something"), |
| 140 | + ("http://noslashquery.com/path?q=something", "http://noslashquery.com/path?q=something") |
| 141 | + ]; |
| 142 | + for &(input, result) in data.iter() { |
| 143 | + let url = Url::parse(input).unwrap(); |
| 144 | + assert_eq!(url.to_string(), result.to_string()); |
| 145 | + } |
| 146 | + } |
| 147 | +} |
0 commit comments