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