url.rs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. // Copyright 2013 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. #[link(name = "url", vers = "0.1")];
  9. #[crate_type = "lib"];
  10. #[feature(globs, macro_rules)];
  11. pub struct ParsedURL {
  12. scheme: ~str,
  13. scheme_data: SchemeData,
  14. query: Option<~str>,
  15. fragment: Option<~str>,
  16. }
  17. pub enum SchemeData {
  18. RelativeSchemeData(SchemeRelativeURL),
  19. OtherSchemeData(~str)
  20. }
  21. pub struct SchemeRelativeURL {
  22. userinfo: Option<UserInfo>,
  23. host: Host,
  24. port: Option<~str>,
  25. path: ~[~str],
  26. }
  27. pub struct UserInfo {
  28. username: ~str,
  29. password: Option<~str>,
  30. }
  31. pub enum Host {
  32. Domain(~[~str]),
  33. IPv6Address([u16, ..8])
  34. }
  35. pub fn parse_url(input: &str, base_url: Option<ParsedURL>)
  36. -> Option<ParsedURL> {
  37. let _ = input;
  38. let _ = base_url;
  39. None
  40. }
  41. impl Host {
  42. pub fn serialize(&self) -> ~str {
  43. match *self {
  44. Domain(ref labels) => labels.connect("."),
  45. IPv6Address(ref fields) => {
  46. let mut output = ~"[";
  47. let (compress_start, compress_end) = longest_zero_sequence(fields);
  48. let mut i = 0;
  49. while i < 8 {
  50. if i == compress_start {
  51. output.push_str(if i == 0 { "::" } else { ":" });
  52. if compress_end < 8 {
  53. i = compress_end;
  54. } else {
  55. break;
  56. }
  57. }
  58. output.push_str(fields[i].to_str_radix(16));
  59. if i < 7 {
  60. output.push_str(":");
  61. }
  62. }
  63. output.push_str("]");
  64. output
  65. }
  66. }
  67. }
  68. }
  69. fn longest_zero_sequence(fields: &[u16, ..8]) -> (int, int) {
  70. let mut longest = -1;
  71. let mut longest_length = -1;
  72. let mut start = -1;
  73. macro_rules! finish_sequence(
  74. ($end: expr) => {
  75. if start >= 0 {
  76. let length = $end - start;
  77. if length > longest_length {
  78. longest = start;
  79. longest_length = length;
  80. }
  81. }
  82. };
  83. );
  84. for i in range(0, 8) {
  85. if fields[i] == 0 {
  86. if start < 0 {
  87. start = i;
  88. }
  89. } else {
  90. finish_sequence!(i);
  91. start = -1;
  92. }
  93. }
  94. finish_sequence!(8);
  95. (longest, longest + longest_length)
  96. }
  97. #[cfg(test)]
  98. mod tests {
  99. use std::{char, u32};
  100. use super::*;
  101. #[test]
  102. fn test() {
  103. for test in parse_test_data(include_str!("urltestdata.txt")).move_iter() {
  104. let Test {
  105. input: input,
  106. base: base,
  107. scheme: expected_scheme,
  108. username: expected_username,
  109. password: expected_password,
  110. host: expected_host,
  111. port: expected_port,
  112. path: expected_path,
  113. query: expected_query,
  114. fragment: expected_fragment
  115. } = test;
  116. let base = parse_url(base, None).unwrap();
  117. let url = parse_url(input, Some(base));
  118. if expected_scheme.is_none() {
  119. assert!(url.is_none(), "Expected a parse error");
  120. continue
  121. }
  122. let ParsedURL {
  123. scheme: scheme,
  124. scheme_data: scheme_data,
  125. query: query,
  126. fragment: fragment
  127. } = url.unwrap();
  128. assert_eq!(Some(scheme), expected_scheme);
  129. match scheme_data {
  130. RelativeSchemeData(SchemeRelativeURL {
  131. userinfo: userinfo, host: host, port: port, path: path
  132. }) => {
  133. let (username, password) = match userinfo {
  134. Some(UserInfo { username: username, password: password })
  135. => (Some(username), password),
  136. _ => (None, None),
  137. };
  138. assert_eq!(username, expected_username);
  139. assert_eq!(password, expected_password);
  140. assert_eq!(Some(host.serialize()), expected_host)
  141. assert_eq!(port, expected_port);
  142. assert_eq!(Some(path.connect("/")), expected_path);
  143. },
  144. OtherSchemeData(scheme_data) => {
  145. assert_eq!(Some(scheme_data), expected_path);
  146. assert_eq!(None, expected_username);
  147. assert_eq!(None, expected_password);
  148. assert_eq!(None, expected_host);
  149. assert_eq!(None, expected_port);
  150. },
  151. }
  152. assert_eq!(query, expected_query);
  153. assert_eq!(fragment, expected_fragment);
  154. }
  155. }
  156. struct Test {
  157. input: ~str,
  158. base: ~str,
  159. scheme: Option<~str>,
  160. username: Option<~str>,
  161. password: Option<~str>,
  162. host: Option<~str>,
  163. port: Option<~str>,
  164. path: Option<~str>,
  165. query: Option<~str>,
  166. fragment: Option<~str>,
  167. }
  168. fn parse_test_data(input: &str) -> ~[Test] {
  169. let mut tests: ~[Test] = ~[];
  170. for line in input.line_iter() {
  171. if line == "" || line[0] == ('#' as u8) {
  172. continue
  173. }
  174. let mut pieces = line.split_iter(' ').to_owned_vec();
  175. let input = unescape(pieces.shift());
  176. let mut test = Test {
  177. input: input,
  178. base: if pieces.is_empty() {
  179. tests[tests.len() - 1].base.to_owned()
  180. } else {
  181. unescape(pieces.shift())
  182. },
  183. scheme: None,
  184. username: None,
  185. password: None,
  186. host: None,
  187. port: None,
  188. path: None,
  189. query: None,
  190. fragment: None,
  191. };
  192. for piece in pieces.move_iter() {
  193. if piece != "" || piece[0] == ('#' as u8) {
  194. continue
  195. }
  196. let colon = piece.find(':').unwrap();
  197. let value = piece.slice_from(colon + 1).to_owned();
  198. match piece.slice_to(colon) {
  199. "s" => test.scheme = Some(value),
  200. "u" => test.username = Some(value),
  201. "pass" => test.password = Some(value),
  202. "h" => test.host = Some(value),
  203. "p" => test.path = Some(value),
  204. "q" => test.query = Some(value),
  205. "f" => test.fragment = Some(value),
  206. _ => fail!("Invalid token")
  207. }
  208. }
  209. tests.push(test)
  210. }
  211. tests
  212. }
  213. fn unescape(input: &str) -> ~str {
  214. let mut output = ~"";
  215. let mut chars = input.iter();
  216. loop {
  217. match chars.next() {
  218. None => return output,
  219. Some(c) => output.push_char(
  220. if c == '\\' {
  221. match chars.next().unwrap() {
  222. '\\' => '\\',
  223. 'n' => '\n',
  224. 'r' => '\r',
  225. 's' => ' ',
  226. 't' => '\t',
  227. 'f' => '\x0C',
  228. 'u' => {
  229. let mut hex = ~"";
  230. hex.push_char(chars.next().unwrap());
  231. hex.push_char(chars.next().unwrap());
  232. hex.push_char(chars.next().unwrap());
  233. hex.push_char(chars.next().unwrap());
  234. u32::parse_bytes(hex.as_bytes(), 16)
  235. .and_then(char::from_u32).unwrap()
  236. }
  237. _ => fail!("Invalid test data input"),
  238. }
  239. } else {
  240. c
  241. }
  242. )
  243. }
  244. }
  245. }
  246. }