Skip to content

Commit 9359daa

Browse files
committed
Simplify utf8 logic
1 parent 27950ea commit 9359daa

File tree

4 files changed

+72
-96
lines changed

4 files changed

+72
-96
lines changed

src/mime/constants.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use super::ParamKind;
21
use crate::Mime;
32
use std::borrow::Cow;
43

@@ -10,18 +9,18 @@ macro_rules! utf8_mime_const {
109
$desc,
1110
$base,
1211
$sub,
13-
Some(ParamKind::Utf8),
12+
true,
1413
";charset=utf-8"
1514
);
1615
};
1716
}
1817
macro_rules! mime_const {
1918
($name:ident, $desc:expr, $base:expr, $sub:expr) => {
20-
mime_const!(with_params, $name, $desc, $base, $sub, None, "");
19+
mime_const!(with_params, $name, $desc, $base, $sub, false, "");
2120
};
2221

23-
(with_params, $name:ident, $desc:expr, $base:expr, $sub:expr, $params:expr, $doccomment:expr) => {
24-
mime_const!(doc_expanded, $name, $desc, $base, $sub, $params,
22+
(with_params, $name:ident, $desc:expr, $base:expr, $sub:expr, $is_utf8:expr, $doccomment:expr) => {
23+
mime_const!(doc_expanded, $name, $desc, $base, $sub, $is_utf8,
2524
concat!(
2625
"Content-Type for ",
2726
$desc,
@@ -30,13 +29,14 @@ macro_rules! mime_const {
3029
);
3130
};
3231

33-
(doc_expanded, $name:ident, $desc:expr, $base:expr, $sub:expr, $params:expr, $doccomment:expr) => {
32+
(doc_expanded, $name:ident, $desc:expr, $base:expr, $sub:expr, $is_utf8:expr, $doccomment:expr) => {
3433
#[doc = $doccomment]
3534
pub const $name: Mime = Mime {
3635
essence: Cow::Borrowed(concat!($base, "/", $sub)),
3736
basetype: Cow::Borrowed($base),
3837
subtype: Cow::Borrowed($sub),
39-
params: $params,
38+
is_utf8: $is_utf8,
39+
params: vec![],
4040
};
4141
};
4242
}

src/mime/mod.rs

+22-54
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,15 @@ use infer::Infer;
2828
/// ```
2929
// NOTE: we cannot statically initialize Strings with values yet, so we keep dedicated static
3030
// fields for the static strings.
31-
#[derive(Clone, Eq)]
31+
#[derive(Clone, PartialEq, Eq, Debug)]
3232
pub struct Mime {
3333
pub(crate) essence: Cow<'static, str>,
3434
pub(crate) basetype: Cow<'static, str>,
3535
pub(crate) subtype: Cow<'static, str>,
36-
pub(crate) params: Option<ParamKind>,
36+
// NOTE(yosh): this is a hack because we can't populate vecs in const yet.
37+
// This enables us to encode media types as utf-8 at compilation.
38+
pub(crate) is_utf8: bool,
39+
pub(crate) params: Vec<(ParamName, ParamValue)>,
3740
}
3841

3942
impl Mime {
@@ -81,45 +84,24 @@ impl Mime {
8184
/// Get a reference to a param.
8285
pub fn param(&self, name: impl Into<ParamName>) -> Option<&ParamValue> {
8386
let name: ParamName = name.into();
84-
self.params
85-
.as_ref()
86-
.map(|inner| match inner {
87-
ParamKind::Vec(v) => v
88-
.iter()
89-
.find_map(|(k, v)| if k == &name { Some(v) } else { None }),
90-
ParamKind::Utf8 => match name {
91-
ParamName(Cow::Borrowed("charset")) => Some(&ParamValue(Cow::Borrowed("utf8"))),
92-
_ => None,
93-
},
94-
})
95-
.flatten()
87+
if name.as_str() == "charset" && self.is_utf8 {
88+
return Some(&ParamValue(Cow::Borrowed("utf-8")));
89+
}
90+
91+
self.params.iter().find(|(k, _)| k == &name).map(|(_, v)| v)
9692
}
9793

9894
/// Remove a param from the set. Returns the `ParamValue` if it was contained within the set.
9995
pub fn remove_param(&mut self, name: impl Into<ParamName>) -> Option<ParamValue> {
10096
let name: ParamName = name.into();
101-
let mut unset_params = false;
102-
let ret = self
103-
.params
104-
.as_mut()
105-
.map(|inner| match inner {
106-
ParamKind::Vec(v) => match v.iter().position(|(k, _)| k == &name) {
107-
Some(index) => Some(v.remove(index).1),
108-
None => None,
109-
},
110-
ParamKind::Utf8 => match name {
111-
ParamName(Cow::Borrowed("charset")) => {
112-
unset_params = true;
113-
Some(ParamValue(Cow::Borrowed("utf8")))
114-
}
115-
_ => None,
116-
},
117-
})
118-
.flatten();
119-
if unset_params {
120-
self.params = None;
97+
if name.as_str() == "charset" && self.is_utf8 {
98+
self.is_utf8 = false;
99+
return Some(ParamValue(Cow::Borrowed("utf-8")));
121100
}
122-
ret
101+
self.params
102+
.iter()
103+
.position(|(k, _)| k == &name)
104+
.map(|pos| self.params.remove(pos).1)
123105
}
124106
}
125107

@@ -129,11 +111,11 @@ impl Display for Mime {
129111
}
130112
}
131113

132-
impl Debug for Mime {
133-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134-
Debug::fmt(&self.essence, f)
135-
}
136-
}
114+
// impl Debug for Mime {
115+
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116+
// Debug::fmt(&self.essence, f)
117+
// }
118+
// }
137119

138120
impl FromStr for Mime {
139121
type Err = crate::Error;
@@ -164,12 +146,6 @@ impl ToHeaderValues for Mime {
164146
}
165147
}
166148

167-
impl PartialEq<Mime> for Mime {
168-
fn eq(&self, other: &Mime) -> bool {
169-
self.essence == other.essence
170-
}
171-
}
172-
173149
/// A parameter name.
174150
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
175151
pub struct ParamName(Cow<'static, str>);
@@ -233,11 +209,3 @@ impl PartialEq<str> for ParamValue {
233209
self.0 == other
234210
}
235211
}
236-
237-
/// This is a hack that allows us to mark a trait as utf8 during compilation. We
238-
/// can remove this once we can construct HashMap during compilation.
239-
#[derive(Debug, Clone, PartialEq, Eq)]
240-
pub(crate) enum ParamKind {
241-
Utf8,
242-
Vec(Vec<(ParamName, ParamValue)>),
243-
}

src/mime/parse.rs

+32-33
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::borrow::Cow;
22
use std::fmt;
33

4-
use super::{Mime, ParamKind, ParamName, ParamValue};
4+
use super::{Mime, ParamName, ParamValue};
55

66
/// Parse a string into a mime type.
77
/// Follows the [WHATWG MIME parsing algorithm](https://mimesniff.spec.whatwg.org/#parsing-a-mime-type)
@@ -41,7 +41,8 @@ pub(crate) fn parse(input: &str) -> crate::Result<Mime> {
4141
// 10.
4242
let basetype = basetype.to_ascii_lowercase();
4343
let subtype = subtype.to_ascii_lowercase();
44-
let mut params = None;
44+
let mut params = vec![];
45+
let mut is_utf8 = false;
4546

4647
// 11.
4748
let mut input = input;
@@ -92,13 +93,14 @@ pub(crate) fn parse(input: &str) -> crate::Result<Mime> {
9293
};
9394

9495
// 10.
95-
if !parameter_name.is_empty()
96+
if parameter_name == "charset" && parameter_value == "utf-8" {
97+
is_utf8 = true;
98+
} else if !parameter_name.is_empty()
9699
&& parameter_name.chars().all(is_http_token_code_point)
97100
&& parameter_value
98101
.chars()
99102
.all(is_http_quoted_string_token_code_point)
100103
{
101-
let params = params.get_or_insert_with(Vec::new);
102104
let name = ParamName(parameter_name.into());
103105
let value = ParamValue(parameter_value.into());
104106
if !params.iter().any(|(k, _)| k == &name) {
@@ -111,7 +113,8 @@ pub(crate) fn parse(input: &str) -> crate::Result<Mime> {
111113
essence: Cow::Owned(format!("{}/{}", &basetype, &subtype)),
112114
basetype: basetype.into(),
113115
subtype: subtype.into(),
114-
params: params.map(ParamKind::Vec),
116+
params,
117+
is_utf8,
115118
})
116119
}
117120

@@ -204,34 +207,30 @@ fn collect_http_quoted_string(mut input: &str) -> (String, &str) {
204207
/// Implementation of [WHATWG MIME serialization algorithm](https://mimesniff.spec.whatwg.org/#serializing-a-mime-type)
205208
pub(crate) fn format(mime_type: &Mime, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206209
write!(f, "{}", &mime_type.essence)?;
207-
if let Some(params) = &mime_type.params {
208-
match params {
209-
ParamKind::Utf8 => write!(f, ";charset=utf-8")?,
210-
ParamKind::Vec(params) => {
211-
for (name, value) in params {
212-
if value.0.chars().all(is_http_token_code_point) && !value.0.is_empty() {
213-
write!(f, ";{}={}", name, value)?;
214-
} else {
215-
write!(
216-
f,
217-
";{}=\"{}\"",
218-
name,
219-
value
220-
.0
221-
.chars()
222-
.flat_map(|c| match c {
223-
'"' | '\\' => EscapeMimeValue {
224-
state: EscapeMimeValueState::Backslash(c)
225-
},
226-
c => EscapeMimeValue {
227-
state: EscapeMimeValueState::Char(c)
228-
},
229-
})
230-
.collect::<String>()
231-
)?;
232-
}
233-
}
234-
}
210+
if mime_type.is_utf8 {
211+
write!(f, ";charset=utf-8")?;
212+
}
213+
for (name, value) in mime_type.params.iter() {
214+
if value.0.chars().all(is_http_token_code_point) && !value.0.is_empty() {
215+
write!(f, ";{}={}", name, value)?;
216+
} else {
217+
write!(
218+
f,
219+
";{}=\"{}\"",
220+
name,
221+
value
222+
.0
223+
.chars()
224+
.flat_map(|c| match c {
225+
'"' | '\\' => EscapeMimeValue {
226+
state: EscapeMimeValueState::Backslash(c)
227+
},
228+
c => EscapeMimeValue {
229+
state: EscapeMimeValueState::Char(c)
230+
},
231+
})
232+
.collect::<String>()
233+
)?;
235234
}
236235
}
237236
Ok(())

tests/mime.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
#[cfg(features = "fs")]
1+
// #[cfg(features = "fs")]
22
mod tests {
33
use async_std::fs;
44
use async_std::io;
5-
use http_types::{mime, Body, Response};
5+
use http_types::{mime, Body, Request, Response};
66

77
#[async_std::test]
88
async fn guess_plain_text_mime() -> io::Result<()> {
@@ -44,4 +44,13 @@ mod tests {
4444
assert_eq!(res.content_type(), Some(mime::BYTE_STREAM));
4545
Ok(())
4646
}
47+
48+
// #[test]
49+
// fn match_mime_types() {
50+
// let req = Request::get("https://example.com");
51+
// match req.content_type() {
52+
// Some(mime::JSON) => {}
53+
// _ => {}
54+
// }
55+
// }
4756
}

0 commit comments

Comments
 (0)