1use std::fmt;
2use std::borrow::Cow;
3use std::str::FromStr;
4#[allow(unused, deprecated)]
5use std::ascii::AsciiExt;
6
7use mime::Mime;
8use language_tags::LanguageTag;
9
10use header::parsing;
11use header::{Header, Raw};
12
13#[derive(Clone, PartialEq, Debug)]
73pub struct Link {
74 values: Vec<LinkValue>
76}
77
78#[derive(Clone, PartialEq, Debug)]
81pub struct LinkValue {
82 link: Cow<'static, str>,
84
85 rel: Option<Vec<RelationType>>,
87
88 anchor: Option<String>,
90
91 rev: Option<Vec<RelationType>>,
93
94 href_lang: Option<Vec<LanguageTag>>,
97
98 media_desc: Option<Vec<MediaDesc>>,
100
101 title: Option<String>,
103
104 title_star: Option<String>,
106
107 media_type: Option<Mime>,
110}
111
112#[derive(Clone, PartialEq, Debug)]
117pub enum MediaDesc {
118 Screen,
120 Tty,
122 Tv,
124 Projection,
126 Handheld,
128 Print,
130 Braille,
132 Aural,
134 All,
136 Extension(String)
138}
139
140#[derive(Clone, PartialEq, Debug)]
143pub enum RelationType {
144 Alternate,
146 Appendix,
148 Bookmark,
150 Chapter,
152 Contents,
154 Copyright,
156 Current,
158 DescribedBy,
160 Edit,
162 EditMedia,
164 Enclosure,
166 First,
168 Glossary,
170 Help,
172 Hub,
174 Index,
176 Last,
178 LatestVersion,
180 License,
182 Next,
184 NextArchive,
186 Payment,
188 Prev,
190 PredecessorVersion,
192 Previous,
194 PrevArchive,
196 Related,
198 Replies,
200 Section,
202 RelationTypeSelf,
204 Service,
206 Start,
208 Stylesheet,
210 Subsection,
212 SuccessorVersion,
214 Up,
216 VersionHistory,
218 Via,
220 WorkingCopy,
222 WorkingCopyOf,
224 ExtRelType(String)
226}
227
228impl Link {
233 pub fn new(link_values: Vec<LinkValue>) -> Link {
235 Link { values: link_values }
236 }
237
238 pub fn values(&self) -> &[LinkValue] {
240 self.values.as_ref()
241 }
242
243 pub fn push_value(&mut self, link_value: LinkValue) {
245 self.values.push(link_value);
246 }
247}
248
249impl LinkValue {
250 pub fn new<T>(uri: T) -> LinkValue
252 where T: Into<Cow<'static, str>> {
253 LinkValue {
254 link: uri.into(),
255 rel: None,
256 anchor: None,
257 rev: None,
258 href_lang: None,
259 media_desc: None,
260 title: None,
261 title_star: None,
262 media_type: None,
263 }
264 }
265
266 pub fn link(&self) -> &str {
268 self.link.as_ref()
269 }
270
271 pub fn rel(&self) -> Option<&[RelationType]> {
273 self.rel.as_ref().map(AsRef::as_ref)
274 }
275
276 pub fn anchor(&self) -> Option<&str> {
278 self.anchor.as_ref().map(AsRef::as_ref)
279 }
280
281 pub fn rev(&self) -> Option<&[RelationType]> {
283 self.rev.as_ref().map(AsRef::as_ref)
284 }
285
286 pub fn href_lang(&self) -> Option<&[LanguageTag]> {
288 self.href_lang.as_ref().map(AsRef::as_ref)
289 }
290
291 pub fn media_desc(&self) -> Option<&[MediaDesc]> {
293 self.media_desc.as_ref().map(AsRef::as_ref)
294 }
295
296 pub fn title(&self) -> Option<&str> {
298 self.title.as_ref().map(AsRef::as_ref)
299 }
300
301 pub fn title_star(&self) -> Option<&str> {
303 self.title_star.as_ref().map(AsRef::as_ref)
304 }
305
306 pub fn media_type(&self) -> Option<&Mime> {
308 self.media_type.as_ref()
309 }
310
311 pub fn push_rel(mut self, rel: RelationType) -> LinkValue {
313 let mut v = self.rel.take().unwrap_or(Vec::new());
314
315 v.push(rel);
316
317 self.rel = Some(v);
318
319 self
320 }
321
322 pub fn set_anchor<T: Into<String>>(mut self, anchor: T) -> LinkValue {
324 self.anchor = Some(anchor.into());
325
326 self
327 }
328
329 pub fn push_rev(mut self, rev: RelationType) -> LinkValue {
331 let mut v = self.rev.take().unwrap_or(Vec::new());
332
333 v.push(rev);
334
335 self.rev = Some(v);
336
337 self
338 }
339
340 pub fn push_href_lang(mut self, language_tag: LanguageTag) -> LinkValue {
342 let mut v = self.href_lang.take().unwrap_or(Vec::new());
343
344 v.push(language_tag);
345
346 self.href_lang = Some(v);
347
348 self
349 }
350
351 pub fn push_media_desc(mut self, media_desc: MediaDesc) -> LinkValue {
353 let mut v = self.media_desc.take().unwrap_or(Vec::new());
354
355 v.push(media_desc);
356
357 self.media_desc = Some(v);
358
359 self
360 }
361
362 pub fn set_title<T: Into<String>>(mut self, title: T) -> LinkValue {
364 self.title = Some(title.into());
365
366 self
367 }
368
369 pub fn set_title_star<T: Into<String>>(mut self, title_star: T) -> LinkValue {
371 self.title_star = Some(title_star.into());
372
373 self
374 }
375
376 pub fn set_media_type(mut self, media_type: Mime) -> LinkValue {
378 self.media_type = Some(media_type);
379
380 self
381 }
382}
383
384impl Header for Link {
389 fn header_name() -> &'static str {
390 static NAME: &'static str = "Link";
391 NAME
392 }
393
394 fn parse_header(raw: &Raw) -> ::Result<Link> {
395 raw.iter()
399 .map(parsing::from_raw_str::<Link>)
400 .fold(None, |p, c| {
401 match (p, c) {
402 (None, c) => Some(c),
403 (e @ Some(Err(_)), _) => e,
404 (Some(Ok(mut p)), Ok(c)) => {
405 p.values.extend(c.values);
406
407 Some(Ok(p))
408 },
409 _ => Some(Err(::Error::Header)),
410 }
411 })
412 .unwrap_or(Err(::Error::Header))
413 }
414
415 fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
416 f.fmt_line(self)
417 }
418}
419
420impl fmt::Display for Link {
421 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
422 fmt_delimited(f, self.values.as_slice(), ", ", ("", ""))
423 }
424}
425
426impl fmt::Display for LinkValue {
427 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
428 try!(write!(f, "<{}>", self.link));
429
430 if let Some(ref rel) = self.rel {
431 try!(fmt_delimited(f, rel.as_slice(), " ", ("; rel=\"", "\"")));
432 }
433 if let Some(ref anchor) = self.anchor {
434 try!(write!(f, "; anchor=\"{}\"", anchor));
435 }
436 if let Some(ref rev) = self.rev {
437 try!(fmt_delimited(f, rev.as_slice(), " ", ("; rev=\"", "\"")));
438 }
439 if let Some(ref href_lang) = self.href_lang {
440 for tag in href_lang {
441 try!(write!(f, "; hreflang={}", tag));
442 }
443 }
444 if let Some(ref media_desc) = self.media_desc {
445 try!(fmt_delimited(f, media_desc.as_slice(), ", ", ("; media=\"", "\"")));
446 }
447 if let Some(ref title) = self.title {
448 try!(write!(f, "; title=\"{}\"", title));
449 }
450 if let Some(ref title_star) = self.title_star {
451 try!(write!(f, "; title*={}", title_star));
452 }
453 if let Some(ref media_type) = self.media_type {
454 try!(write!(f, "; type=\"{}\"", media_type));
455 }
456
457 Ok(())
458 }
459}
460
461impl FromStr for Link {
462 type Err = ::Error;
463
464 fn from_str(s: &str) -> ::Result<Link> {
465 let link_split = SplitAsciiUnquoted::new(s, ";,");
467
468 let mut link_values: Vec<LinkValue> = Vec::new();
469
470 for segment in link_split {
473 if segment.trim().starts_with('<') {
476 link_values.push(
477 match verify_and_trim(segment.trim(), (b'<', b'>')) {
478 Err(_) => return Err(::Error::Header),
479 Ok(s) => {
480 LinkValue {
481 link: s.to_owned().into(),
482 rel: None,
483 anchor: None,
484 rev: None,
485 href_lang: None,
486 media_desc: None,
487 title: None,
488 title_star: None,
489 media_type: None,
490 }
491 },
492 }
493 );
494 } else {
495 let mut link_param_split = segment.splitn(2, '=');
497
498 let link_param_name = match link_param_split.next() {
499 None => return Err(::Error::Header),
500 Some(p) => p.trim(),
501 };
502
503 let link_header = match link_values.last_mut() {
504 None => return Err(::Error::Header),
505 Some(l) => l,
506 };
507
508 if "rel".eq_ignore_ascii_case(link_param_name) {
509 if link_header.rel.is_none() {
512 link_header.rel = match link_param_split.next() {
513 None | Some("") => return Err(::Error::Header),
514 Some(s) => {
515 s.trim_matches(|c: char| c == '"' || c.is_whitespace())
516 .split(' ')
517 .map(|t| t.trim().parse())
518 .collect::<Result<Vec<RelationType>, _>>()
519 .or_else(|_| Err(::Error::Header))
520 .ok()
521 },
522 };
523 }
524 } else if "anchor".eq_ignore_ascii_case(link_param_name) {
525 link_header.anchor = match link_param_split.next() {
528 None | Some("") => return Err(::Error::Header),
529 Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) {
530 Err(_) => return Err(::Error::Header),
531 Ok(a) => Some(String::from(a)),
532 },
533 };
534 } else if "rev".eq_ignore_ascii_case(link_param_name) {
535 if link_header.rev.is_none() {
538 link_header.rev = match link_param_split.next() {
539 None | Some("") => return Err(::Error::Header),
540 Some(s) => {
541 s.trim_matches(|c: char| c == '"' || c.is_whitespace())
542 .split(' ')
543 .map(|t| t.trim().parse())
544 .collect::<Result<Vec<RelationType>, _>>()
545 .or_else(|_| Err(::Error::Header))
546 .ok()
547 },
548 }
549 }
550 } else if "hreflang".eq_ignore_ascii_case(link_param_name) {
551 let mut v = link_header.href_lang.take().unwrap_or(Vec::new());
554
555 v.push(
556 match link_param_split.next() {
557 None | Some("") => return Err(::Error::Header),
558 Some(s) => match s.trim().parse() {
559 Err(_) => return Err(::Error::Header),
560 Ok(t) => t,
561 },
562 }
563 );
564
565 link_header.href_lang = Some(v);
566 } else if "media".eq_ignore_ascii_case(link_param_name) {
567 if link_header.media_desc.is_none() {
570 link_header.media_desc = match link_param_split.next() {
571 None | Some("") => return Err(::Error::Header),
572 Some(s) => {
573 s.trim_matches(|c: char| c == '"' || c.is_whitespace())
574 .split(',')
575 .map(|t| t.trim().parse())
576 .collect::<Result<Vec<MediaDesc>, _>>()
577 .or_else(|_| Err(::Error::Header))
578 .ok()
579 },
580 };
581 }
582 } else if "title".eq_ignore_ascii_case(link_param_name) {
583 if link_header.title.is_none() {
586 link_header.title = match link_param_split.next() {
587 None | Some("") => return Err(::Error::Header),
588 Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) {
589 Err(_) => return Err(::Error::Header),
590 Ok(t) => Some(String::from(t)),
591 },
592 };
593 }
594 } else if "title*".eq_ignore_ascii_case(link_param_name) {
595 if link_header.title_star.is_none() {
601 link_header.title_star = match link_param_split.next() {
602 None | Some("") => return Err(::Error::Header),
603 Some(s) => Some(String::from(s.trim())),
604 };
605 }
606 } else if "type".eq_ignore_ascii_case(link_param_name) {
607 if link_header.media_type.is_none() {
610 link_header.media_type = match link_param_split.next() {
611 None | Some("") => return Err(::Error::Header),
612 Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) {
613 Err(_) => return Err(::Error::Header),
614 Ok(t) => match t.parse() {
615 Err(_) => return Err(::Error::Header),
616 Ok(m) => Some(m),
617 },
618 },
619
620 };
621 }
622 } else {
623 return Err(::Error::Header);
624 }
625 }
626 }
627
628 Ok(Link::new(link_values))
629 }
630}
631
632impl fmt::Display for MediaDesc {
633 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
634 match *self {
635 MediaDesc::Screen => write!(f, "screen"),
636 MediaDesc::Tty => write!(f, "tty"),
637 MediaDesc::Tv => write!(f, "tv"),
638 MediaDesc::Projection => write!(f, "projection"),
639 MediaDesc::Handheld => write!(f, "handheld"),
640 MediaDesc::Print => write!(f, "print"),
641 MediaDesc::Braille => write!(f, "braille"),
642 MediaDesc::Aural => write!(f, "aural"),
643 MediaDesc::All => write!(f, "all"),
644 MediaDesc::Extension(ref other) => write!(f, "{}", other),
645 }
646 }
647}
648
649impl FromStr for MediaDesc {
650 type Err = ::Error;
651
652 fn from_str(s: &str) -> ::Result<MediaDesc> {
653 match s {
654 "screen" => Ok(MediaDesc::Screen),
655 "tty" => Ok(MediaDesc::Tty),
656 "tv" => Ok(MediaDesc::Tv),
657 "projection" => Ok(MediaDesc::Projection),
658 "handheld" => Ok(MediaDesc::Handheld),
659 "print" => Ok(MediaDesc::Print),
660 "braille" => Ok(MediaDesc::Braille),
661 "aural" => Ok(MediaDesc::Aural),
662 "all" => Ok(MediaDesc::All),
663 _ => Ok(MediaDesc::Extension(String::from(s))),
664 }
665 }
666}
667
668impl fmt::Display for RelationType {
669 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
670 match *self {
671 RelationType::Alternate => write!(f, "alternate"),
672 RelationType::Appendix => write!(f, "appendix"),
673 RelationType::Bookmark => write!(f, "bookmark"),
674 RelationType::Chapter => write!(f, "chapter"),
675 RelationType::Contents => write!(f, "contents"),
676 RelationType::Copyright => write!(f, "copyright"),
677 RelationType::Current => write!(f, "current"),
678 RelationType::DescribedBy => write!(f, "describedby"),
679 RelationType::Edit => write!(f, "edit"),
680 RelationType::EditMedia => write!(f, "edit-media"),
681 RelationType::Enclosure => write!(f, "enclosure"),
682 RelationType::First => write!(f, "first"),
683 RelationType::Glossary => write!(f, "glossary"),
684 RelationType::Help => write!(f, "help"),
685 RelationType::Hub => write!(f, "hub"),
686 RelationType::Index => write!(f, "index"),
687 RelationType::Last => write!(f, "last"),
688 RelationType::LatestVersion => write!(f, "latest-version"),
689 RelationType::License => write!(f, "license"),
690 RelationType::Next => write!(f, "next"),
691 RelationType::NextArchive => write!(f, "next-archive"),
692 RelationType::Payment => write!(f, "payment"),
693 RelationType::Prev => write!(f, "prev"),
694 RelationType::PredecessorVersion => write!(f, "predecessor-version"),
695 RelationType::Previous => write!(f, "previous"),
696 RelationType::PrevArchive => write!(f, "prev-archive"),
697 RelationType::Related => write!(f, "related"),
698 RelationType::Replies => write!(f, "replies"),
699 RelationType::Section => write!(f, "section"),
700 RelationType::RelationTypeSelf => write!(f, "self"),
701 RelationType::Service => write!(f, "service"),
702 RelationType::Start => write!(f, "start"),
703 RelationType::Stylesheet => write!(f, "stylesheet"),
704 RelationType::Subsection => write!(f, "subsection"),
705 RelationType::SuccessorVersion => write!(f, "successor-version"),
706 RelationType::Up => write!(f, "up"),
707 RelationType::VersionHistory => write!(f, "version-history"),
708 RelationType::Via => write!(f, "via"),
709 RelationType::WorkingCopy => write!(f, "working-copy"),
710 RelationType::WorkingCopyOf => write!(f, "working-copy-of"),
711 RelationType::ExtRelType(ref uri) => write!(f, "{}", uri),
712 }
713 }
714}
715
716impl FromStr for RelationType {
717 type Err = ::Error;
718
719 fn from_str(s: &str) -> ::Result<RelationType> {
720 if "alternate".eq_ignore_ascii_case(s) {
721 Ok(RelationType::Alternate)
722 } else if "appendix".eq_ignore_ascii_case(s) {
723 Ok(RelationType::Appendix)
724 } else if "bookmark".eq_ignore_ascii_case(s) {
725 Ok(RelationType::Bookmark)
726 } else if "chapter".eq_ignore_ascii_case(s) {
727 Ok(RelationType::Chapter)
728 } else if "contents".eq_ignore_ascii_case(s) {
729 Ok(RelationType::Contents)
730 } else if "copyright".eq_ignore_ascii_case(s) {
731 Ok(RelationType::Copyright)
732 } else if "current".eq_ignore_ascii_case(s) {
733 Ok(RelationType::Current)
734 } else if "describedby".eq_ignore_ascii_case(s) {
735 Ok(RelationType::DescribedBy)
736 } else if "edit".eq_ignore_ascii_case(s) {
737 Ok(RelationType::Edit)
738 } else if "edit-media".eq_ignore_ascii_case(s) {
739 Ok(RelationType::EditMedia)
740 } else if "enclosure".eq_ignore_ascii_case(s) {
741 Ok(RelationType::Enclosure)
742 } else if "first".eq_ignore_ascii_case(s) {
743 Ok(RelationType::First)
744 } else if "glossary".eq_ignore_ascii_case(s) {
745 Ok(RelationType::Glossary)
746 } else if "help".eq_ignore_ascii_case(s) {
747 Ok(RelationType::Help)
748 } else if "hub".eq_ignore_ascii_case(s) {
749 Ok(RelationType::Hub)
750 } else if "index".eq_ignore_ascii_case(s) {
751 Ok(RelationType::Index)
752 } else if "last".eq_ignore_ascii_case(s) {
753 Ok(RelationType::Last)
754 } else if "latest-version".eq_ignore_ascii_case(s) {
755 Ok(RelationType::LatestVersion)
756 } else if "license".eq_ignore_ascii_case(s) {
757 Ok(RelationType::License)
758 } else if "next".eq_ignore_ascii_case(s) {
759 Ok(RelationType::Next)
760 } else if "next-archive".eq_ignore_ascii_case(s) {
761 Ok(RelationType::NextArchive)
762 } else if "payment".eq_ignore_ascii_case(s) {
763 Ok(RelationType::Payment)
764 } else if "prev".eq_ignore_ascii_case(s) {
765 Ok(RelationType::Prev)
766 } else if "predecessor-version".eq_ignore_ascii_case(s) {
767 Ok(RelationType::PredecessorVersion)
768 } else if "previous".eq_ignore_ascii_case(s) {
769 Ok(RelationType::Previous)
770 } else if "prev-archive".eq_ignore_ascii_case(s) {
771 Ok(RelationType::PrevArchive)
772 } else if "related".eq_ignore_ascii_case(s) {
773 Ok(RelationType::Related)
774 } else if "replies".eq_ignore_ascii_case(s) {
775 Ok(RelationType::Replies)
776 } else if "section".eq_ignore_ascii_case(s) {
777 Ok(RelationType::Section)
778 } else if "self".eq_ignore_ascii_case(s) {
779 Ok(RelationType::RelationTypeSelf)
780 } else if "service".eq_ignore_ascii_case(s) {
781 Ok(RelationType::Service)
782 } else if "start".eq_ignore_ascii_case(s) {
783 Ok(RelationType::Start)
784 } else if "stylesheet".eq_ignore_ascii_case(s) {
785 Ok(RelationType::Stylesheet)
786 } else if "subsection".eq_ignore_ascii_case(s) {
787 Ok(RelationType::Subsection)
788 } else if "successor-version".eq_ignore_ascii_case(s) {
789 Ok(RelationType::SuccessorVersion)
790 } else if "up".eq_ignore_ascii_case(s) {
791 Ok(RelationType::Up)
792 } else if "version-history".eq_ignore_ascii_case(s) {
793 Ok(RelationType::VersionHistory)
794 } else if "via".eq_ignore_ascii_case(s) {
795 Ok(RelationType::Via)
796 } else if "working-copy".eq_ignore_ascii_case(s) {
797 Ok(RelationType::WorkingCopy)
798 } else if "working-copy-of".eq_ignore_ascii_case(s) {
799 Ok(RelationType::WorkingCopyOf)
800 } else {
801 Ok(RelationType::ExtRelType(String::from(s)))
802 }
803 }
804}
805
806struct SplitAsciiUnquoted<'a> {
811 src: &'a str,
812 pos: usize,
813 del: &'a str
814}
815
816impl<'a> SplitAsciiUnquoted<'a> {
817 fn new(s: &'a str, d: &'a str) -> SplitAsciiUnquoted<'a> {
818 SplitAsciiUnquoted{
819 src: s,
820 pos: 0,
821 del: d,
822 }
823 }
824}
825
826impl<'a> Iterator for SplitAsciiUnquoted<'a> {
827 type Item = &'a str;
828
829 fn next(&mut self) -> Option<&'a str> {
830 if self.pos < self.src.len() {
831 let prev_pos = self.pos;
832 let mut pos = self.pos;
833
834 let mut in_quotes = false;
835
836 for c in self.src[prev_pos..].as_bytes().iter() {
837 in_quotes ^= *c == b'"';
838
839 if !in_quotes && self.del.as_bytes().contains(c) {
841 break;
842 }
843
844 pos += 1;
845 }
846
847 self.pos = pos + 1;
848
849 Some(&self.src[prev_pos..pos])
850 } else {
851 None
852 }
853 }
854}
855
856fn fmt_delimited<T: fmt::Display>(f: &mut fmt::Formatter, p: &[T], d: &str, b: (&str, &str)) -> fmt::Result {
857 if p.len() != 0 {
858 try!(write!(f, "{}{}", b.0, p[0]));
860
861 for i in &p[1..] {
862 try!(write!(f, "{}{}", d, i));
864 }
865
866 try!(write!(f, "{}", b.1));
868 }
869
870 Ok(())
871}
872
873fn verify_and_trim(s: &str, b: (u8, u8)) -> ::Result<&str> {
874 let length = s.len();
875 let byte_array = s.as_bytes();
876
877 if length > 1 && b.0 == byte_array[0] && b.1 == byte_array[length - 1] {
880 Ok(s.trim_matches(
881 |c: char| c == b.0 as char || c == b.1 as char || c.is_whitespace())
882 )
883 } else {
884 Err(::Error::Header)
885 }
886}
887
888#[cfg(test)]
893mod tests {
894 use std::fmt;
895 use std::fmt::Write;
896
897 use super::{Link, LinkValue, MediaDesc, RelationType, SplitAsciiUnquoted};
898 use super::{fmt_delimited, verify_and_trim};
899
900 use header::Header;
901
902 use buffer::BufReader;
903 use mock::MockStream;
904 use http::h1::parse_request;
905
906 use mime;
907
908 #[test]
909 fn test_link() {
910 let link_value = LinkValue::new("http://example.com/TheBook/chapter2")
911 .push_rel(RelationType::Previous)
912 .push_rev(RelationType::Next)
913 .set_title("previous chapter");
914
915 let link_header = b"<http://example.com/TheBook/chapter2>; \
916 rel=\"previous\"; rev=next; title=\"previous chapter\"";
917
918 let expected_link = Link::new(vec![link_value]);
919
920 let link = Header::parse_header(&vec![link_header.to_vec()].into());
921 assert_eq!(link.ok(), Some(expected_link));
922 }
923
924 #[test]
925 fn test_link_multiple_values() {
926 let first_link = LinkValue::new("/TheBook/chapter2")
927 .push_rel(RelationType::Previous)
928 .set_title_star("UTF-8'de'letztes%20Kapitel");
929
930 let second_link = LinkValue::new("/TheBook/chapter4")
931 .push_rel(RelationType::Next)
932 .set_title_star("UTF-8'de'n%c3%a4chstes%20Kapitel");
933
934 let link_header = b"</TheBook/chapter2>; \
935 rel=\"previous\"; title*=UTF-8'de'letztes%20Kapitel, \
936 </TheBook/chapter4>; \
937 rel=\"next\"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel";
938
939 let expected_link = Link::new(vec![first_link, second_link]);
940
941 let link = Header::parse_header(&vec![link_header.to_vec()].into());
942 assert_eq!(link.ok(), Some(expected_link));
943 }
944
945 #[test]
946 fn test_link_all_attributes() {
947 let link_value = LinkValue::new("http://example.com/TheBook/chapter2")
948 .push_rel(RelationType::Previous)
949 .set_anchor("../anchor/example/")
950 .push_rev(RelationType::Next)
951 .push_href_lang("de".parse().unwrap())
952 .push_media_desc(MediaDesc::Screen)
953 .set_title("previous chapter")
954 .set_title_star("title* unparsed")
955 .set_media_type(mime::TEXT_PLAIN);
956
957 let link_header = b"<http://example.com/TheBook/chapter2>; \
958 rel=\"previous\"; anchor=\"../anchor/example/\"; \
959 rev=\"next\"; hreflang=de; media=\"screen\"; \
960 title=\"previous chapter\"; title*=title* unparsed; \
961 type=\"text/plain\"";
962
963 let expected_link = Link::new(vec![link_value]);
964
965 let link = Header::parse_header(&vec![link_header.to_vec()].into());
966 assert_eq!(link.ok(), Some(expected_link));
967 }
968
969 #[test]
970 fn test_link_multiple_link_headers() {
971 let first_link = LinkValue::new("/TheBook/chapter2")
972 .push_rel(RelationType::Previous)
973 .set_title_star("UTF-8'de'letztes%20Kapitel");
974
975 let second_link = LinkValue::new("/TheBook/chapter4")
976 .push_rel(RelationType::Next)
977 .set_title_star("UTF-8'de'n%c3%a4chstes%20Kapitel");
978
979 let third_link = LinkValue::new("http://example.com/TheBook/chapter2")
980 .push_rel(RelationType::Previous)
981 .push_rev(RelationType::Next)
982 .set_title("previous chapter");
983
984 let expected_link = Link::new(vec![first_link, second_link, third_link]);
985
986 let mut raw = MockStream::with_input(b"GET /super_short_uri/and_whatever HTTP/1.1\r\nHost: \
987 hyper.rs\r\nAccept: a lot of things\r\nAccept-Charset: \
988 utf8\r\nAccept-Encoding: *\r\nLink: </TheBook/chapter2>; \
989 rel=\"previous\"; title*=UTF-8'de'letztes%20Kapitel, \
990 </TheBook/chapter4>; rel=\"next\"; title*=\
991 UTF-8'de'n%c3%a4chstes%20Kapitel\r\n\
992 Access-Control-Allow-Credentials: None\r\nLink: \
993 <http://example.com/TheBook/chapter2>; \
994 rel=\"previous\"; rev=next; title=\"previous chapter\"\
995 \r\n\r\n");
996
997 let mut buf = BufReader::new(&mut raw);
998 let res = parse_request(&mut buf).unwrap();
999
1000 let link = res.headers.get::<Link>().unwrap();
1001
1002 assert_eq!(*link, expected_link);
1003 }
1004
1005 #[test]
1006 fn test_link_display() {
1007 let link_value = LinkValue::new("http://example.com/TheBook/chapter2")
1008 .push_rel(RelationType::Previous)
1009 .set_anchor("/anchor/example/")
1010 .push_rev(RelationType::Next)
1011 .push_href_lang("de".parse().unwrap())
1012 .push_media_desc(MediaDesc::Screen)
1013 .set_title("previous chapter")
1014 .set_title_star("title* unparsed")
1015 .set_media_type(mime::TEXT_PLAIN);
1016
1017 let link = Link::new(vec![link_value]);
1018
1019 let mut link_header = String::new();
1020 write!(&mut link_header, "{}", link).unwrap();
1021
1022 let expected_link_header = "<http://example.com/TheBook/chapter2>; \
1023 rel=\"previous\"; anchor=\"/anchor/example/\"; \
1024 rev=\"next\"; hreflang=de; media=\"screen\"; \
1025 title=\"previous chapter\"; title*=title* unparsed; \
1026 type=\"text/plain\"";
1027
1028 assert_eq!(link_header, expected_link_header);
1029 }
1030
1031 #[test]
1032 fn test_link_parsing_errors() {
1033 let link_a = b"http://example.com/TheBook/chapter2; \
1034 rel=\"previous\"; rev=next; title=\"previous chapter\"";
1035
1036 let mut err: Result<Link, _> = Header::parse_header(&vec![link_a.to_vec()].into());
1037 assert_eq!(err.is_err(), true);
1038
1039 let link_b = b"<http://example.com/TheBook/chapter2>; \
1040 =\"previous\"; rev=next; title=\"previous chapter\"";
1041
1042 err = Header::parse_header(&vec![link_b.to_vec()].into());
1043 assert_eq!(err.is_err(), true);
1044
1045 let link_c = b"<http://example.com/TheBook/chapter2>; \
1046 rel=; rev=next; title=\"previous chapter\"";
1047
1048 err = Header::parse_header(&vec![link_c.to_vec()].into());
1049 assert_eq!(err.is_err(), true);
1050
1051 let link_d = b"<http://example.com/TheBook/chapter2>; \
1052 rel=\"previous\"; rev=next; title=";
1053
1054 err = Header::parse_header(&vec![link_d.to_vec()].into());
1055 assert_eq!(err.is_err(), true);
1056
1057 let link_e = b"<http://example.com/TheBook/chapter2>; \
1058 rel=\"previous\"; rev=next; attr=unknown";
1059
1060 err = Header::parse_header(&vec![link_e.to_vec()].into());
1061 assert_eq!(err.is_err(), true);
1062 }
1063
1064 #[test]
1065 fn test_link_split_ascii_unquoted_iterator() {
1066 let string = "some, text; \"and, more; in quotes\", or not";
1067 let mut string_split = SplitAsciiUnquoted::new(string, ";,");
1068
1069 assert_eq!(Some("some"), string_split.next());
1070 assert_eq!(Some(" text"), string_split.next());
1071 assert_eq!(Some(" \"and, more; in quotes\""), string_split.next());
1072 assert_eq!(Some(" or not"), string_split.next());
1073 assert_eq!(None, string_split.next());
1074 }
1075
1076 #[test]
1077 fn test_link_fmt_delimited() {
1078 struct TestFormatterStruct<'a> { v: Vec<&'a str> };
1079
1080 impl<'a> fmt::Display for TestFormatterStruct<'a> {
1081 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1082 fmt_delimited(f, self.v.as_slice(), ", ", (">>", "<<"))
1083 }
1084 }
1085
1086 let test_formatter = TestFormatterStruct { v: vec!["first", "second"] };
1087
1088 let mut string = String::new();
1089 write!(&mut string, "{}", test_formatter).unwrap();
1090
1091 let expected_string = ">>first, second<<";
1092
1093 assert_eq!(string, expected_string);
1094 }
1095
1096 #[test]
1097 fn test_link_verify_and_trim() {
1098 let string = verify_and_trim("> some string <", (b'>', b'<'));
1099 assert_eq!(string.ok(), Some("some string"));
1100
1101 let err = verify_and_trim(" > some string <", (b'>', b'<'));
1102 assert_eq!(err.is_err(), true);
1103 }
1104}
1105
1106bench_header!(bench_link, Link, { vec![b"<http://example.com/TheBook/chapter2>; rel=\"previous\"; rev=next; title=\"previous chapter\"; type=\"text/html\"; media=\"screen, tty\"".to_vec()] });