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