hyper_sync/header/common/
link.rs

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/// The `Link` header, defined in
14/// [RFC5988](http://tools.ietf.org/html/rfc5988#section-5)
15///
16/// # ABNF
17///
18/// ```text
19/// Link           = "Link" ":" #link-value
20/// link-value     = "<" URI-Reference ">" *( ";" link-param )
21/// link-param     = ( ( "rel" "=" relation-types )
22///                | ( "anchor" "=" <"> URI-Reference <"> )
23///                | ( "rev" "=" relation-types )
24///                | ( "hreflang" "=" Language-Tag )
25///                | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) )
26///                | ( "title" "=" quoted-string )
27///                | ( "title*" "=" ext-value )
28///                | ( "type" "=" ( media-type | quoted-mt ) )
29///                | ( link-extension ) )
30/// link-extension = ( parmname [ "=" ( ptoken | quoted-string ) ] )
31///                | ( ext-name-star "=" ext-value )
32/// ext-name-star  = parmname "*" ; reserved for RFC2231-profiled
33/// ; extensions.  Whitespace NOT
34/// ; allowed in between.
35/// ptoken         = 1*ptokenchar
36/// ptokenchar     = "!" | "#" | "$" | "%" | "&" | "'" | "("
37///                | ")" | "*" | "+" | "-" | "." | "/" | DIGIT
38///                | ":" | "<" | "=" | ">" | "?" | "@" | ALPHA
39///                | "[" | "]" | "^" | "_" | "`" | "{" | "|"
40///                | "}" | "~"
41/// media-type     = type-name "/" subtype-name
42/// quoted-mt      = <"> media-type <">
43/// relation-types = relation-type
44///                | <"> relation-type *( 1*SP relation-type ) <">
45/// relation-type  = reg-rel-type | ext-rel-type
46/// reg-rel-type   = LOALPHA *( LOALPHA | DIGIT | "." | "-" )
47/// ext-rel-type   = URI
48/// ```
49///
50/// # Example values
51///
52/// `Link: <http://example.com/TheBook/chapter2>; rel="previous";
53///        title="previous chapter"`
54///
55/// `Link: </TheBook/chapter2>; rel="previous"; title*=UTF-8'de'letztes%20Kapitel,
56///        </TheBook/chapter4>; rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel`
57///
58/// # Examples
59///
60/// ```
61/// use hyper_sync::header::{Headers, Link, LinkValue, RelationType};
62///
63/// let link_value = LinkValue::new("http://example.com/TheBook/chapter2")
64///     .push_rel(RelationType::Previous)
65///     .set_title("previous chapter");
66///
67/// let mut headers = Headers::new();
68/// headers.set(
69///     Link::new(vec![link_value])
70/// );
71/// ```
72#[derive(Clone, PartialEq, Debug)]
73pub struct Link {
74    /// A list of the `link-value`s of the Link entity-header.
75    values: Vec<LinkValue>
76}
77
78/// A single `link-value` of a `Link` header, based on:
79/// [RFC5988](http://tools.ietf.org/html/rfc5988#section-5)
80#[derive(Clone, PartialEq, Debug)]
81pub struct LinkValue {
82    /// Target IRI: `link-value`.
83    link: Cow<'static, str>,
84
85    /// Forward Relation Types: `rel`.
86    rel: Option<Vec<RelationType>>,
87
88    /// Context IRI: `anchor`.
89    anchor: Option<String>,
90
91    /// Reverse Relation Types: `rev`.
92    rev: Option<Vec<RelationType>>,
93
94    /// Hint on the language of the result of dereferencing
95    /// the link: `hreflang`.
96    href_lang: Option<Vec<LanguageTag>>,
97
98    /// Destination medium or media: `media`.
99    media_desc: Option<Vec<MediaDesc>>,
100
101    /// Label of the destination of a Link: `title`.
102    title: Option<String>,
103
104    /// The `title` encoded in a different charset: `title*`.
105    title_star: Option<String>,
106
107    /// Hint on the media type of the result of dereferencing
108    /// the link: `type`.
109    media_type: Option<Mime>,
110}
111
112/// A Media Descriptors Enum based on:
113/// [https://www.w3.org/TR/html401/types.html#h-6.13][url]
114///
115/// [url]: https://www.w3.org/TR/html401/types.html#h-6.13
116#[derive(Clone, PartialEq, Debug)]
117pub enum MediaDesc {
118    /// screen.
119    Screen,
120    /// tty.
121    Tty,
122    /// tv.
123    Tv,
124    /// projection.
125    Projection,
126    /// handheld.
127    Handheld,
128    /// print.
129    Print,
130    /// braille.
131    Braille,
132    /// aural.
133    Aural,
134    /// all.
135    All,
136    /// Unrecognized media descriptor extension.
137    Extension(String)
138}
139
140/// A Link Relation Type Enum based on:
141/// [RFC5988](https://tools.ietf.org/html/rfc5988#section-6.2.2)
142#[derive(Clone, PartialEq, Debug)]
143pub enum RelationType {
144    /// alternate.
145    Alternate,
146    /// appendix.
147    Appendix,
148    /// bookmark.
149    Bookmark,
150    /// chapter.
151    Chapter,
152    /// contents.
153    Contents,
154    /// copyright.
155    Copyright,
156    /// current.
157    Current,
158    /// describedby.
159    DescribedBy,
160    /// edit.
161    Edit,
162    /// edit-media.
163    EditMedia,
164    /// enclosure.
165    Enclosure,
166    /// first.
167    First,
168    /// glossary.
169    Glossary,
170    /// help.
171    Help,
172    /// hub.
173    Hub,
174    /// index.
175    Index,
176    /// last.
177    Last,
178    /// latest-version.
179    LatestVersion,
180    /// license.
181    License,
182    /// next.
183    Next,
184    /// next-archive.
185    NextArchive,
186    /// payment.
187    Payment,
188    /// prev.
189    Prev,
190    /// predecessor-version.
191    PredecessorVersion,
192    /// previous.
193    Previous,
194    /// prev-archive.
195    PrevArchive,
196    /// related.
197    Related,
198    /// replies.
199    Replies,
200    /// section.
201    Section,
202    /// self.
203    RelationTypeSelf,
204    /// service.
205    Service,
206    /// start.
207    Start,
208    /// stylesheet.
209    Stylesheet,
210    /// subsection.
211    Subsection,
212    /// successor-version.
213    SuccessorVersion,
214    /// up.
215    Up,
216    /// versionHistory.
217    VersionHistory,
218    /// via.
219    Via,
220    /// working-copy.
221    WorkingCopy,
222    /// working-copy-of.
223    WorkingCopyOf,
224    /// ext-rel-type.
225    ExtRelType(String)
226}
227
228////////////////////////////////////////////////////////////////////////////////
229// Struct methods
230////////////////////////////////////////////////////////////////////////////////
231
232impl Link {
233    /// Create `Link` from a `Vec<LinkValue>`.
234    pub fn new(link_values: Vec<LinkValue>) -> Link {
235        Link { values: link_values }
236    }
237
238    /// Get the `Link` header's `LinkValue`s.
239    pub fn values(&self) -> &[LinkValue] {
240        self.values.as_ref()
241    }
242
243    /// Add a `LinkValue` instance to the `Link` header's values.
244    pub fn push_value(&mut self, link_value: LinkValue) {
245        self.values.push(link_value);
246    }
247}
248
249impl LinkValue {
250    /// Create `LinkValue` from URI-Reference.
251    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    /// Get the `LinkValue`'s value.
267    pub fn link(&self) -> &str {
268        self.link.as_ref()
269    }
270
271    /// Get the `LinkValue`'s `rel` parameter(s).
272    pub fn rel(&self) -> Option<&[RelationType]> {
273        self.rel.as_ref().map(AsRef::as_ref)
274    }
275
276    /// Get the `LinkValue`'s `anchor` parameter.
277    pub fn anchor(&self) -> Option<&str> {
278        self.anchor.as_ref().map(AsRef::as_ref)
279    }
280
281    /// Get the `LinkValue`'s `rev` parameter(s).
282    pub fn rev(&self) -> Option<&[RelationType]> {
283        self.rev.as_ref().map(AsRef::as_ref)
284    }
285
286    /// Get the `LinkValue`'s `hreflang` parameter(s).
287    pub fn href_lang(&self) -> Option<&[LanguageTag]> {
288        self.href_lang.as_ref().map(AsRef::as_ref)
289    }
290
291    /// Get the `LinkValue`'s `media` parameter(s).
292    pub fn media_desc(&self) -> Option<&[MediaDesc]> {
293        self.media_desc.as_ref().map(AsRef::as_ref)
294    }
295
296    /// Get the `LinkValue`'s `title` parameter.
297    pub fn title(&self) -> Option<&str> {
298        self.title.as_ref().map(AsRef::as_ref)
299    }
300
301    /// Get the `LinkValue`'s `title*` parameter.
302    pub fn title_star(&self) -> Option<&str> {
303        self.title_star.as_ref().map(AsRef::as_ref)
304    }
305
306    /// Get the `LinkValue`'s `type` parameter.
307    pub fn media_type(&self) -> Option<&Mime> {
308        self.media_type.as_ref()
309    }
310
311    /// Add a `RelationType` to the `LinkValue`'s `rel` parameter.
312    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    /// Set `LinkValue`'s `anchor` parameter.
323    pub fn set_anchor<T: Into<String>>(mut self, anchor: T) -> LinkValue {
324        self.anchor = Some(anchor.into());
325
326        self
327    }
328
329    /// Add a `RelationType` to the `LinkValue`'s `rev` parameter.
330    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    /// Add a `LanguageTag` to the `LinkValue`'s `hreflang` parameter.
341    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    /// Add a `MediaDesc` to the `LinkValue`'s `media_desc` parameter.
352    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    /// Set `LinkValue`'s `title` parameter.
363    pub fn set_title<T: Into<String>>(mut self, title: T) -> LinkValue {
364        self.title = Some(title.into());
365
366        self
367    }
368
369    /// Set `LinkValue`'s `title*` parameter.
370    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    /// Set `LinkValue`'s `type` parameter.
377    pub fn set_media_type(mut self, media_type: Mime) -> LinkValue {
378        self.media_type = Some(media_type);
379
380        self
381    }
382}
383
384////////////////////////////////////////////////////////////////////////////////
385// Trait implementations
386////////////////////////////////////////////////////////////////////////////////
387
388impl 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        // If more that one `Link` headers are present in a request's
396        // headers they are combined in a single `Link` header containing
397        // all the `link-value`s present in each of those `Link` headers.
398        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        // Create a split iterator with delimiters: `;`, `,`
466        let link_split = SplitAsciiUnquoted::new(s, ";,");
467
468        let mut link_values: Vec<LinkValue> = Vec::new();
469
470        // Loop over the splits parsing the Link header into
471        // a `Vec<LinkValue>`
472        for segment in link_split {
473            // Parse the `Target IRI`
474            // https://tools.ietf.org/html/rfc5988#section-5.1
475            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                // Parse the current link-value's parameters
496                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                    // Parse relation type: `rel`.
510                    // https://tools.ietf.org/html/rfc5988#section-5.3
511                    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                    // Parse the `Context IRI`.
526                    // https://tools.ietf.org/html/rfc5988#section-5.2
527                    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                    // Parse relation type: `rev`.
536                    // https://tools.ietf.org/html/rfc5988#section-5.3
537                    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                    // Parse target attribute: `hreflang`.
552                    // https://tools.ietf.org/html/rfc5988#section-5.4
553                    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                    // Parse target attribute: `media`.
568                    // https://tools.ietf.org/html/rfc5988#section-5.4
569                    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                    // Parse target attribute: `title`.
584                    // https://tools.ietf.org/html/rfc5988#section-5.4
585                    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                    // Parse target attribute: `title*`.
596                    // https://tools.ietf.org/html/rfc5988#section-5.4
597                    //
598                    // Definition of `ext-value`:
599                    //       https://tools.ietf.org/html/rfc5987#section-3.2.1
600                    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                    // Parse target attribute: `type`.
608                    // https://tools.ietf.org/html/rfc5988#section-5.4
609                    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
806////////////////////////////////////////////////////////////////////////////////
807// Utilities
808////////////////////////////////////////////////////////////////////////////////
809
810struct 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                // Ignore `c` if we're `in_quotes`.
840                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        // Write a starting string `b.0` before the first element
859        try!(write!(f, "{}{}", b.0, p[0]));
860
861        for i in &p[1..] {
862            // Write the next element preceded by the delimiter `d`
863            try!(write!(f, "{}{}", d, i));
864        }
865
866        // Write a ending string `b.1` before the first element
867        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    // Verify that `s` starts with `b.0` and ends with `b.1` and return
878    // the contained substring after trimming whitespace.
879    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////////////////////////////////////////////////////////////////////////////////
889// Tests
890////////////////////////////////////////////////////////////////////////////////
891
892#[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()] });