// Copyright 2013-2014 Simon Sapin. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Formatting utilities for URLs. //! //! These formatters can be used to coerce various URL parts into strings. //! //! You can use `.to_string()`, as the formatters implement `Show`. use std::fmt::{mod, Show, Formatter}; use super::Url; /// Formatter and serializer for URL path data. pub struct PathFormatter<'a, T:'a> { /// The path as a slice of string-like objects (String or &str). pub path: &'a [T] } impl<'a, T: Str + Show> Show for PathFormatter<'a, T> { fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { if self.path.is_empty() { formatter.write(b"/") } else { for path_part in self.path.iter() { try!("/".fmt(formatter)); try!(path_part.fmt(formatter)); } Ok(()) } } } /// Formatter and serializer for URL username and password data. pub struct UserInfoFormatter<'a> { /// URL username as a string slice. pub username: &'a str, /// URL password as an optional string slice. /// /// You can convert an `Option` with `.as_ref().map(|s| s.as_slice())`. pub password: Option<&'a str> } impl<'a> Show for UserInfoFormatter<'a> { fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { if !self.username.is_empty() || self.password.is_some() { try!(formatter.write(self.username.as_bytes())); match self.password { None => (), Some(password) => { try!(formatter.write(b":")); try!(formatter.write(password.as_bytes())); } } try!(formatter.write(b"@")); } Ok(()) } } /// Formatter for URLs which ignores the fragment field. pub struct UrlNoFragmentFormatter<'a> { pub url: &'a Url } impl<'a> Show for UrlNoFragmentFormatter<'a> { fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { try!(formatter.write(self.url.scheme.as_bytes())); try!(formatter.write(b":")); try!(self.url.scheme_data.fmt(formatter)); match self.url.query { None => (), Some(ref query) => { try!(formatter.write(b"?")); try!(formatter.write(query.as_bytes())); } } Ok(()) } } /// Formatting Tests #[cfg(test)] mod tests { use super::super::Url; use super::{PathFormatter, UserInfoFormatter}; #[test] fn path_formatting() { let data = [ (vec![], "/"), (vec![""], "/"), (vec!["test", "path"], "/test/path"), (vec!["test", "path", ""], "/test/path/") ]; for &(ref path, result) in data.iter() { assert_eq!(PathFormatter { path: path.as_slice() }.to_string(), result.to_string()); } } #[test] fn userinfo_formatting() { // Test data as (username, password, result) tuples. let data = [ ("", None, ""), ("", Some(""), ":@"), ("", Some("password"), ":password@"), ("username", None, "username@"), ("username", Some(""), "username:@"), ("username", Some("password"), "username:password@") ]; for &(username, password, result) in data.iter() { assert_eq!(UserInfoFormatter { username: username, password: password }.to_string(), result.to_string()); } } #[test] fn relative_scheme_url_formatting() { let data = [ ("http://example.com/", "http://example.com/"), ("http://addslash.com", "http://addslash.com/"), ("http://@emptyuser.com/", "http://emptyuser.com/"), ("http://:@emptypass.com/", "http://:@emptypass.com/"), ("http://user@user.com/", "http://user@user.com/"), ("http://user:pass@userpass.com/", "http://user:pass@userpass.com/"), ("http://slashquery.com/path/?q=something", "http://slashquery.com/path/?q=something"), ("http://noslashquery.com/path?q=something", "http://noslashquery.com/path?q=something") ]; for &(input, result) in data.iter() { let url = Url::parse(input).unwrap(); assert_eq!(url.to_string(), result.to_string()); } } }