1use crate::ZInt;
15use core::fmt;
16use std::{borrow::Cow, convert::TryFrom};
17
18mod consts {
19 pub(super) const MIMES: [&str; 21] = [
20 "",
21 "application/octet-stream",
22 "application/custom", "text/plain",
24 "application/properties", "application/json", "application/sql",
27 "application/integer", "application/float", "application/xml", "application/xhtml+xml",
32 "application/x-www-form-urlencoded",
33 "text/json", "text/html",
35 "text/xml", "text/css",
37 "text/csv",
38 "text/javascript",
39 "image/jpeg",
40 "image/png",
41 "image/gif",
42 ];
43}
44
45#[repr(u8)]
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub enum KnownEncoding {
48 Empty = 0,
49 AppOctetStream = 1,
50 AppCustom = 2,
51 TextPlain = 3,
52 AppProperties = 4,
53 AppJson = 5,
54 AppSql = 6,
55 AppInteger = 7,
56 AppFloat = 8,
57 AppXml = 9,
58 AppXhtmlXml = 10,
59 AppXWwwFormUrlencoded = 11,
60 TextJson = 12,
61 TextHtml = 13,
62 TextXml = 14,
63 TextCss = 15,
64 TextCsv = 16,
65 TextJavascript = 17,
66 ImageJpeg = 18,
67 ImagePng = 19,
68 ImageGif = 20,
69}
70
71impl From<KnownEncoding> for u8 {
72 fn from(val: KnownEncoding) -> Self {
73 unsafe { std::mem::transmute(val) }
74 }
75}
76
77impl From<KnownEncoding> for &str {
78 fn from(val: KnownEncoding) -> Self {
79 consts::MIMES[usize::from(val)]
80 }
81}
82
83impl From<KnownEncoding> for usize {
84 fn from(val: KnownEncoding) -> Self {
85 u8::from(val) as usize
86 }
87}
88
89impl std::convert::TryFrom<u8> for KnownEncoding {
90 type Error = ();
91 fn try_from(value: u8) -> Result<Self, Self::Error> {
92 if value < consts::MIMES.len() as u8 + 1 {
93 Ok(unsafe { std::mem::transmute(value) })
94 } else {
95 Err(())
96 }
97 }
98}
99
100impl std::convert::TryFrom<ZInt> for KnownEncoding {
101 type Error = ();
102
103 fn try_from(value: ZInt) -> Result<Self, Self::Error> {
104 if value < consts::MIMES.len() as ZInt + 1 {
105 Ok(unsafe { std::mem::transmute(value as u8) })
106 } else {
107 Err(())
108 }
109 }
110}
111
112impl AsRef<str> for KnownEncoding {
113 fn as_ref(&self) -> &str {
114 consts::MIMES[usize::from(*self)]
115 }
116}
117#[derive(Clone, Debug, PartialEq, Eq)]
122pub enum Encoding {
123 Exact(KnownEncoding),
124 WithSuffix(KnownEncoding, Cow<'static, str>),
125}
126
127impl Encoding {
128 pub fn new<IntoCowStr>(prefix: ZInt, suffix: IntoCowStr) -> Option<Self>
129 where
130 IntoCowStr: Into<Cow<'static, str>> + AsRef<str>,
131 {
132 let prefix = KnownEncoding::try_from(prefix).ok()?;
133 if suffix.as_ref().is_empty() {
134 Some(Encoding::Exact(prefix))
135 } else {
136 Some(Encoding::WithSuffix(prefix, suffix.into()))
137 }
138 }
139
140 pub fn with_suffix<IntoCowStr>(self, suffix: IntoCowStr) -> Self
142 where
143 IntoCowStr: Into<Cow<'static, str>>,
144 {
145 match self {
146 Encoding::Exact(e) => Encoding::WithSuffix(e, suffix.into()),
147 Encoding::WithSuffix(e, s) => {
148 Encoding::WithSuffix(e, Cow::Owned(format!("{}{}", s, suffix.into())))
149 }
150 }
151 }
152
153 pub fn as_ref<'a, T>(&'a self) -> T
154 where
155 &'a Self: Into<T>,
156 {
157 self.into()
158 }
159
160 pub fn starts_with<T>(&self, with: T) -> bool
163 where
164 T: Into<Encoding>,
165 {
166 let with: Encoding = with.into();
167 self.prefix() == with.prefix() && self.suffix().starts_with(with.suffix())
168 }
169
170 pub const fn prefix(&self) -> &KnownEncoding {
171 match self {
172 Encoding::Exact(e) | Encoding::WithSuffix(e, _) => e,
173 }
174 }
175
176 pub fn suffix(&self) -> &str {
177 match self {
178 Encoding::Exact(_) => "",
179 Encoding::WithSuffix(_, s) => s.as_ref(),
180 }
181 }
182}
183
184impl Encoding {
185 pub const EMPTY: Encoding = Encoding::Exact(KnownEncoding::Empty);
186 pub const APP_OCTET_STREAM: Encoding = Encoding::Exact(KnownEncoding::AppOctetStream);
187 pub const APP_CUSTOM: Encoding = Encoding::Exact(KnownEncoding::AppCustom);
188 pub const TEXT_PLAIN: Encoding = Encoding::Exact(KnownEncoding::TextPlain);
189 pub const APP_PROPERTIES: Encoding = Encoding::Exact(KnownEncoding::AppProperties);
190 pub const APP_JSON: Encoding = Encoding::Exact(KnownEncoding::AppJson);
191 pub const APP_SQL: Encoding = Encoding::Exact(KnownEncoding::AppSql);
192 pub const APP_INTEGER: Encoding = Encoding::Exact(KnownEncoding::AppInteger);
193 pub const APP_FLOAT: Encoding = Encoding::Exact(KnownEncoding::AppFloat);
194 pub const APP_XML: Encoding = Encoding::Exact(KnownEncoding::AppXml);
195 pub const APP_XHTML_XML: Encoding = Encoding::Exact(KnownEncoding::AppXhtmlXml);
196 pub const APP_XWWW_FORM_URLENCODED: Encoding =
197 Encoding::Exact(KnownEncoding::AppXWwwFormUrlencoded);
198 pub const TEXT_JSON: Encoding = Encoding::Exact(KnownEncoding::TextJson);
199 pub const TEXT_HTML: Encoding = Encoding::Exact(KnownEncoding::TextHtml);
200 pub const TEXT_XML: Encoding = Encoding::Exact(KnownEncoding::TextXml);
201 pub const TEXT_CSS: Encoding = Encoding::Exact(KnownEncoding::TextCss);
202 pub const TEXT_CSV: Encoding = Encoding::Exact(KnownEncoding::TextCsv);
203 pub const TEXT_JAVASCRIPT: Encoding = Encoding::Exact(KnownEncoding::TextJavascript);
204 pub const IMAGE_JPEG: Encoding = Encoding::Exact(KnownEncoding::ImageJpeg);
205 pub const IMAGE_PNG: Encoding = Encoding::Exact(KnownEncoding::ImagePng);
206 pub const IMAGE_GIF: Encoding = Encoding::Exact(KnownEncoding::ImageGif);
207}
208
209impl fmt::Display for Encoding {
210 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
211 match self {
212 Encoding::Exact(e) => f.write_str(e.as_ref()),
213 Encoding::WithSuffix(e, s) => {
214 f.write_str(e.as_ref())?;
215 f.write_str(s)
216 }
217 }
218 }
219}
220
221impl From<&'static str> for Encoding {
222 fn from(s: &'static str) -> Self {
223 for (i, v) in consts::MIMES.iter().enumerate().skip(1) {
224 if let Some(suffix) = s.strip_prefix(v) {
225 if suffix.is_empty() {
226 return Encoding::Exact(unsafe { std::mem::transmute(i as u8) });
227 } else {
228 return Encoding::WithSuffix(
229 unsafe { std::mem::transmute(i as u8) },
230 suffix.into(),
231 );
232 }
233 }
234 }
235 if s.is_empty() {
236 Encoding::Exact(KnownEncoding::Empty)
237 } else {
238 Encoding::WithSuffix(KnownEncoding::Empty, s.into())
239 }
240 }
241}
242
243impl From<String> for Encoding {
244 fn from(mut s: String) -> Self {
245 for (i, v) in consts::MIMES.iter().enumerate().skip(1) {
246 if s.starts_with(v) {
247 s.replace_range(..v.len(), "");
248 if s.is_empty() {
249 return Encoding::Exact(unsafe { std::mem::transmute(i as u8) });
250 } else {
251 return Encoding::WithSuffix(unsafe { std::mem::transmute(i as u8) }, s.into());
252 }
253 }
254 }
255 if s.is_empty() {
256 Encoding::Exact(KnownEncoding::Empty)
257 } else {
258 Encoding::WithSuffix(KnownEncoding::Empty, s.into())
259 }
260 }
261}
262
263impl From<&KnownEncoding> for Encoding {
264 fn from(e: &KnownEncoding) -> Encoding {
265 Encoding::Exact(*e)
266 }
267}
268
269impl From<KnownEncoding> for Encoding {
270 fn from(e: KnownEncoding) -> Encoding {
271 Encoding::Exact(e)
272 }
273}
274
275impl Default for Encoding {
276 fn default() -> Self {
277 KnownEncoding::Empty.into()
278 }
279}