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