1use 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
109impl 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
268impl 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 if &owned[range(name)] == "charset" {
301 owned[range(value)].make_ascii_lowercase();
302 }
303 }
304
305 owned
306}
307
308
309enum 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
329pub 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