mco_http/header/common/
link.rs

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/// The `Link` header, defined in
13/// [RFC5988](http://tools.ietf.org/html/rfc5988#section-5)
14///
15/// # ABNF
16/// ```plain
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/// use mco_http::header::{Headers, Link, LinkValue, RelationType};
59///
60/// let link_value = LinkValue::new("http://example.com/TheBook/chapter2")
61///     .push_rel(RelationType::Previous)
62///     .set_title("previous chapter");
63///
64/// let mut headers = Headers::new();
65/// headers.set(
66///     Link::new(vec![link_value])
67/// );
68/// ```
69#[derive(Clone, PartialEq, Debug)]
70pub struct Link {
71    /// A list of the `link-value`s of the Link entity-header.
72    values: Vec<LinkValue>
73}
74
75/// A single `link-value` of a `Link` header, based on:
76/// [RFC5988](http://tools.ietf.org/html/rfc5988#section-5)
77#[derive(Clone, PartialEq, Debug)]
78pub struct LinkValue {
79    /// Target IRI: `link-value`.
80    link: Cow<'static, str>,
81
82    /// Forward Relation Types: `rel`.
83    rel: Option<Vec<RelationType>>,
84
85    /// Context IRI: `anchor`.
86    anchor: Option<String>,
87
88    /// Reverse Relation Types: `rev`.
89    rev: Option<Vec<RelationType>>,
90
91    /// Hint on the language of the result of dereferencing
92    /// the link: `hreflang`.
93    href_lang: Option<Vec<LanguageTag>>,
94
95    /// Destination medium or media: `media`.
96    media_desc: Option<Vec<MediaDesc>>,
97
98    /// Label of the destination of a Link: `title`.
99    title: Option<String>,
100
101    /// The `title` encoded in a different charset: `title*`.
102    title_star: Option<String>,
103
104    /// Hint on the media type of the result of dereferencing
105    /// the link: `type`.
106    media_type: Option<Mime>,
107}
108
109/// A Media Descriptors Enum based on:
110/// https://www.w3.org/TR/html401/types.html#h-6.13
111#[derive(Clone, PartialEq, Debug)]
112pub enum MediaDesc {
113    /// screen.
114    Screen,
115    /// tty.
116    Tty,
117    /// tv.
118    Tv,
119    /// projection.
120    Projection,
121    /// handheld.
122    Handheld,
123    /// print.
124    Print,
125    /// braille.
126    Braille,
127    /// aural.
128    Aural,
129    /// all.
130    All,
131    /// Unrecognized media descriptor extension.
132    Extension(String)
133}
134
135/// A Link Relation Type Enum based on:
136/// [RFC5988](https://tools.ietf.org/html/rfc5988#section-6.2.2)
137#[derive(Clone, PartialEq, Debug)]
138pub enum RelationType {
139    /// alternate.
140    Alternate,
141    /// appendix.
142    Appendix,
143    /// bookmark.
144    Bookmark,
145    /// chapter.
146    Chapter,
147    /// contents.
148    Contents,
149    /// copyright.
150    Copyright,
151    /// current.
152    Current,
153    /// describedby.
154    DescribedBy,
155    /// edit.
156    Edit,
157    /// edit-media.
158    EditMedia,
159    /// enclosure.
160    Enclosure,
161    /// first.
162    First,
163    /// glossary.
164    Glossary,
165    /// help.
166    Help,
167    /// hub.
168    Hub,
169    /// index.
170    Index,
171    /// last.
172    Last,
173    /// latest-version.
174    LatestVersion,
175    /// license.
176    License,
177    /// next.
178    Next,
179    /// next-archive.
180    NextArchive,
181    /// payment.
182    Payment,
183    /// prev.
184    Prev,
185    /// predecessor-version.
186    PredecessorVersion,
187    /// previous.
188    Previous,
189    /// prev-archive.
190    PrevArchive,
191    /// related.
192    Related,
193    /// replies.
194    Replies,
195    /// section.
196    Section,
197    /// self.
198    RelationTypeSelf,
199    /// service.
200    Service,
201    /// start.
202    Start,
203    /// stylesheet.
204    Stylesheet,
205    /// subsection.
206    Subsection,
207    /// successor-version.
208    SuccessorVersion,
209    /// up.
210    Up,
211    /// versionHistory.
212    VersionHistory,
213    /// via.
214    Via,
215    /// working-copy.
216    WorkingCopy,
217    /// working-copy-of.
218    WorkingCopyOf,
219    /// ext-rel-type.
220    ExtRelType(String)
221}
222
223////////////////////////////////////////////////////////////////////////////////
224// Struct methods
225////////////////////////////////////////////////////////////////////////////////
226
227impl Link {
228    /// Create `Link` from a `Vec<LinkValue>`.
229    pub fn new(link_values: Vec<LinkValue>) -> Link {
230        Link { values: link_values }
231    }
232
233    /// Get the `Link` header's `LinkValue`s.
234    pub fn values(&self) -> &[LinkValue] {
235        self.values.as_ref()
236    }
237
238    /// Add a `LinkValue` instance to the `Link` header's values.
239    pub fn push_value(&mut self, link_value: LinkValue) {
240        self.values.push(link_value);
241    }
242}
243
244impl LinkValue {
245    /// Create `LinkValue` from URI-Reference.
246    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    /// Get the `LinkValue`'s value.
262    pub fn link(&self) -> &str {
263        self.link.as_ref()
264    }
265
266    /// Get the `LinkValue`'s `rel` parameter(s).
267    pub fn rel(&self) -> Option<&[RelationType]> {
268        self.rel.as_ref().map(AsRef::as_ref)
269    }
270
271    /// Get the `LinkValue`'s `anchor` parameter.
272    pub fn anchor(&self) -> Option<&str> {
273        self.anchor.as_ref().map(AsRef::as_ref)
274    }
275
276    /// Get the `LinkValue`'s `rev` parameter(s).
277    pub fn rev(&self) -> Option<&[RelationType]> {
278        self.rev.as_ref().map(AsRef::as_ref)
279    }
280
281    /// Get the `LinkValue`'s `hreflang` parameter(s).
282    pub fn href_lang(&self) -> Option<&[LanguageTag]> {
283        self.href_lang.as_ref().map(AsRef::as_ref)
284    }
285
286    /// Get the `LinkValue`'s `media` parameter(s).
287    pub fn media_desc(&self) -> Option<&[MediaDesc]> {
288        self.media_desc.as_ref().map(AsRef::as_ref)
289    }
290
291    /// Get the `LinkValue`'s `title` parameter.
292    pub fn title(&self) -> Option<&str> {
293        self.title.as_ref().map(AsRef::as_ref)
294    }
295
296    /// Get the `LinkValue`'s `title*` parameter.
297    pub fn title_star(&self) -> Option<&str> {
298        self.title_star.as_ref().map(AsRef::as_ref)
299    }
300
301    /// Get the `LinkValue`'s `type` parameter.
302    pub fn media_type(&self) -> Option<&Mime> {
303        self.media_type.as_ref()
304    }
305
306    /// Add a `RelationType` to the `LinkValue`'s `rel` parameter.
307    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    /// Set `LinkValue`'s `anchor` parameter.
318    pub fn set_anchor<T: Into<String>>(mut self, anchor: T) -> LinkValue {
319        self.anchor = Some(anchor.into());
320
321        self
322    }
323
324    /// Add a `RelationType` to the `LinkValue`'s `rev` parameter.
325    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    /// Add a `LanguageTag` to the `LinkValue`'s `hreflang` parameter.
336    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    /// Add a `MediaDesc` to the `LinkValue`'s `media_desc` parameter.
347    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    /// Set `LinkValue`'s `title` parameter.
358    pub fn set_title<T: Into<String>>(mut self, title: T) -> LinkValue {
359        self.title = Some(title.into());
360
361        self
362    }
363
364    /// Set `LinkValue`'s `title*` parameter.
365    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    /// Set `LinkValue`'s `type` parameter.
372    pub fn set_media_type(mut self, media_type: Mime) -> LinkValue {
373        self.media_type = Some(media_type);
374
375        self
376    }
377}
378
379////////////////////////////////////////////////////////////////////////////////
380// Trait implementations
381////////////////////////////////////////////////////////////////////////////////
382
383impl 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        // If more that one `Link` headers are present in a request's
391        // headers they are combined in a single `Link` header containing
392        // all the `link-value`s present in each of those `Link` headers.
393        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        // Create a split iterator with delimiters: `;`, `,`
460        let link_split = SplitAsciiUnquoted::new(s, ";,");
461
462        let mut link_values: Vec<LinkValue> = Vec::new();
463
464        // Loop over the splits parsing the Link header into
465        // a `Vec<LinkValue>`
466        for segment in link_split {
467            // Parse the `Target IRI`
468            // https://tools.ietf.org/html/rfc5988#section-5.1
469            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                // Parse the current link-value's parameters
490                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                    // Parse relation type: `rel`.
504                    // https://tools.ietf.org/html/rfc5988#section-5.3
505                    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                    // Parse the `Context IRI`.
521                    // https://tools.ietf.org/html/rfc5988#section-5.2
522                    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                    // Parse relation type: `rev`.
532                    // https://tools.ietf.org/html/rfc5988#section-5.3
533                    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                    // Parse target attribute: `hreflang`.
549                    // https://tools.ietf.org/html/rfc5988#section-5.4
550                    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                    // Parse target attribute: `media`.
566                    // https://tools.ietf.org/html/rfc5988#section-5.4
567                    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                    // Parse target attribute: `title`.
583                    // https://tools.ietf.org/html/rfc5988#section-5.4
584                    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                    // 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 => 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                    // 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 => 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
808////////////////////////////////////////////////////////////////////////////////
809// Utilities
810////////////////////////////////////////////////////////////////////////////////
811
812struct 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                // Ignore `c` if we're `in_quotes`.
842                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 a starting string `b.0` before the first element
861        write!(f, "{}{}", b.0, p[0])?;
862
863        for i in &p[1..] {
864            // Write the next element preceded by the delimiter `d`
865            write!(f, "{}{}", d, i)?;
866        }
867
868        // Write a ending string `b.1` before the first element
869        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    // Verify that `s` starts with `b.0` and ends with `b.1` and return
880    // the contained substring after triming whitespace.
881    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////////////////////////////////////////////////////////////////////////////////
891// Tests
892////////////////////////////////////////////////////////////////////////////////
893
894#[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()] });