1use crate::error::{Error, ParseErr};
3use std::{
4 convert::TryFrom,
5 fmt,
6 ops::{Index, Range},
7 str,
8 string::ToString,
9};
10
11const HTTP_PORT: u16 = 80;
12const HTTPS_PORT: u16 = 443;
13
14#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
16pub struct RangeC {
17 pub start: usize,
18 pub end: usize,
19}
20
21impl RangeC {
22 pub const fn new(start: usize, end: usize) -> RangeC {
31 RangeC { start, end }
32 }
33}
34
35impl From<RangeC> for Range<usize> {
36 fn from(range: RangeC) -> Range<usize> {
37 Range {
38 start: range.start,
39 end: range.end,
40 }
41 }
42}
43
44impl Index<RangeC> for str {
45 type Output = str;
46
47 #[inline]
48 fn index(&self, index: RangeC) -> &str {
49 &self[..][Range::from(index)]
50 }
51}
52
53impl Index<RangeC> for String {
54 type Output = str;
55
56 #[inline]
57 fn index(&self, index: RangeC) -> &str {
58 &self[..][Range::from(index)]
59 }
60}
61
62#[derive(Clone, Debug, PartialEq)]
73pub struct Uri<'a> {
74 inner: &'a str,
75 scheme: RangeC,
76 authority: Option<Authority<'a>>,
77 path: Option<RangeC>,
78 query: Option<RangeC>,
79 fragment: Option<RangeC>,
80}
81
82impl<'a> Uri<'a> {
83 pub fn scheme(&self) -> &str {
94 &self.inner[self.scheme]
95 }
96
97 pub fn user_info(&self) -> Option<&str> {
108 self.authority.as_ref().and_then(|a| a.user_info())
109 }
110
111 pub fn host(&self) -> Option<&str> {
122 self.authority.as_ref().map(|a| a.host())
123 }
124
125 pub fn host_header(&self) -> Option<String> {
136 self.host().map(|h| match self.corr_port() {
137 HTTP_PORT | HTTPS_PORT => h.to_string(),
138 p => format!("{}:{}", h, p),
139 })
140 }
141
142 pub fn port(&self) -> Option<u16> {
153 self.authority.as_ref().and_then(|a| a.port())
154 }
155
156 pub fn corr_port(&self) -> u16 {
168 let default_port = match self.scheme() {
169 "https" => HTTPS_PORT,
170 _ => HTTP_PORT,
171 };
172
173 match self.authority {
174 Some(ref a) => a.port().unwrap_or(default_port),
175 None => default_port,
176 }
177 }
178
179 pub fn path(&self) -> Option<&str> {
190 self.path.map(|r| &self.inner[r])
191 }
192
193 pub fn query(&self) -> Option<&str> {
204 self.query.map(|r| &self.inner[r])
205 }
206
207 pub fn fragment(&self) -> Option<&str> {
218 self.fragment.map(|r| &self.inner[r])
219 }
220
221 pub fn resource(&self) -> &str {
232 let mut result = "/";
233
234 for v in &[self.path, self.query, self.fragment] {
235 if let Some(r) = v {
236 result = &self.inner[r.start..];
237 break;
238 }
239 }
240
241 result
242 }
243}
244
245impl<'a> fmt::Display for Uri<'a> {
246 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
247 let uri = if let Some(auth) = &self.authority {
248 let mut uri = self.inner.to_string();
249 let auth = auth.to_string();
250 let start = self.scheme.end + 3;
251
252 uri.replace_range(start..(start + auth.len()), &auth);
253 uri
254 } else {
255 self.inner.to_string()
256 };
257
258 write!(f, "{}", uri)
259 }
260}
261
262impl<'a> TryFrom<&'a str> for Uri<'a> {
263 type Error = Error;
264
265 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
266 let (scheme, mut uri_part) = get_chunks(&s, Some(RangeC::new(0, s.len())), ":");
267 let scheme = scheme.ok_or(ParseErr::UriErr)?;
268
269 let mut authority = None;
270
271 if let Some(u) = &uri_part {
272 if s[*u].contains("//") {
273 let (auth, part) = get_chunks(&s, Some(RangeC::new(u.start + 2, u.end)), "/");
274
275 authority = if let Some(a) = auth {
276 Some(Authority::try_from(&s[a])?)
277 } else {
278 None
279 };
280
281 uri_part = part;
282 }
283 }
284
285 let (mut path, uri_part) = get_chunks(&s, uri_part, "?");
286
287 if authority.is_some() || &s[scheme] == "file" {
288 path = path.map(|p| RangeC::new(p.start - 1, p.end));
289 }
290
291 let (query, fragment) = get_chunks(&s, uri_part, "#");
292
293 Ok(Uri {
294 inner: s,
295 scheme,
296 authority,
297 path,
298 query,
299 fragment,
300 })
301 }
302}
303
304#[derive(Clone, Debug, PartialEq)]
315pub struct Authority<'a> {
316 inner: &'a str,
317 username: Option<RangeC>,
318 password: Option<RangeC>,
319 host: RangeC,
320 port: Option<RangeC>,
321}
322
323impl<'a> Authority<'a> {
324 pub fn username(&self) -> Option<&'a str> {
335 self.username.map(|r| &self.inner[r])
336 }
337
338 pub fn password(&self) -> Option<&str> {
349 self.password.map(|r| &self.inner[r])
350 }
351
352 pub fn user_info(&self) -> Option<&str> {
363 match (&self.username, &self.password) {
364 (Some(u), Some(p)) => Some(&self.inner[u.start..p.end]),
365 (Some(u), None) => Some(&self.inner[*u]),
366 _ => None,
367 }
368 }
369
370 pub fn host(&self) -> &str {
381 &self.inner[self.host]
382 }
383
384 pub fn port(&self) -> Option<u16> {
395 self.port.as_ref().map(|p| self.inner[*p].parse().unwrap())
396 }
397}
398
399impl<'a> TryFrom<&'a str> for Authority<'a> {
400 type Error = ParseErr;
401
402 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
403 let mut username = None;
404 let mut password = None;
405
406 let uri_part = if s.contains('@') {
407 let (info, part) = get_chunks(&s, Some(RangeC::new(0, s.len())), "@");
408 let (name, pass) = get_chunks(&s, info, ":");
409
410 username = name;
411 password = pass;
412
413 part
414 } else {
415 Some(RangeC::new(0, s.len()))
416 };
417
418 let split_by = if s.contains(']') && s.contains('[') {
419 "]:"
420 } else {
421 ":"
422 };
423 let (host, port) = get_chunks(&s, uri_part, split_by);
424 let host = host.ok_or(ParseErr::UriErr)?;
425
426 if let Some(p) = port {
427 if s[p].parse::<u16>().is_err() {
428 return Err(ParseErr::UriErr);
429 }
430 }
431
432 Ok(Authority {
433 inner: s,
434 username,
435 password,
436 host,
437 port,
438 })
439 }
440}
441
442impl<'a> fmt::Display for Authority<'a> {
443 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
444 let auth = if let Some(pass) = self.password {
445 let range = Range::from(pass);
446
447 let hidden_pass = "*".repeat(range.len());
448 let mut auth = self.inner.to_string();
449 auth.replace_range(range, &hidden_pass);
450
451 auth
452 } else {
453 self.inner.to_string()
454 };
455
456 write!(f, "{}", auth)
457 }
458}
459
460pub fn remove_spaces(text: &mut String) {
462 text.retain(|c| !c.is_whitespace());
463}
464
465fn get_chunks<'a>(
469 s: &'a str,
470 range: Option<RangeC>,
471 separator: &'a str,
472) -> (Option<RangeC>, Option<RangeC>) {
473 if let Some(r) = range {
474 let range = Range::from(r);
475
476 match s[range.clone()].find(separator) {
477 Some(i) => {
478 let mid = r.start + i + separator.len();
479 let before = Some(RangeC::new(r.start, mid - 1)).filter(|r| r.start != r.end);
480 let after = Some(RangeC::new(mid, r.end)).filter(|r| r.start != r.end);
481
482 (before, after)
483 }
484 None => {
485 if !s[range].is_empty() {
486 (Some(r), None)
487 } else {
488 (None, None)
489 }
490 }
491 }
492 } else {
493 (None, None)
494 }
495}
496
497#[cfg(test)]
498mod tests {
499 use super::*;
500
501 const TEST_URIS: [&str; 5] = [
502 "https://user:info@foo.com:12/bar/baz?query#fragment",
503 "file:///C:/Users/User/Pictures/screenshot.png",
504 "https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol",
505 "mailto:John.Doe@example.com",
506 "https://[4b10:bbb0:0:d0::ba7:8001]:443/",
507 ];
508
509 const TEST_AUTH: [&str; 4] = [
510 "user:info@foo.com:12",
511 "en.wikipedia.org",
512 "John.Doe@example.com",
513 "[4b10:bbb0:0:d0::ba7:8001]:443",
514 ];
515
516 #[test]
517 fn remove_space() {
518 let mut text = String::from("Hello World !");
519 let expect = String::from("HelloWorld!");
520
521 remove_spaces(&mut text);
522 assert_eq!(text, expect);
523 }
524
525 #[test]
526 fn uri_full_parse() {
527 let uri = Uri::try_from(
528 "abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1",
529 )
530 .unwrap();
531 assert_eq!(uri.scheme(), "abc");
532
533 assert_eq!(uri.user_info(), Some("username:password"));
534 assert_eq!(uri.host(), Some("example.com"));
535 assert_eq!(uri.port(), Some(123));
536
537 assert_eq!(uri.path(), Some("/path/data"));
538 assert_eq!(uri.query(), Some("key=value&key2=value2"));
539 assert_eq!(uri.fragment(), Some("fragid1"));
540 }
541
542 #[test]
543 fn uri_parse() {
544 for uri in TEST_URIS.iter() {
545 Uri::try_from(*uri).unwrap();
546 }
547 }
548
549 #[test]
550 fn uri_scheme() {
551 let uris: Vec<_> = TEST_URIS
552 .iter()
553 .map(|uri| Uri::try_from(*uri).unwrap())
554 .collect();
555
556 assert_eq!(uris[0].scheme(), "https");
557 assert_eq!(uris[1].scheme(), "file");
558 assert_eq!(uris[2].scheme(), "https");
559 assert_eq!(uris[3].scheme(), "mailto");
560 assert_eq!(uris[4].scheme(), "https");
561 }
562
563 #[test]
564 fn uri_uesr_info() {
565 let uris: Vec<_> = TEST_URIS
566 .iter()
567 .map(|uri| Uri::try_from(*uri).unwrap())
568 .collect();
569
570 assert_eq!(uris[0].user_info(), Some("user:info"));
571 assert_eq!(uris[1].user_info(), None);
572 assert_eq!(uris[2].user_info(), None);
573 assert_eq!(uris[3].user_info(), None);
574 assert_eq!(uris[4].user_info(), None);
575 }
576
577 #[test]
578 fn uri_host() {
579 let uris: Vec<_> = TEST_URIS
580 .iter()
581 .map(|uri| Uri::try_from(*uri).unwrap())
582 .collect();
583
584 assert_eq!(uris[0].host(), Some("foo.com"));
585 assert_eq!(uris[1].host(), None);
586 assert_eq!(uris[2].host(), Some("en.wikipedia.org"));
587 assert_eq!(uris[3].host(), None);
588 assert_eq!(uris[4].host(), Some("[4b10:bbb0:0:d0::ba7:8001]"));
589 }
590
591 #[test]
592 fn uri_host_header() {
593 let uri_def: Uri =
594 Uri::try_from("https://en.wikipedia.org:443/wiki/Hypertext_Transfer_Protocol").unwrap();
595 let uris: Vec<_> = TEST_URIS
596 .iter()
597 .map(|uri| Uri::try_from(*uri).unwrap())
598 .collect();
599
600 assert_eq!(uris[0].host_header(), Some("foo.com:12".to_string()));
601 assert_eq!(uris[2].host_header(), Some("en.wikipedia.org".to_string()));
602 assert_eq!(uri_def.host_header(), Some("en.wikipedia.org".to_string()));
603 }
604
605 #[test]
606 fn uri_port() {
607 let uris: Vec<_> = TEST_URIS
608 .iter()
609 .map(|uri| Uri::try_from(*uri).unwrap())
610 .collect();
611
612 assert_eq!(uris[0].port(), Some(12));
613 assert_eq!(uris[4].port(), Some(443));
614
615 for i in 1..3 {
616 assert_eq!(uris[i].port(), None);
617 }
618 }
619
620 #[test]
621 fn uri_corr_port() {
622 let uris: Vec<_> = TEST_URIS
623 .iter()
624 .map(|uri| Uri::try_from(*uri).unwrap())
625 .collect();
626
627 assert_eq!(uris[0].corr_port(), 12);
628 assert_eq!(uris[1].corr_port(), HTTP_PORT);
629 assert_eq!(uris[2].corr_port(), HTTPS_PORT);
630 assert_eq!(uris[3].corr_port(), HTTP_PORT);
631 assert_eq!(uris[4].corr_port(), HTTPS_PORT);
632 }
633
634 #[test]
635 fn uri_path() {
636 let uris: Vec<_> = TEST_URIS
637 .iter()
638 .map(|uri| Uri::try_from(*uri).unwrap())
639 .collect();
640
641 assert_eq!(uris[0].path(), Some("/bar/baz"));
642 assert_eq!(
643 uris[1].path(),
644 Some("/C:/Users/User/Pictures/screenshot.png")
645 );
646 assert_eq!(uris[2].path(), Some("/wiki/Hypertext_Transfer_Protocol"));
647 assert_eq!(uris[3].path(), Some("John.Doe@example.com"));
648 assert_eq!(uris[4].path(), None);
649 }
650
651 #[test]
652 fn uri_query() {
653 let uris: Vec<_> = TEST_URIS
654 .iter()
655 .map(|uri| Uri::try_from(*uri).unwrap())
656 .collect();
657
658 assert_eq!(uris[0].query(), Some("query"));
659
660 for i in 1..4 {
661 assert_eq!(uris[i].query(), None);
662 }
663 }
664
665 #[test]
666 fn uri_fragment() {
667 let uris: Vec<_> = TEST_URIS
668 .iter()
669 .map(|uri| Uri::try_from(*uri).unwrap())
670 .collect();
671
672 assert_eq!(uris[0].fragment(), Some("fragment"));
673
674 for i in 1..4 {
675 assert_eq!(uris[i].fragment(), None);
676 }
677 }
678
679 #[test]
680 fn uri_resource() {
681 let uris: Vec<_> = TEST_URIS
682 .iter()
683 .map(|uri| Uri::try_from(*uri).unwrap())
684 .collect();
685
686 assert_eq!(uris[0].resource(), "/bar/baz?query#fragment");
687 assert_eq!(uris[1].resource(), "/C:/Users/User/Pictures/screenshot.png");
688 assert_eq!(uris[2].resource(), "/wiki/Hypertext_Transfer_Protocol");
689 assert_eq!(uris[3].resource(), "John.Doe@example.com");
690 assert_eq!(uris[4].resource(), "/");
691 }
692
693 #[test]
694 fn uri_display() {
695 let uris: Vec<_> = TEST_URIS
696 .iter()
697 .map(|uri| Uri::try_from(*uri).unwrap())
698 .collect();
699
700 assert_eq!(
701 uris[0].to_string(),
702 "https://user:****@foo.com:12/bar/baz?query#fragment"
703 );
704
705 for i in 1..uris.len() {
706 let s = uris[i].to_string();
707 assert_eq!(s, TEST_URIS[i]);
708 }
709 }
710
711 #[test]
712 fn authority_username() {
713 let auths: Vec<_> = TEST_AUTH
714 .iter()
715 .map(|auth| Authority::try_from(*auth).unwrap())
716 .collect();
717
718 assert_eq!(auths[0].username(), Some("user"));
719 assert_eq!(auths[1].username(), None);
720 assert_eq!(auths[2].username(), Some("John.Doe"));
721 assert_eq!(auths[3].username(), None);
722 }
723
724 #[test]
725 fn authority_password() {
726 let auths: Vec<_> = TEST_AUTH
727 .iter()
728 .map(|auth| Authority::try_from(*auth).unwrap())
729 .collect();
730
731 assert_eq!(auths[0].password(), Some("info"));
732 assert_eq!(auths[1].password(), None);
733 assert_eq!(auths[2].password(), None);
734 assert_eq!(auths[3].password(), None);
735 }
736
737 #[test]
738 fn authority_host() {
739 let auths: Vec<_> = TEST_AUTH
740 .iter()
741 .map(|auth| Authority::try_from(*auth).unwrap())
742 .collect();
743
744 assert_eq!(auths[0].host(), "foo.com");
745 assert_eq!(auths[1].host(), "en.wikipedia.org");
746 assert_eq!(auths[2].host(), "example.com");
747 assert_eq!(auths[3].host(), "[4b10:bbb0:0:d0::ba7:8001]");
748 }
749
750 #[test]
751 fn authority_port() {
752 let auths: Vec<_> = TEST_AUTH
753 .iter()
754 .map(|auth| Authority::try_from(*auth).unwrap())
755 .collect();
756
757 assert_eq!(auths[0].port(), Some(12));
758 assert_eq!(auths[1].port(), None);
759 assert_eq!(auths[2].port(), None);
760 assert_eq!(auths[3].port(), Some(443));
761 }
762
763 #[test]
764 fn authority_from_str() {
765 for auth in TEST_AUTH.iter() {
766 Authority::try_from(*auth).unwrap();
767 }
768 }
769
770 #[test]
771 fn authority_display() {
772 let auths: Vec<_> = TEST_AUTH
773 .iter()
774 .map(|auth| Authority::try_from(*auth).unwrap())
775 .collect();
776
777 assert_eq!("user:****@foo.com:12", auths[0].to_string());
778
779 for i in 1..auths.len() {
780 let s = auths[i].to_string();
781 assert_eq!(s, TEST_AUTH[i]);
782 }
783 }
784
785 #[test]
786 fn range_c_new() {
787 assert_eq!(
788 RangeC::new(22, 343),
789 RangeC {
790 start: 22,
791 end: 343,
792 }
793 )
794 }
795
796 #[test]
797 fn range_from_range_c() {
798 assert_eq!(
799 Range::from(RangeC::new(222, 43)),
800 Range {
801 start: 222,
802 end: 43,
803 }
804 )
805 }
806
807 #[test]
808 fn range_c_index() {
809 const RANGE: RangeC = RangeC::new(0, 4);
810 let text = TEST_AUTH[0].to_string();
811
812 assert_eq!(text[..4], text[RANGE])
813 }
814}