1mod constants;
6mod parse;
7
8pub use constants::*;
9
10use std::borrow::Cow;
11use std::fmt::{self, Debug, Display};
12use std::option;
13use std::str::FromStr;
14
15use crate::headers::{HeaderValue, ToHeaderValues};
16
17use infer::Infer;
18
19#[derive(Clone, PartialEq, Eq, Debug)]
32pub struct Mime {
33 pub(crate) essence: Cow<'static, str>,
34 pub(crate) basetype: Cow<'static, str>,
35 pub(crate) subtype: Cow<'static, str>,
36 pub(crate) is_utf8: bool,
39 pub(crate) params: Vec<(ParamName, ParamValue)>,
40}
41
42impl Mime {
43 pub fn sniff(bytes: &[u8]) -> crate::Result<Self> {
45 let info = Infer::new();
46 let mime = match info.get(bytes) {
47 Some(info) => info.mime_type(),
48 None => crate::bail!("Could not sniff the mime type"),
49 };
50 Mime::from_str(mime)
51 }
52
53 pub fn from_extension(extension: impl AsRef<str>) -> Option<Self> {
55 match extension.as_ref() {
56 "7z" => Some(SEVENZIP),
57 "atom" => Some(ATOM),
58 "avi" => Some(AVI),
59 "bin" | "exe" | "dll" | "iso" | "img" => Some(BYTE_STREAM),
60 "bmp" => Some(BMP),
61 "css" => Some(CSS),
62 "html" => Some(HTML),
63 "ico" => Some(ICO),
64 "js" | "mjs" | "jsonp" => Some(JAVASCRIPT),
65 "json" => Some(JSON),
66 "m4a" => Some(M4A),
67 "mid" | "midi" | "kar" => Some(MIDI),
68 "mp3" => Some(MP3),
69 "mp4" => Some(MP4),
70 "mpeg" | "mpg" => Some(MPEG),
71 "ogg" => Some(OGG),
72 "otf" => Some(OTF),
73 "rss" => Some(RSS),
74 "svg" | "svgz" => Some(SVG),
75 "ttf" => Some(TTF),
76 "txt" => Some(PLAIN),
77 "wasm" => Some(WASM),
78 "webm" => Some(WEBM),
79 "webp" => Some(WEBP),
80 "woff" => Some(WOFF),
81 "woff2" => Some(WOFF2),
82 "xml" => Some(XML),
83 "zip" => Some(ZIP),
84 _ => None,
85 }
86 }
87
88 pub fn basetype(&self) -> &str {
93 &self.basetype
94 }
95
96 pub fn subtype(&self) -> &str {
98 &self.subtype
99 }
100
101 pub fn essence(&self) -> &str {
103 &self.essence
104 }
105
106 pub fn param(&self, name: impl Into<ParamName>) -> Option<&ParamValue> {
108 let name: ParamName = name.into();
109 if name.as_str() == "charset" && self.is_utf8 {
110 return Some(&ParamValue(Cow::Borrowed("utf-8")));
111 }
112
113 self.params.iter().find(|(k, _)| k == &name).map(|(_, v)| v)
114 }
115
116 pub fn remove_param(&mut self, name: impl Into<ParamName>) -> Option<ParamValue> {
118 let name: ParamName = name.into();
119 if name.as_str() == "charset" && self.is_utf8 {
120 self.is_utf8 = false;
121 return Some(ParamValue(Cow::Borrowed("utf-8")));
122 }
123 self.params
124 .iter()
125 .position(|(k, _)| k == &name)
126 .map(|pos| self.params.remove(pos).1)
127 }
128
129 pub fn subset_eq(&self, other: &Mime) -> bool {
150 if other.basetype() != "*" && self.basetype() != other.basetype() {
151 return false;
152 }
153 if other.subtype() != "*" && self.subtype() != other.subtype() {
154 return false;
155 }
156 for (name, value) in other.params.iter() {
157 if !self
158 .param(name.clone())
159 .map(|v| v == value)
160 .unwrap_or(false)
161 {
162 return false;
163 }
164 }
165 true
166 }
167}
168
169impl Display for Mime {
170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 parse::format(self, f)
172 }
173}
174
175impl FromStr for Mime {
182 type Err = crate::Error;
183
184 fn from_str(s: &str) -> Result<Self, Self::Err> {
188 parse::parse(s)
189 }
190}
191
192impl<'a> From<&'a str> for Mime {
193 fn from(value: &'a str) -> Self {
194 Self::from_str(value).unwrap()
195 }
196}
197
198impl ToHeaderValues for Mime {
199 type Iter = option::IntoIter<HeaderValue>;
200
201 fn to_header_values(&self) -> crate::Result<Self::Iter> {
202 let mime = self.clone();
203 let header: HeaderValue = mime.into();
204
205 Ok(header.to_header_values().unwrap())
207 }
208}
209
210#[derive(Debug, Clone, PartialEq, Eq, Hash)]
212pub struct ParamName(Cow<'static, str>);
213
214impl ParamName {
215 pub fn as_str(&self) -> &str {
217 &self.0
218 }
219}
220
221impl Display for ParamName {
222 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
223 Display::fmt(&self.0, f)
224 }
225}
226
227impl FromStr for ParamName {
228 type Err = crate::Error;
229
230 fn from_str(s: &str) -> Result<Self, Self::Err> {
234 crate::ensure!(s.is_ascii(), "String slice should be valid ASCII");
235 Ok(ParamName(Cow::Owned(s.to_ascii_lowercase())))
236 }
237}
238
239impl<'a> From<&'a str> for ParamName {
240 fn from(value: &'a str) -> Self {
241 Self::from_str(value).unwrap()
242 }
243}
244
245#[derive(Debug, Clone, PartialEq, Eq, Hash)]
247pub struct ParamValue(Cow<'static, str>);
248
249impl ParamValue {
250 pub fn as_str(&self) -> &str {
252 &self.0
253 }
254}
255
256impl Display for ParamValue {
257 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258 Display::fmt(&self.0, f)
259 }
260}
261
262impl<'a> PartialEq<&'a str> for ParamValue {
263 fn eq(&self, other: &&'a str) -> bool {
264 &self.0 == other
265 }
266}
267
268impl PartialEq<str> for ParamValue {
269 fn eq(&self, other: &str) -> bool {
270 self.0 == other
271 }
272}