mime_parse_4/
lib.rs

1//! Internal types for the `mime` crate.
2//!
3//! Nothing to see here. Move along.
4
5use std::error::Error;
6use std::{fmt, slice};
7
8pub mod constants;
9mod rfc7231;
10
11use self::constants::Atoms;
12use self::sealed::Sealed;
13
14pub struct Parser {
15    can_range: bool,
16}
17
18#[derive(Clone)]
19pub struct Mime {
20    source: Source,
21    slash: u16,
22    plus: Option<u16>,
23    params: ParamSource,
24}
25
26#[derive(Clone)]
27pub enum Source {
28    Atom(u8, &'static str),
29    Dynamic(String),
30}
31
32impl AsRef<str> for Source {
33    fn as_ref(&self) -> &str {
34        match *self {
35            Source::Atom(_, s) => s,
36            Source::Dynamic(ref s) => s,
37        }
38    }
39}
40
41type Indexed = (u16, u16);
42type IndexedPair = (Indexed, Indexed);
43
44#[derive(Clone)]
45pub enum ParamSource {
46    None,
47    Utf8(u16),
48    One(u16, IndexedPair),
49    Two(u16, IndexedPair, IndexedPair),
50    Custom(u16, Vec<IndexedPair>),
51}
52
53pub enum InternParams {
54    Utf8(usize),
55    None,
56}
57
58#[derive(Debug)]
59pub enum ParseError {
60    MissingSlash,
61    MissingEqual,
62    MissingQuote,
63    InvalidToken {
64        pos: usize,
65        byte: Byte,
66    },
67    InvalidRange,
68    TooLong,
69}
70
71#[derive(Clone, Copy)]
72pub struct Byte(u8);
73
74impl fmt::Debug for Byte {
75    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76        match self.0 {
77            b'\n' => f.write_str("'\\n'"),
78            b'\r' => f.write_str("'\\r'"),
79            b'\t' => f.write_str("'\\t'"),
80            b'\\' => f.write_str("'\\'"),
81            b'\0' => f.write_str("'\\0'"),
82            0x20..=0x7f => write!(f, "'{}'", self.0 as char),
83            _ => write!(f, "'\\x{:02x}'", self.0),
84        }
85    }
86}
87
88impl Error for ParseError {
89}
90
91impl fmt::Display for ParseError {
92    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93        let description = match self {
94            ParseError::MissingSlash => "a slash (/) was missing between the type and subtype",
95            ParseError::MissingEqual => "an equals sign (=) was missing between a parameter and its value",
96            ParseError::MissingQuote => "a quote (\") was missing from a parameter value",
97            ParseError::InvalidToken { .. } => "invalid token",
98            ParseError::InvalidRange => "unexpected asterisk",
99            ParseError::TooLong => "the string is too long",
100        };
101        if let ParseError::InvalidToken { pos, byte } = *self {
102            write!(f, "{}, {:?} at position {}", description, byte, pos)
103        } else {
104            f.write_str(description)
105        }
106    }
107}
108
109// ===== impl Mime =====
110
111impl Mime {
112    #[inline]
113    pub fn type_(&self) -> &str {
114        &self.source.as_ref()[..self.slash as usize]
115    }
116
117    #[inline]
118    pub fn subtype(&self) -> &str {
119        let end = self.semicolon_or_end();
120        &self.source.as_ref()[self.slash as usize + 1..end]
121    }
122
123    #[doc(hidden)]
124    pub fn private_subtype_offset(&self) -> u16 {
125        self.slash
126    }
127
128    #[inline]
129    pub fn suffix(&self) -> Option<&str> {
130        let end = self.semicolon_or_end();
131        self.plus.map(|idx| &self.source.as_ref()[idx as usize + 1..end])
132    }
133
134    #[doc(hidden)]
135    pub fn private_suffix_offset(&self) -> Option<u16> {
136        self.plus
137    }
138
139    #[inline]
140    pub fn params(&self) -> Params {
141        let inner = match self.params {
142            ParamSource::Utf8(_) => ParamsInner::Utf8,
143            ParamSource::One(_, a) => ParamsInner::Inlined(&self.source, Inline::One(a)),
144            ParamSource::Two(_, a, b) => ParamsInner::Inlined(&self.source, Inline::Two(a, b)),
145            ParamSource::Custom(_, ref params) => {
146                ParamsInner::Custom {
147                    source: &self.source,
148                    params: params.iter(),
149                }
150            }
151            ParamSource::None => ParamsInner::None,
152        };
153
154        Params(inner)
155    }
156
157    #[doc(hidden)]
158    pub fn private_params_source(&self) -> &ParamSource {
159        &self.params
160    }
161
162    pub fn param<'a>(&'a self, attr: &str) -> Option<&'a str> {
163        self.params().find(|e| attr == e.0).map(|e| e.1)
164    }
165
166    #[inline]
167    pub fn has_params(&self) -> bool {
168        self.semicolon().is_some()
169    }
170
171    #[inline]
172    pub fn without_params(self) -> Self {
173        let semicolon = match self.semicolon() {
174            None => return self,
175            Some(i) => i,
176        };
177
178        let mut mtype = self;
179        mtype.params = ParamSource::None;
180        mtype.source = Atoms::intern(
181            &mtype.source.as_ref()[..semicolon],
182            mtype.slash,
183            InternParams::None,
184        );
185        mtype
186    }
187
188    #[inline]
189    fn semicolon(&self) -> Option<usize> {
190        match self.params {
191            ParamSource::Utf8(i) |
192            ParamSource::One(i, ..) |
193            ParamSource::Two(i, ..) |
194            ParamSource::Custom(i, _) => Some(i as usize),
195            ParamSource::None => None,
196        }
197    }
198
199    #[inline]
200    fn semicolon_or_end(&self) -> usize {
201        self.semicolon().unwrap_or_else(|| self.source.as_ref().len())
202    }
203
204    #[doc(hidden)]
205    pub fn private_atom(&self) -> u8 {
206        self.atom()
207    }
208
209    fn atom(&self) -> u8 {
210        match self.source {
211            Source::Atom(a, _) => a,
212            Source::Dynamic(_) => 0,
213        }
214    }
215
216    pub fn essence(&self) -> &str {
217        &self.source.as_ref()[..self.semicolon_or_end()]
218    }
219
220    #[doc(hidden)]
221    pub const unsafe fn private_from_proc_macro(
222        source: Source,
223        slash: u16,
224        plus: Option<u16>,
225        params: ParamSource,
226    ) -> Mime {
227        Mime {
228            source,
229            slash,
230            plus,
231            params,
232        }
233    }
234}
235
236impl AsRef<str> for Mime {
237    #[inline]
238    fn as_ref(&self) -> &str {
239        self.source.as_ref()
240    }
241}
242
243impl fmt::Debug for Mime {
244    #[inline]
245    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246        fmt::Debug::fmt(self.source.as_ref(), f)
247    }
248}
249
250impl fmt::Display for Mime {
251    #[inline]
252    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
253        fmt::Display::fmt(self.source.as_ref(), f)
254    }
255}
256
257#[inline]
258fn as_u16(i: usize) -> u16 {
259    debug_assert!(i <= std::u16::MAX as usize, "as_u16 overflow");
260    i as u16
261}
262
263#[inline]
264fn range(index: (u16, u16)) -> std::ops::Range<usize> {
265    index.0 as usize .. index.1 as usize
266}
267
268// ===== impl Parser =====
269
270impl Parser {
271    #[inline]
272    pub fn can_range() -> Self {
273        Parser {
274            can_range: true,
275        }
276    }
277
278    #[inline]
279    pub fn cannot_range() -> Self {
280        Parser {
281            can_range: false,
282        }
283    }
284
285    pub fn parse(&self, src: impl Parse) -> Result<Mime, ParseError> {
286        rfc7231::parse(self, src)
287    }
288}
289
290
291fn lower_ascii_with_params(s: &str, semi: usize, params: &[IndexedPair]) -> String {
292    let mut owned = s.to_owned();
293    owned[..semi].make_ascii_lowercase();
294
295    for &(name, value) in params {
296        owned[range(name)].make_ascii_lowercase();
297        // Since we just converted this part of the string to lowercase,
298        // we can skip the `Name == &str` unicase check and do a faster
299        // memcmp instead.
300        if &owned[range(name)] == "charset" {
301            owned[range(value)].make_ascii_lowercase();
302        }
303    }
304
305    owned
306}
307
308
309// Params ===================
310
311
312enum ParamsInner<'a> {
313    Utf8,
314    Inlined(&'a Source, Inline),
315    Custom {
316        source: &'a Source,
317        params: slice::Iter<'a, IndexedPair>,
318    },
319    None,
320}
321
322
323enum Inline {
324    Done,
325    One(IndexedPair),
326    Two(IndexedPair, IndexedPair),
327}
328
329/// An iterator over the parameters of a MIME.
330pub struct Params<'a>(ParamsInner<'a>);
331
332impl<'a> fmt::Debug for Params<'a> {
333    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
334        fmt.debug_struct("Params").finish()
335    }
336}
337
338impl<'a> Iterator for Params<'a> {
339    type Item = (&'a str, &'a str);
340
341    #[inline]
342    fn next(&mut self) -> Option<Self::Item> {
343        match self.0 {
344            ParamsInner::Utf8 => {
345                let value = ("charset", "utf-8");
346                self.0 = ParamsInner::None;
347                Some(value)
348            },
349            ParamsInner::Inlined(source, ref mut inline) => {
350                let next = match *inline {
351                    Inline::Done => {
352                        None
353                    }
354                    Inline::One(one) => {
355                        *inline = Inline::Done;
356                        Some(one)
357                    },
358                    Inline::Two(one, two) => {
359                        *inline = Inline::One(two);
360                        Some(one)
361                    },
362                };
363                next.map(|(name, value)| {
364                    let name = &source.as_ref()[range(name)];
365                    let value = &source.as_ref()[range(value)];
366                    (name, value)
367                })
368            },
369            ParamsInner::Custom { source, ref mut params } => {
370                params.next().map(|&(name, value)| {
371                    let name = &source.as_ref()[range(name)];
372                    let value = &source.as_ref()[range(value)];
373                    (name, value)
374                })
375            },
376            ParamsInner::None => None,
377        }
378    }
379
380    #[inline]
381    fn size_hint(&self) -> (usize, Option<usize>) {
382        match self.0 {
383            ParamsInner::Utf8 => (1, Some(1)),
384            ParamsInner::Inlined(_, Inline::Done) => (0, Some(0)),
385            ParamsInner::Inlined(_, Inline::One(..)) => (1, Some(1)),
386            ParamsInner::Inlined(_, Inline::Two(..)) => (2, Some(2)),
387            ParamsInner::Custom { ref params, .. } => params.size_hint(),
388            ParamsInner::None => (0, Some(0)),
389        }
390    }
391}
392
393mod sealed {
394    pub trait Sealed {
395        fn as_str(&self) -> &str;
396    }
397}
398
399pub trait Parse: Sealed {}
400
401impl<'a> Sealed for &'a str {
402    fn as_str(&self) -> &str {
403        self
404    }
405}
406
407impl<'a> Parse for &'a str {}
408
409impl<'a> Sealed for &'a String {
410    fn as_str(&self) -> &str {
411        *self
412    }
413}
414
415impl<'a> Parse for &'a String {}
416