1use std::collections::HashMap;
22use url_search_params;
23use url_search_params::{build_url_search_params, parse_url_search_params};
24
25#[derive(PartialEq, Eq, Clone, Debug)]
26pub struct UrlComponents {
27 pub scheme: String,
28 pub authority: Option<UrlAuthority>,
29 pub path: String,
30 pub query: Option<HashMap<String, String>>,
31 pub fragment: Option<String>
32}
33#[derive(PartialEq, Eq, Clone, Debug)]
34pub struct UrlAuthority {
35 pub user_info: Option<UrlUserInfo>,
36 pub host: String,
37 pub port: Option<usize>
38}
39#[derive(PartialEq, Eq, Clone, Debug)]
40pub struct UrlUserInfo {
41 pub username: String,
42 pub password: Option<String>
43}
44
45
46impl UrlComponents {
47 pub fn new() -> UrlComponents {
48 let url_components = UrlComponents {
49 scheme: "".to_string(),
50 authority: None,
51 path: "".to_string(),
52 query: None,
53 fragment: None };
54 url_components
55 }
56}
57
58
59pub fn parse_url(url: &str) -> Result<UrlComponents, String> {
97 let mut url_components = UrlComponents::new();
98
99 let boxed_scheme = extract_scheme(url);
100 if boxed_scheme.is_err() {
101 return Err(boxed_scheme.err().unwrap());
102 }
103
104 let (scheme, _remaining_url) = boxed_scheme.unwrap();
105 url_components.scheme = scheme;
106 let mut remaining_url = _remaining_url;
107
108
109 let boxed_authority = extract_authority(remaining_url.as_str());
110 if boxed_authority.is_err() {
111 return Err(boxed_authority.err().unwrap());
112 }
113
114 let (authority_string, boxed_remaining_url) = boxed_authority.unwrap();
115
116 if authority_string.is_some() {
117 let boxed_authority = parse_authority(authority_string.unwrap().as_str());
118 if boxed_authority.is_err() {
119 return Err(boxed_authority.err().unwrap());
120 }
121
122 let (boxed_username, boxed_password, host, boxed_port) = boxed_authority.unwrap();
123
124
125 let mut authority = UrlAuthority {
126 user_info: None,
127 host,
128 port: None
129 };
130
131 let mut user_info : Option<UrlUserInfo> = None;
132
133 if boxed_username.is_some() {
134 let username = boxed_username.unwrap();
135 if user_info.is_none() {
136 let mut password : Option<String> = None;
137 if boxed_password.is_some() {
138 password = boxed_password;
139 }
140 user_info = Some(UrlUserInfo { username: username.to_string(), password });
141 }
142
143 authority.user_info = user_info;
144 }
145
146
147 if boxed_port.is_some() {
148 let port = boxed_port;
149 authority.port = port;
150 }
151
152 url_components.authority = Option::from(authority);
153 }
154
155
156
157 if boxed_remaining_url.is_none() {
158 return Ok(url_components)
159 }
160 remaining_url = boxed_remaining_url.unwrap();
161
162 let boxed_path = extract_path(remaining_url.as_str());
163 if boxed_path.is_err() {
164 return Err(boxed_path.err().unwrap());
165 }
166 let (_path, _remaining_url) = boxed_path.unwrap();
167
168 url_components.path = _path;
169 if _remaining_url.is_none() {
170 return Ok(url_components)
171 }
172 remaining_url = _remaining_url.unwrap();
173
174
175 let (boxed_query, _remaining_url) = extract_query(remaining_url.as_str());
176 if boxed_query.is_some() {
177 let query = boxed_query.unwrap();
178 let parsed_query = parse_query(query.as_str()).unwrap();
179 let params: HashMap<String, String> = parse_url_search_params(parsed_query.as_str());
180 url_components.query = Some(params);
181 if _remaining_url.is_none() {
182 return Ok(url_components)
183 }
184 remaining_url = _remaining_url.unwrap();
185 }
186
187 let boxed_fragment = extract_fragment(remaining_url.as_str());
188 if boxed_fragment.is_err() {
189 return Err(boxed_fragment.err().unwrap());
190 }
191
192 let fragment = parse_fragment(boxed_fragment.unwrap().as_str()).unwrap();
193 url_components.fragment = Option::from(fragment);
194
195 Ok(url_components)
196}
197
198pub fn build_url(url_components: UrlComponents) -> Result<String, String> {
219 let mut url = "".to_string();
220
221 if url_components.fragment.is_some() {
222 url = ["#".to_string(), url_components.fragment.unwrap(), url].join("");
223 }
224
225 if url_components.query.is_some() {
226 let query = build_url_search_params(url_components.query.unwrap());
227 url = ["?".to_string(), query, url].join("");
228 }
229
230 url = [url_components.path, url].join("");
231
232 if url_components.authority.is_some() {
233 let authority = build_authority(url_components.authority.unwrap());
234 url = ["//".to_string(), authority, url].join("");
235 }
236
237 url = [url_components.scheme, ":".to_string(), url].join("");
238
239 Ok(url)
240}
241
242pub(crate) fn build_authority(url_authority: UrlAuthority) -> String {
243 let mut authority = "".to_string();
244
245 if url_authority.user_info.is_some() {
246 let url_user_info = url_authority.user_info.unwrap();
247 authority = url_user_info.username;
248 if url_user_info.password.is_some() {
249 authority = [authority, ":".to_string(), url_user_info.password.unwrap()].join("");
250 }
251 }
252
253 if authority.chars().count() != 0 {
254 authority = [authority, "@".to_string(), url_authority.host].join("");
255 } else {
256 authority = [authority, url_authority.host].join("");
257 }
258
259
260
261 if url_authority.port.is_some() {
262 authority = [authority, ":".to_string(), url_authority.port.unwrap().to_string()].join("");
263 }
264
265 authority
266}
267
268pub(crate) fn extract_scheme(url: &str) -> Result<(String, String), String> {
269 let boxed_split_at_path = url.split_once(":");
270 if boxed_split_at_path.is_some() {
271 let (scheme, remaining_url) = boxed_split_at_path.unwrap();
272 Ok((scheme.to_string(), remaining_url.to_string()))
273 } else {
274 Err("unable to identify scheme".to_string())
275 }
276}
277
278pub(crate) fn extract_authority(mut url: &str) -> Result<(Option<String>, Option<String>), String> {
279 if url.chars().count() == 0 {
280 let error_message = "error: remaining url is empty";
281 return Err(error_message.to_string())
282 }
283
284 if !url.contains("//") {
285 return Ok((None, Option::from(url.to_string())))
286 }
287
288 let (_, _remaining_url) = url.split_once("//").unwrap();
289 url = _remaining_url;
290
291 let is_there_a_slash = url.contains("/");
292 let is_there_a_question_mark = url.contains("?");
293 let is_there_a_hash = url.contains("#");
294
295 if !is_there_a_slash && !is_there_a_question_mark && !is_there_a_hash {
296 return Ok((Option::from(url.to_string()), None))
297 }
298
299 if is_there_a_slash {
300 let boxed_split = url.split_once("/");
301 if boxed_split.is_some() {
302 let (authority, remaining_url) = boxed_split.unwrap();
303 let remaining_url = ["/", remaining_url].join("");
304 let authority_option = Option::from(authority.to_string());
305 let remaining_url = Option::from(remaining_url.to_string());
306 return Ok((authority_option, remaining_url))
307 }
308 }
309
310 if !is_there_a_slash && is_there_a_question_mark {
311 let boxed_split = url.split_once("?");
312 if boxed_split.is_some() {
313 let (authority, remaining_url) = boxed_split.unwrap();
314 let authority_option = Option::from(authority.to_string());
315 let remaining_url = ["?", remaining_url].join("");
316 let remaining_url = Option::from(remaining_url.to_string());
317 return Ok((authority_option, remaining_url))
318 }
319 }
320
321 if !is_there_a_slash && !is_there_a_question_mark && is_there_a_hash {
322 let boxed_split = url.split_once("#");
323 if boxed_split.is_some() {
324 let (authority, remaining_url) = boxed_split.unwrap();
325 let remaining_url = ["#", remaining_url].join("");
326 let authority_option = Option::from(authority.to_string());
327 let remaining_url = Option::from(remaining_url.to_string());
328 return Ok((authority_option, remaining_url))
329 }
330 }
331
332 let error_message = ["error: something went wrong with remaining url ", url].join("");
333 Err(error_message.to_string())
334
335}
336
337pub(crate) fn extract_path(url: &str) -> Result<(String, Option<String>), String> {
338 if url.chars().count() == 0 {
339 let error_message = "error: remaining url is empty";
340 return Err(error_message.to_string())
341 }
342
343 let is_there_a_question_mark = url.contains("?");
344 let is_there_a_hash = url.contains("#");
345
346 if !is_there_a_question_mark && !is_there_a_hash {
347 return Ok((url.to_string(), None));
348 }
349
350 let mut delimiter = "?";
351 if !is_there_a_question_mark && is_there_a_hash {
352 delimiter = "#";
353 }
354
355 let boxed_split = url.split_once(&delimiter);
356 if boxed_split.is_some() {
357 let (_path, _rest) = boxed_split.unwrap();
358 let path = _path.to_string();
359 let remaining_url: String =
360 [delimiter.to_string(), _rest.to_string()].join("");
361
362 return Ok((path.to_string(), Option::from(remaining_url)));
363 }
364
365
366 let error_message = ["error: something went wrong with remaining url ", url].join("");
367 Err(error_message.to_string())
368
369}
370
371pub(crate) fn extract_query(url: &str) ->
372 (Option<String>, Option<String>) {
373 if url.chars().count() == 0 {
374 return (None, None);
375 }
376
377 let is_there_a_hash = url.contains("#");
378
379 if is_there_a_hash {
380 let (query, rest) = url.split_once("#").unwrap();
381 let rest = ["#".to_string(), rest.to_string()].join("");
382 let mut query_option : Option<String> = None;
383
384 if query.chars().count() != 0 {
385 query_option = Some(query.to_string());
386 }
387
388 (query_option, Option::from(rest.to_string()))
389 } else {
390 (Option::from(url.to_string()), None)
391 }
392
393}
394
395pub(crate) fn extract_fragment(url: &str) -> Result<String, String> {
396 if url.chars().count() == 0 {
397 let error_message = "error: remaining url is empty";
398 return Err(error_message.to_string())
399 }
400
401 let is_there_a_hash = url.contains("#");
402
403 if !is_there_a_hash {
404 let error_message = ["error: fragment is not defined url: ", url].join("");
405 return Err(error_message.to_string())
406 }
407
408 let (_, fragment) = url.split_once("#").unwrap();
409
410 let fragment = ["#".to_string(), fragment.to_string()].join("");
411 Ok(fragment.to_string())
412
413}
414
415pub(crate) fn parse_query(query_with_question_mark: &str) -> Result<String, String> {
416 let (_, query) = query_with_question_mark.split_once("?").unwrap();
417
418 Ok(query.to_string())
419}
420
421pub(crate) fn parse_fragment(url: &str) -> Result<String, String> {
422 let (_, fragment) = url.split_once("#").unwrap();
423
424 Ok(fragment.to_string())
425}
426
427pub(crate) fn parse_authority(authority: &str)
428 -> Result<
429 (
430 Option<String>,
431 Option<String>,
432 String,
433 Option<usize>
434 ), String> {
435 let mut port : Option<usize> = None;
436
437 let mut remaining_authority = authority.to_string();
438
439 let boxed_userinfo = extract_userinfo(remaining_authority.as_str());
440 let (username, password, _remaining_authority) = boxed_userinfo.unwrap();
441 remaining_authority = _remaining_authority;
442
443 let boxed_host = extract_host(remaining_authority.as_str());
444 let (host, _remaining_authority) = boxed_host.unwrap();
445
446 if _remaining_authority.is_some() {
447 let boxed_port = extract_port(_remaining_authority.unwrap().as_str());
448 port = boxed_port.unwrap();
449 }
450
451 Ok((username, password, host, port))
452}
453
454pub(crate) fn extract_userinfo(authority: &str) -> Result<(Option<String>, Option<String>, String), String> {
455 let mut username : Option<String> = None;
456 let mut password : Option<String> = None;
457
458
459 let mut remaining_authority = authority.to_string();
460
461 let is_there_an_at_symbol = authority.contains("@");
462 if is_there_an_at_symbol {
463 let (userinfo, _remaining_authority) = authority.split_once("@").unwrap();
464 remaining_authority = _remaining_authority.to_string();
465 let is_there_a_colon = userinfo.contains(":");
466 if is_there_a_colon {
467 let (_username, _password) = userinfo.split_once(":").unwrap();
468 username = Some(_username.to_string());
469 password = Some(_password.to_string());
470 } else {
471 let _username = userinfo.to_string();
472 username = Some(_username);
473 }
474 }
475
476 Ok((username, password, remaining_authority))
477}
478
479pub(crate) fn extract_host(authority: &str) -> Result<(String, Option<String>), String> {
480 let mut host : String = authority.to_string();
481 let mut remaining_authority: Option<String> = None;
482
483 let is_it_an_ip_v6_url = authority.contains("]");
484 if is_it_an_ip_v6_url {
485 let (_host, _remaining_authority) = authority.split_once("]").unwrap();
486 host = [_host, "]"].join("");
487 let it_contains_port = _remaining_authority.contains(":");
488 if it_contains_port {
489 remaining_authority = Option::from(_remaining_authority.to_string());
490 }
491 } else {
492 let it_contains_port = authority.contains(":");
493 if it_contains_port {
494 let (_host, _remaining_authority) = authority.split_once(":").unwrap();
495 host = _host.to_string();
496 remaining_authority = Option::from([":", _remaining_authority].join(""));
497 }
498 }
499
500 Ok((host, remaining_authority))
501}
502
503pub(crate) fn extract_port(authority: &str) -> Result<Option<usize>, String> {
504 let mut port: Option<usize> = None;
505
506 let is_there_a_colon = authority.contains(":");
507 if is_there_a_colon {
508 let (_, port_as_string) = authority.split_once(":").unwrap();
509
510 let boxed_port = port_as_string.parse::<usize>();
511 if boxed_port.is_err() {
512 let msg = [
513 "unable to parse port from remaining authority ".to_string(),
514 " | ".to_string(),
515 boxed_port.err().unwrap().to_string(),
516 " | ".to_string(),
517 port_as_string.to_string()].join("");
518 return Err(msg)
519 }
520
521 port = Some(boxed_port.unwrap());
522 }
523
524 Ok(port)
525}
526
527
528
529
530#[cfg(test)]
531mod tests {
532 use std::collections::HashMap;
533 use crate::{build_authority, build_url, extract_authority, extract_fragment, extract_host, extract_path, extract_port, extract_query, extract_scheme, extract_userinfo, parse_authority, parse_url, UrlAuthority, UrlComponents, UrlUserInfo};
534
535 #[test]
536 fn extract_scheme_test_no_delimiter() {
537 let url = "schemewithoutdelimiter";
538 let boxed_result = extract_scheme(url);
539
540 assert!(boxed_result.is_err());
541 assert_eq!("unable to identify scheme", boxed_result.err().unwrap());
542 }
543
544 #[test]
545 fn extract_scheme_test() {
546 let url = "https://example.com";
547 let boxed_result = extract_scheme(url);
548 let (scheme, remaining_url) = boxed_result.unwrap();
549
550 assert_eq!("https", scheme);
551 assert_eq!("//example.com", remaining_url);
552 }
553
554 #[test]
555 fn extract_authority_test_no_authority() {
556 let remaining_url = "/path?q=qwerty";
557 let boxed_result = extract_authority(remaining_url);
558 let (authority, remaining_url) = boxed_result.unwrap();
559
560 assert_eq!(None, authority);
561 assert_eq!("/path?q=qwerty", remaining_url.unwrap());
562 }
563
564 #[test]
565 fn extract_authority_test_no_authority_no_slash() {
566 let remaining_url = "path?q=qwerty";
567 let boxed_result = extract_authority(remaining_url);
568 let (authority, remaining_url) = boxed_result.unwrap();
569
570 assert_eq!(None, authority);
571 assert_eq!("path?q=qwerty", remaining_url.unwrap());
572 }
573
574 #[test]
575 fn extract_authority_test() {
576 let remaining_url = "//example.com";
577 let boxed_result = extract_authority(remaining_url);
578 let (authority, remaining_url) = boxed_result.unwrap();
579
580 assert_eq!("example.com", authority.unwrap());
581 assert_eq!(None, remaining_url);
582 }
583
584 #[test]
585 fn extract_authority_path_defined_query_defined_fragment_defined() {
586 let remaining_url = "//example.com/some-path?q=test#123";
587 let boxed_result = extract_authority(remaining_url);
588 let (authority, remaining_url) = boxed_result.unwrap();
589
590 assert_eq!("example.com", authority.unwrap());
591 assert_eq!("/some-path?q=test#123", remaining_url.unwrap());
592 }
593
594 #[test]
595 fn extract_authority_path_defined_as_slash_query_defined_fragment_defined() {
596 let remaining_url = "//user:passwd@example.com:443/?q=test#123";
597 let boxed_result = extract_authority(remaining_url);
598 let (authority, remaining_url) = boxed_result.unwrap();
599
600 assert_eq!("user:passwd@example.com:443", authority.unwrap());
601 assert_eq!("/?q=test#123", remaining_url.unwrap());
602 }
603
604 #[test]
605 fn extract_authority_path_undefined_query_defined_fragment_defined() {
606 let remaining_url = "//user:passwd@example.com?q=test#123";
607 let boxed_result = extract_authority(remaining_url);
608 let (authority, remaining_url) = boxed_result.unwrap();
609
610 assert_eq!("user:passwd@example.com", authority.unwrap());
611 assert_eq!("?q=test#123", remaining_url.unwrap());
612 }
613
614 #[test]
615 fn extract_authority_path_undefined_query_undefined_fragment_defined() {
616 let remaining_url = "//example.com:80#123";
617 let boxed_result = extract_authority(remaining_url);
618 let (authority, remaining_url) = boxed_result.unwrap();
619
620 assert_eq!("example.com:80", authority.unwrap());
621 assert_eq!("#123", remaining_url.unwrap());
622 }
623
624 #[test]
625 fn extract_authority_path_defined_query_undefined_fragment_defined() {
626 let remaining_url = "//example.com/some-path#123";
627 let boxed_result = extract_authority(remaining_url);
628 let (authority, remaining_url) = boxed_result.unwrap();
629
630 assert_eq!("example.com", authority.unwrap());
631 assert_eq!("/some-path#123", remaining_url.unwrap());
632 }
633
634 #[test]
635 fn extract_authority_undefined_path_zero_length_query_undefined_fragment_undefined() {
636 let remaining_url = "";
637 let boxed_result = extract_authority(remaining_url);
638 assert!(boxed_result.is_err());
639 assert_eq!("error: remaining url is empty", boxed_result.err().unwrap());
640 }
641
642 #[test]
643 fn extract_authority_defined_path_zero_length_query_undefined_fragment_undefined() {
644 let remaining_url = "//usr:pwd@host:443";
645 let boxed_result = extract_authority(remaining_url);
646
647 let (authority, remaining_url) = boxed_result.unwrap();
648
649 assert_eq!("usr:pwd@host:443", authority.unwrap());
650 assert_eq!(None, remaining_url);
651 }
652
653 #[test]
654 fn extract_path_path_defined_query_undefined_fragment_defined() {
655 let remaining_url = "/some-path#123";
656 let boxed_result = extract_path(remaining_url);
657 let (path, remaining_url) = boxed_result.unwrap();
658
659 assert_eq!("/some-path", path);
660 assert_eq!("#123", remaining_url.unwrap());
661 }
662
663 #[test]
664 fn extract_path_path_defined_query_defined_fragment_defined() {
665 let remaining_url = "/some-path?q=query#123";
666 let boxed_result = extract_path(remaining_url);
667 let (path, remaining_url) = boxed_result.unwrap();
668
669 assert_eq!("/some-path", path);
670 assert_eq!("?q=query#123", remaining_url.unwrap());
671 }
672
673 #[test]
674 fn extract_path_path_defined_query_defined_fragment_undefined() {
675 let remaining_url = "/some-path?q=query";
676 let boxed_result = extract_path(remaining_url);
677 let (path, remaining_url) = boxed_result.unwrap();
678
679 assert_eq!("/some-path", path);
680 assert_eq!("?q=query", remaining_url.unwrap());
681 }
682
683 #[test]
684 fn extract_path_path_defined_as_slash_query_defined_fragment_undefined() {
685 let remaining_url = "/?q=query";
686 let boxed_result = extract_path(remaining_url);
687 let (path, remaining_url) = boxed_result.unwrap();
688
689 assert_eq!("/", path);
690 assert_eq!("?q=query", remaining_url.unwrap());
691 }
692
693 #[test]
694 fn extract_path_path_zero_length_query_defined_fragment_defined() {
695 let remaining_url = "?q=query#fragment";
696 let boxed_result = extract_path(remaining_url);
697 let (path, remaining_url) = boxed_result.unwrap();
698
699 assert_eq!("", path);
700 assert_eq!("?q=query#fragment", remaining_url.unwrap());
701 }
702
703 #[test]
704 fn extract_path_path_zero_length_query_undefined_fragment_defined() {
705 let remaining_url = "#fragment";
706 let boxed_result = extract_path(remaining_url);
707 let (path, remaining_url) = boxed_result.unwrap();
708
709 assert_eq!("", path);
710 assert_eq!("#fragment", remaining_url.unwrap());
711 }
712
713 #[test]
714 fn extract_path_path_zero_length_query_defined_fragment_undefined() {
715 let remaining_url = "?q=query";
716 let boxed_result = extract_path(remaining_url);
717 let (path, remaining_url) = boxed_result.unwrap();
718
719 assert_eq!("", path);
720 assert_eq!("?q=query", remaining_url.unwrap());
721 }
722
723 #[test]
724 fn extract_path_path_zero_length_query_undefined_fragment_undefined() {
725 let remaining_url = "";
726 let boxed_result = extract_path(remaining_url);
727 assert!(boxed_result.is_err());
728 assert_eq!("error: remaining url is empty", boxed_result.err().unwrap());
729 }
730
731 #[test]
732 fn extract_query_empty_remaining_url() {
733 let remaining_url = "";
734 let (boxed_query, _remaining_url) = extract_query(remaining_url);
735 assert!(boxed_query.is_none());
736 }
737
738 #[test]
739 fn extract_query_query_undefined() {
740 let remaining_url = "#qweqwe";
741 let (query, remaining_url) = extract_query(remaining_url);
742
743 assert!(query.is_none());
744 assert_eq!("#qweqwe", remaining_url.unwrap());
745 }
746
747 #[test]
748 fn extract_query_query_defined_fragment_undefined() {
749 let remaining_url = "?q=query";
750 let (query, _remaining_url) = extract_query(remaining_url);
751
752 assert_eq!("?q=query", query.unwrap());
753 assert_eq!(None, _remaining_url);
754 }
755
756 #[test]
757 fn extract_query_query_defined_fragment_defined() {
758 let remaining_url = "?q=query#fragment1";
759 let (query, remaining_url) = extract_query(remaining_url);
760
761 assert_eq!("?q=query", query.unwrap());
762 assert_eq!("#fragment1", remaining_url.unwrap());
763 }
764
765 #[test]
766 fn extract_fragment_undefined() {
767 let remaining_url = "gment1";
768 let boxed_result = extract_fragment(remaining_url);
769 assert!(boxed_result.is_err());
770 assert_eq!("error: fragment is not defined url: gment1", boxed_result.err().unwrap());
771 }
772
773 #[test]
774 fn extract_fragment_undefined_empty() {
775 let remaining_url = "";
776 let boxed_result = extract_fragment(remaining_url);
777 assert!(boxed_result.is_err());
778 assert_eq!("error: remaining url is empty", boxed_result.err().unwrap());
779 }
780
781 #[test]
782 fn extract_fragment_defined() {
783 let remaining_url = "#test";
784 let boxed_result = extract_fragment(remaining_url);
785 assert!(boxed_result.is_ok());
786 assert_eq!("#test", boxed_result.unwrap());
787 }
788
789 #[test]
790 fn parse_authority_parts() {
791 let authority = "usr:pwd@somehost:80";
792 let boxed_result = parse_authority(authority);
793
794
795 assert!(boxed_result.is_ok());
796 let (boxed_username, boxed_password, host, boxed_port) = boxed_result.unwrap();
797
798 assert!(boxed_username.is_some());
799 assert_eq!("usr", boxed_username.unwrap());
800
801 assert!(boxed_password.is_some());
802 assert_eq!("pwd", boxed_password.unwrap());
803
804 assert_eq!("somehost", host);
805
806 assert!(boxed_port.is_some());
807 assert_eq!(80, boxed_port.unwrap());
808 }
809
810 #[test]
811 fn parse_authority_parts_no_password() {
812 let authority = "usr@somehost:80";
813 let boxed_result = parse_authority(authority);
814
815
816 assert!(boxed_result.is_ok());
817 let (boxed_username, boxed_password, host, boxed_port) = boxed_result.unwrap();
818
819 assert!(boxed_username.is_some());
820 assert_eq!("usr", boxed_username.unwrap());
821
822 assert!(boxed_password.is_none());
823
824 assert_eq!("somehost", host);
825
826 assert!(boxed_port.is_some());
827 assert_eq!(80, boxed_port.unwrap());
828 }
829
830 #[test]
831 fn parse_authority_parts_no_user_no_password() {
832 let authority = "somehost:80";
833 let boxed_result = parse_authority(authority);
834
835
836 assert!(boxed_result.is_ok());
837 let (boxed_username, boxed_password, host, boxed_port) = boxed_result.unwrap();
838
839 assert!(boxed_username.is_none());
840 assert!(boxed_password.is_none());
841
842 assert_eq!("somehost", host);
843
844 assert!(boxed_port.is_some());
845 assert_eq!(80, boxed_port.unwrap());
846 }
847
848 #[test]
849 fn parse_authority_parts_no_user_no_password_no_port() {
850 let authority = "somehost";
851 let boxed_result = parse_authority(authority);
852
853
854 assert!(boxed_result.is_ok());
855 let (boxed_username, boxed_password, host, boxed_port) = boxed_result.unwrap();
856
857 assert!(boxed_username.is_none());
858 assert!(boxed_password.is_none());
859
860 assert_eq!("somehost", host);
861
862 assert!(boxed_port.is_none());
863 }
864
865 #[test]
866 fn parse_authority_parts_no_password_no_port() {
867 let authority = "usr@somehost";
868 let boxed_result = parse_authority(authority);
869
870
871 assert!(boxed_result.is_ok());
872 let (boxed_username, boxed_password, host, boxed_port) = boxed_result.unwrap();
873
874 assert!(boxed_username.is_some());
875 assert_eq!("usr", boxed_username.unwrap());
876 assert!(boxed_password.is_none());
877
878 assert_eq!("somehost", host);
879
880 assert!(boxed_port.is_none());
881 }
882
883
884 #[test]
885 fn parse_authority_parts_no_port() {
886 let authority = "usr:pwd@somehost";
887 let boxed_result = parse_authority(authority);
888
889
890 assert!(boxed_result.is_ok());
891 let (boxed_username, boxed_password, host, boxed_port) = boxed_result.unwrap();
892
893 assert!(boxed_username.is_some());
894 assert_eq!("usr", boxed_username.unwrap());
895
896
897 assert!(boxed_password.is_some());
898 assert_eq!("pwd", boxed_password.unwrap());
899
900 assert_eq!("somehost", host);
901
902 assert!(boxed_port.is_none());
903 }
904
905 #[test]
906 fn parse_extract_userinfo() {
907 let boxed_userinfo =
908 extract_userinfo(
909 "usr:pwd@[2001:0db8:85a3:0000:0000:8a2e:0370:7334]");
910 assert!(boxed_userinfo.is_ok());
911
912 let (username, password, remaining_authority) = boxed_userinfo.unwrap();
913
914 assert_eq!("usr", username.unwrap());
915 assert_eq!("pwd", password.unwrap());
916 assert_eq!("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", remaining_authority);
917 }
918
919
920 #[test]
921 fn parse_extract_userinfo_no_passwd() {
922 let boxed_userinfo =
923 extract_userinfo(
924 "usr@192.168.0.1");
925 assert!(boxed_userinfo.is_ok());
926
927 let (username, password, remaining_authority) = boxed_userinfo.unwrap();
928
929 assert_eq!("usr", username.unwrap());
930 assert_eq!(None, password);
931 assert_eq!("192.168.0.1", remaining_authority);
932 }
933
934 #[test]
935 fn parse_extract_userinfo_no_passwd_no_user() {
936 let boxed_userinfo =
937 extract_userinfo(
938 "somehost.com");
939 assert!(boxed_userinfo.is_ok());
940
941 let (username, password, remaining_authority) = boxed_userinfo.unwrap();
942
943 assert_eq!(None, username);
944 assert_eq!(None, password);
945 assert_eq!("somehost.com", remaining_authority);
946 }
947
948 #[test]
949 fn parse_extract_host_ip_v4() {
950 let (host, remaining_authority) =
951 extract_host("somehost.com:80".as_ref()).unwrap();
952
953 assert_eq!("somehost.com", host);
954 assert_eq!(":80", remaining_authority.unwrap());
955 }
956
957 #[test]
958 fn parse_extract_host_ip_v4_no_port() {
959 let (host, remaining_authority) =
960 extract_host("somehost.com".as_ref()).unwrap();
961
962 assert_eq!("somehost.com", host);
963 assert_eq!(None, remaining_authority);
964 }
965
966
967 #[test]
968 fn parse_extract_host_ip_v6() {
969 let (host, remaining_authority) =
970 extract_host("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80".as_ref()).unwrap();
971
972 assert_eq!("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", host);
973 assert_eq!(":80", remaining_authority.unwrap());
974 }
975
976 #[test]
977 fn parse_extract_host_ip_v6_no_port() {
978 let (host, remaining_authority) =
979 extract_host("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]".as_ref()).unwrap();
980
981 assert_eq!("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", host);
982 assert_eq!(None, remaining_authority);
983 }
984
985 #[test]
986 fn parse_authority_parts_ip_v6() {
987 let authority = "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]";
988 let boxed_result = parse_authority(authority);
989
990
991 assert!(boxed_result.is_ok());
992
993 let (boxed_username, boxed_password, host, boxed_port) = boxed_result.unwrap();
994
995 assert!(boxed_username.is_none());
996
997
998 assert!(boxed_password.is_none());
999
1000 assert_eq!("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", host);
1001
1002 assert!(boxed_port.is_none());
1003 }
1004
1005 #[test]
1006 fn parse_authority_parts_usr_pwd_ip_v6_port() {
1007 let authority = "usr:pwd@[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80";
1008 let boxed_result = parse_authority(authority);
1009
1010
1011 assert!(boxed_result.is_ok());
1012 let (boxed_username, boxed_password, host, boxed_port) = boxed_result.unwrap();
1013
1014 assert!(boxed_username.is_some());
1015 assert_eq!("usr", boxed_username.unwrap());
1016
1017 assert!(boxed_password.is_some());
1018 assert_eq!("pwd", boxed_password.unwrap());
1019
1020 assert_eq!("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", host);
1021
1022 assert!(boxed_port.is_some());
1023 assert_eq!(80, boxed_port.unwrap());
1024 }
1025
1026 #[test]
1027 fn parse_simple_url_no_authority() {
1028 let url = "mailto:user@host,user2@host";
1029
1030 let url_components = parse_url(url).unwrap();
1031
1032 assert_eq!(url_components.scheme, "mailto");
1033 assert!(url_components.authority.is_none());
1034 assert_eq!(url_components.path, "user@host,user2@host");
1035
1036 }
1037
1038 #[test]
1039 fn parse_simple_url_no_authority_with_query() {
1040 let url = "mailto:user@host?subject=test#fragment";
1041
1042 let url_components = parse_url(url).unwrap();
1043
1044 assert_eq!(url_components.scheme, "mailto");
1045 assert!(url_components.authority.is_none());
1046 assert_eq!(url_components.path, "user@host");
1047 assert_eq!(url_components.fragment.unwrap(), "fragment");
1048
1049 }
1050
1051 #[test]
1052 fn parse_simple_url_no_authority_with_fragment() {
1053 let url = "mailto:user@host#fragment";
1054
1055 let url_components = parse_url(url).unwrap();
1056
1057 assert_eq!(url_components.scheme, "mailto");
1058 assert!(url_components.authority.is_none());
1059 assert_eq!(url_components.path, "user@host");
1060 assert_eq!(url_components.fragment.unwrap(), "fragment");
1061
1062 }
1063
1064 #[test]
1065 fn parse_simple_url_no_authority_with_query_with_fragment() {
1066 let url = "mailto:user@host?q=123#fragment";
1067
1068 let url_components = parse_url(url).unwrap();
1069
1070 assert_eq!(url_components.scheme, "mailto");
1071 assert!(url_components.authority.is_none());
1072 assert_eq!(url_components.path, "user@host");
1073
1074 }
1075
1076 #[test]
1077 fn parse_simple_url_no_authority_no_path_with_query_with_fragment() {
1078 let url = "mailto:?to=&subject=mailto%20with%20examples&body=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FMailto";
1079
1080 let url_components = parse_url(url).unwrap();
1081
1082 assert_eq!(url_components.scheme, "mailto");
1083 assert!(url_components.authority.is_none());
1084 assert_eq!(url_components.path, "");
1085 assert_eq!(url_components.query.as_ref().unwrap().get("subject").unwrap(), "mailto with examples");
1086 assert_eq!(url_components.query.as_ref().unwrap().get("to").unwrap(), "");
1087 assert_eq!(url_components.query.as_ref().unwrap().get("body").unwrap(), "https://en.wikipedia.org/wiki/Mailto");
1088
1089 }
1090
1091 #[test]
1092 fn extract_port_test() {
1093 let boxed_port = extract_port(":80");
1094 assert!(boxed_port.is_ok());
1095 assert_eq!(80, boxed_port.unwrap().unwrap());
1096 }
1097
1098 #[test]
1099 fn extract_port_test_fail() {
1100 let boxed_port = extract_port(":someport");
1101 assert!(boxed_port.is_err());
1102 assert_eq!("unable to parse port from remaining authority | invalid digit found in string | someport", boxed_port.err().unwrap());
1103 }
1104
1105
1106 #[test]
1107 fn build_authority_host_empty() {
1108 let authority = UrlAuthority{
1109 user_info: None,
1110 host: "".to_string(),
1111 port: None
1112 };
1113
1114 let url_authority = build_authority(authority);
1115
1116 assert_eq!(url_authority, "");
1117 }
1118
1119 #[test]
1120 fn build_authority_host_empty_usrname() {
1121 let authority = UrlAuthority{
1122 user_info: Option::from(UrlUserInfo { username: "usr".to_string(), password: None }),
1123 host: "".to_string(),
1124 port: None
1125 };
1126
1127 let url_authority = build_authority(authority);
1128
1129 assert_eq!(url_authority, "usr@");
1130 }
1131
1132 #[test]
1133 fn build_authority_host_usrname_passwd() {
1134 let authority = UrlAuthority{
1135 user_info: Option::from(UrlUserInfo { username: "usr".to_string(), password: Option::from("pwd".to_string()) }),
1136 host: "somehost".to_string(),
1137 port: None
1138 };
1139
1140 let url_authority = build_authority(authority);
1141
1142 assert_eq!(url_authority, "usr:pwd@somehost");
1143 }
1144
1145 #[test]
1146 fn build_url_all_specified() {
1147 let authority = UrlAuthority{
1148 user_info: Option::from(UrlUserInfo { username: "usr".to_string(), password: Option::from("pwd".to_string()) }),
1149 host: "somehost".to_string(),
1150 port: Option::from(80)
1151 };
1152
1153 let mut q = HashMap::new();
1154 q.insert("q".to_string(), "123".to_string());
1155 q.insert("w".to_string(), "456".to_string());
1156
1157
1158 let url_components = UrlComponents{
1159 scheme: "https".to_string(),
1160 authority: Option::from(authority),
1161 path: "/".to_string(),
1162 query: Option::from(q),
1163 fragment: Option::from("fragment".to_string())
1164 };
1165
1166 let url = build_url(url_components.clone()).unwrap();
1167
1168 let parsed_url_components = parse_url(url.as_str()).unwrap();
1169
1170 assert_eq!(url_components, parsed_url_components);
1171 }
1172
1173 #[test]
1174 fn build_url_only_required_specified() {
1175 let url_components = UrlComponents{
1176 scheme: "https".to_string(),
1177 authority: None,
1178 path: "/".to_string(),
1179 query: None,
1180 fragment: None
1181 };
1182
1183 let url = build_url(url_components.clone()).unwrap();
1184 let parsed_url_components = parse_url(url.as_str()).unwrap();
1185 assert_eq!(url_components, parsed_url_components);
1186 }
1187
1188 #[test]
1189 fn build_authority_host_usrname_passwd_port() {
1190 let authority = UrlAuthority{
1191 user_info: Option::from(UrlUserInfo { username: "usr".to_string(), password: Option::from("pwd".to_string()) }),
1192 host: "somehost".to_string(),
1193 port: Option::from(80)
1194 };
1195
1196 let url_authority = build_authority(authority);
1197
1198 assert_eq!(url_authority, "usr:pwd@somehost:80");
1199 }
1200
1201 #[test]
1202 fn simple_build_steam_api() {
1203 let params_map = HashMap::new();
1204
1205 let url_builder = UrlComponents{
1206 scheme: "https".to_string(),
1207 authority: Some(UrlAuthority{
1208 user_info: None,
1209 host: "api.steampowered.com".to_string(),
1210 port: None
1211 }),
1212 query: Some(params_map),
1213 fragment: None,
1214 path: "/path".to_string()
1215 };
1216
1217 let url = build_url(url_builder).unwrap();
1218
1219 assert_eq!("https://api.steampowered.com/path?", url)
1220 }
1221
1222 #[test]
1223 fn parse_simple_url_no_path_no_query_no_fragment() {
1224 let url = "https://usr:pwd@somehost:80";
1225 let url_components = parse_url(url).unwrap();
1226
1227
1228 assert_eq!(url_components.scheme, "https");
1229 assert_eq!(url_components.authority.as_ref().unwrap().user_info.as_ref().unwrap().username, "usr");
1230 assert_eq!(url_components.authority.as_ref().unwrap().user_info.as_ref().unwrap().password.as_ref().unwrap(), "pwd");
1231 assert_eq!(url_components.authority.as_ref().unwrap().host, "somehost");
1232 assert_eq!(*url_components.authority.as_ref().unwrap().port.as_ref().unwrap() as u8, 80 as u8);
1233 assert_eq!(url_components.path, "");
1234
1235 }
1236
1237 #[test]
1238 fn parse_simple_url_no_usr_no_pwd_no_path_no_query_no_fragment() {
1239 let url = "https://somehost";
1240 let url_components = parse_url(url).unwrap();
1241
1242
1243 assert_eq!(url_components.scheme, "https");
1244 assert!(url_components.authority.as_ref().unwrap().user_info.is_none());
1245 assert!(url_components.authority.as_ref().unwrap().port.is_none());
1246 assert_eq!(url_components.authority.as_ref().unwrap().host, "somehost");
1247 assert_eq!(url_components.path, "");
1248 assert_eq!(url_components.query, None);
1249 assert_eq!(url_components.fragment, None);
1250
1251 }
1252
1253 #[test]
1254 fn parse_simple_url() {
1255 let url = "https://usr:pwd@somehost:80/path?param=value&anotherParam#fragment";
1256 let url_components = parse_url(url).unwrap();
1257
1258
1259 assert_eq!(url_components.scheme, "https");
1260 assert_eq!(url_components.authority.as_ref().unwrap().user_info.as_ref().unwrap()
1261 .username, "usr");
1262 assert_eq!(url_components.authority.as_ref().unwrap().user_info.as_ref().unwrap()
1263 .password.as_ref().unwrap(), "pwd");
1264 assert_eq!(url_components.authority.as_ref().unwrap()
1265 .host, "somehost");
1266 assert_eq!(*url_components.authority.as_ref().unwrap()
1267 .port.as_ref().unwrap() as u8, 80 as u8);
1268 assert_eq!(url_components.path, "/path");
1269 assert_eq!(url_components.query.as_ref().unwrap()
1270 .get("param").unwrap(), "value");
1271 assert!(url_components.query.as_ref().unwrap()
1272 .contains_key("anotherParam"));
1273 assert_eq!("", url_components.query.as_ref().unwrap()
1274 .get("anotherParam").unwrap());
1275
1276 }
1277
1278 #[test]
1279 fn parse_simple_url_ftp() {
1280 let url = "ftp://ftp.is.co.za/rfc/rfc1808.txt";
1281 let url_components = parse_url(url).unwrap();
1282
1283
1284 assert_eq!(url_components.scheme, "ftp");
1285 assert_eq!(url_components.authority.as_ref().unwrap().user_info, None);
1286 assert_eq!(url_components.authority.as_ref().unwrap()
1287 .host, "ftp.is.co.za");
1288 assert_eq!(url_components.authority.as_ref().unwrap()
1289 .port, None);
1290 assert_eq!(url_components.path, "/rfc/rfc1808.txt");
1291 assert_eq!(url_components.query, None);
1292 assert_eq!(url_components.fragment, None);
1293 }
1294
1295 #[test]
1296 fn parse_simple_url_ldap() {
1297 let url = "ldap://[2001:db8::7]/c=GB?objectClass?one";
1298 let url_components = parse_url(url).unwrap();
1299
1300
1301 assert_eq!(url_components.scheme, "ldap");
1302 assert_eq!(url_components.authority.as_ref().unwrap().user_info, None);
1303 assert_eq!(url_components.authority.as_ref().unwrap()
1304 .host, "[2001:db8::7]");
1305 assert_eq!(url_components.authority.as_ref().unwrap()
1306 .port, None);
1307 assert_eq!(url_components.path, "/c=GB");
1308 assert!(url_components.query.unwrap().contains_key("objectClass?one"));
1309 assert_eq!(url_components.fragment, None);
1310 }
1311
1312 #[test]
1313 fn parse_simple_url_news() {
1314 let url = "news:comp.infosystems.www.servers.unix";
1315 let url_components = parse_url(url).unwrap();
1316
1317
1318 assert_eq!(url_components.scheme, "news");
1319 assert_eq!(url_components.authority, None);
1320 assert_eq!(url_components.path, "comp.infosystems.www.servers.unix");
1321 assert_eq!(url_components.query, None);
1322 assert_eq!(url_components.fragment, None);
1323 }
1324
1325 #[test]
1326 fn parse_simple_url_tel() {
1327 let url = "tel:+1-816-555-1212";
1328 let url_components = parse_url(url).unwrap();
1329
1330
1331 assert_eq!(url_components.scheme, "tel");
1332 assert_eq!(url_components.authority, None);
1333 assert_eq!(url_components.path, "+1-816-555-1212");
1334 assert_eq!(url_components.query, None);
1335 assert_eq!(url_components.fragment, None);
1336 }
1337
1338 #[test]
1339 fn parse_simple_url_telnet() {
1340 let url = "telnet://192.0.2.16:80/";
1341 let url_components = parse_url(url).unwrap();
1342
1343
1344 assert_eq!(url_components.scheme, "telnet");
1345 assert_eq!(url_components.authority.as_ref().unwrap().user_info, None);
1346 assert_eq!(url_components.authority.as_ref().unwrap().host, "192.0.2.16");
1347 assert_eq!(url_components.authority.as_ref().unwrap().port.unwrap(), 80);
1348 assert_eq!(url_components.path, "/");
1349 assert_eq!(url_components.query, None);
1350 assert_eq!(url_components.fragment, None);
1351 }
1352
1353 #[test]
1354 fn parse_simple_url_mailto() {
1355 let url = "mailto:John.Doe@example.com";
1356 let url_components = parse_url(url).unwrap();
1357
1358
1359 assert_eq!(url_components.scheme, "mailto");
1360 assert_eq!(url_components.authority, None);
1361 assert_eq!(url_components.path, "John.Doe@example.com");
1362 assert_eq!(url_components.query, None);
1363 assert_eq!(url_components.fragment, None);
1364 }
1365
1366 #[test]
1367 fn parse_simple_url_urn() {
1368 let url = "urn:oasis:names:specification:docbook:dtd:xml:4.1.2";
1369 let url_components = parse_url(url).unwrap();
1370
1371
1372 assert_eq!(url_components.scheme, "urn");
1373 assert_eq!(url_components.authority, None);
1374 assert_eq!(url_components.path, "oasis:names:specification:docbook:dtd:xml:4.1.2");
1375 assert_eq!(url_components.query, None);
1376 assert_eq!(url_components.fragment, None);
1377 }
1378}