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,
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 "html" => Some(HTML),
57 "js" | "mjs" | "jsonp" => Some(JAVASCRIPT),
58 "json" => Some(JSON),
59 "css" => Some(CSS),
60 "svg" => Some(SVG),
61 "xml" => Some(XML),
62 _ => None,
63 }
64 }
65
66 pub fn basetype(&self) -> &str {
71 &self.basetype
72 }
73
74 pub fn subtype(&self) -> &str {
76 &self.subtype
77 }
78
79 pub fn essence(&self) -> &str {
81 &self.essence
82 }
83
84 pub fn param(&self, name: impl Into<ParamName>) -> Option<&ParamValue> {
86 let name: ParamName = name.into();
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)
92 }
93
94 pub fn remove_param(&mut self, name: impl Into<ParamName>) -> Option<ParamValue> {
96 let name: ParamName = name.into();
97 if name.as_str() == "charset" && self.is_utf8 {
98 self.is_utf8 = false;
99 return Some(ParamValue(Cow::Borrowed("utf-8")));
100 }
101 self.params
102 .iter()
103 .position(|(k, _)| k == &name)
104 .map(|pos| self.params.remove(pos).1)
105 }
106}
107
108impl Display for Mime {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 parse::format(self, f)
111 }
112}
113
114impl FromStr for Mime {
121 type Err = crate::Error;
122
123 fn from_str(s: &str) -> Result<Self, Self::Err> {
127 parse::parse(s)
128 }
129}
130
131impl<'a> From<&'a str> for Mime {
132 fn from(value: &'a str) -> Self {
133 Self::from_str(value).unwrap()
134 }
135}
136
137impl ToHeaderValues for Mime {
138 type Iter = option::IntoIter<HeaderValue>;
139
140 fn to_header_values(&self) -> crate::Result<Self::Iter> {
141 let mime = self.clone();
142 let header: HeaderValue = mime.into();
143
144 Ok(header.to_header_values().unwrap())
146 }
147}
148
149#[derive(Debug, Clone, PartialEq, Eq, Hash)]
151pub struct ParamName(Cow<'static, str>);
152
153impl ParamName {
154 pub fn as_str(&self) -> &str {
156 &self.0
157 }
158}
159
160impl Display for ParamName {
161 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
162 Display::fmt(&self.0, f)
163 }
164}
165
166impl FromStr for ParamName {
167 type Err = crate::Error;
168
169 fn from_str(s: &str) -> Result<Self, Self::Err> {
173 crate::ensure!(s.is_ascii(), "String slice should be valid ASCII");
174 Ok(ParamName(Cow::Owned(s.to_ascii_lowercase())))
175 }
176}
177
178impl<'a> From<&'a str> for ParamName {
179 fn from(value: &'a str) -> Self {
180 Self::from_str(value).unwrap()
181 }
182}
183
184#[derive(Debug, Clone, PartialEq, Eq, Hash)]
186pub struct ParamValue(Cow<'static, str>);
187
188impl ParamValue {
189 pub fn as_str(&self) -> &str {
191 &self.0
192 }
193}
194
195impl Display for ParamValue {
196 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197 Display::fmt(&self.0, f)
198 }
199}
200
201impl<'a> PartialEq<&'a str> for ParamValue {
202 fn eq(&self, other: &&'a str) -> bool {
203 &self.0 == other
204 }
205}
206
207impl PartialEq<str> for ParamValue {
208 fn eq(&self, other: &str) -> bool {
209 self.0 == other
210 }
211}