hyperx/header/common/
link.rs

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