1#![allow(clippy::upper_case_acronyms)]
2
3use crate::{Error, Error::*, Result};
4use std::fmt;
5use std::fmt::{Display, Formatter};
6use std::str::FromStr;
7
8use digest::{Digest, DynDigest};
9use md5::Md5;
10use sha2::{Sha256, Sha512_256};
11use std::borrow::Cow;
12
13#[derive(Debug, PartialEq, Clone, Copy)]
15#[allow(non_camel_case_types)]
16pub enum AlgorithmType {
17 MD5,
18 SHA2_256,
19 SHA2_512_256,
20}
21
22#[derive(Debug, PartialEq, Clone, Copy)]
24pub struct Algorithm {
25 pub algo: AlgorithmType,
26 pub sess: bool,
27}
28
29impl Algorithm {
30 pub fn new(algo: AlgorithmType, sess: bool) -> Algorithm {
32 Algorithm { algo, sess }
33 }
34
35 pub fn hash(self, bytes: &[u8]) -> String {
37 let mut hash: Box<dyn DynDigest> = match self.algo {
38 AlgorithmType::MD5 => Box::new(Md5::new()),
39 AlgorithmType::SHA2_256 => Box::new(Sha256::new()),
40 AlgorithmType::SHA2_512_256 => Box::new(Sha512_256::new()),
41 };
42
43 hash.update(bytes);
44 hex::encode(hash.finalize())
45 }
46
47 pub fn hash_str(self, bytes: &str) -> String {
49 self.hash(bytes.as_bytes())
50 }
51}
52
53impl FromStr for Algorithm {
54 type Err = Error;
55
56 fn from_str(s: &str) -> Result<Self> {
58 match s {
59 "MD5" => Ok(Algorithm::new(AlgorithmType::MD5, false)),
60 "MD5-sess" => Ok(Algorithm::new(AlgorithmType::MD5, true)),
61 "SHA-256" => Ok(Algorithm::new(AlgorithmType::SHA2_256, false)),
62 "SHA-256-sess" => Ok(Algorithm::new(AlgorithmType::SHA2_256, true)),
63 "SHA-512-256" => Ok(Algorithm::new(AlgorithmType::SHA2_512_256, false)),
64 "SHA-512-256-sess" => Ok(Algorithm::new(AlgorithmType::SHA2_512_256, true)),
65 _ => Err(UnknownAlgorithm(s.into())),
66 }
67 }
68}
69
70impl Default for Algorithm {
71 fn default() -> Self {
73 Algorithm::new(AlgorithmType::MD5, false)
74 }
75}
76
77impl Display for Algorithm {
78 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
80 f.write_str(match self.algo {
81 AlgorithmType::MD5 => "MD5",
82 AlgorithmType::SHA2_256 => "SHA-256",
83 AlgorithmType::SHA2_512_256 => "SHA-512-256",
84 })?;
85
86 if self.sess {
87 f.write_str("-sess")?;
88 }
89
90 Ok(())
91 }
92}
93
94#[derive(Debug, PartialEq, Clone, Copy)]
96#[allow(non_camel_case_types)]
97pub enum Qop {
98 AUTH,
99 AUTH_INT,
100}
101
102impl FromStr for Qop {
103 type Err = Error;
104
105 fn from_str(s: &str) -> Result<Self> {
107 match s {
108 "auth" => Ok(Qop::AUTH),
109 "auth-int" => Ok(Qop::AUTH_INT),
110 _ => Err(BadQop(s.into())),
111 }
112 }
113}
114
115impl Display for Qop {
116 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
117 f.write_str(match self {
118 Qop::AUTH => "auth",
119 Qop::AUTH_INT => "auth-int",
120 })
121 }
122}
123
124#[derive(Debug)]
125#[allow(non_camel_case_types)]
126pub enum QopAlgo<'a> {
127 NONE,
128 AUTH,
129 AUTH_INT(&'a [u8]),
130}
131
132impl<'a> From<QopAlgo<'a>> for Option<Qop> {
134 fn from(algo: QopAlgo<'a>) -> Self {
135 match algo {
136 QopAlgo::NONE => None,
137 QopAlgo::AUTH => Some(Qop::AUTH),
138 QopAlgo::AUTH_INT(_) => Some(Qop::AUTH_INT),
139 }
140 }
141}
142
143#[derive(Debug, PartialEq, Clone)]
145pub enum Charset {
146 ASCII,
147 UTF8,
148}
149
150impl FromStr for Charset {
151 type Err = Error;
152
153 fn from_str(s: &str) -> Result<Self> {
155 match s {
156 "UTF-8" => Ok(Charset::UTF8),
157 _ => Err(BadCharset(s.into())),
158 }
159 }
160}
161
162impl Display for Charset {
163 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
164 f.write_str(match self {
165 Charset::ASCII => "ASCII",
166 Charset::UTF8 => "UTF-8",
167 })
168 }
169}
170
171#[derive(Debug, PartialEq, Clone)]
173pub struct HttpMethod<'a>(pub Cow<'a, str>);
174
175impl<'a> HttpMethod<'a> {
177 pub const GET : Self = HttpMethod(Cow::Borrowed("GET"));
178 pub const POST : Self = HttpMethod(Cow::Borrowed("POST"));
179 pub const PUT : Self = HttpMethod(Cow::Borrowed("PUT"));
180 pub const DELETE : Self = HttpMethod(Cow::Borrowed("DELETE"));
181 pub const HEAD : Self = HttpMethod(Cow::Borrowed("HEAD"));
182 pub const OPTIONS : Self = HttpMethod(Cow::Borrowed("OPTIONS"));
183 pub const CONNECT : Self = HttpMethod(Cow::Borrowed("CONNECT"));
184 pub const PATCH : Self = HttpMethod(Cow::Borrowed("PATCH"));
185 pub const TRACE : Self = HttpMethod(Cow::Borrowed("TRACE"));
186}
187
188impl<'a> Default for HttpMethod<'a> {
189 fn default() -> Self {
190 HttpMethod::GET
191 }
192}
193
194impl<'a> Display for HttpMethod<'a> {
195 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
197 f.write_str(&self.0)
198 }
199}
200
201impl<'a> From<&'a str> for HttpMethod<'a> {
202 fn from(s: &'a str) -> Self {
203 Self(s.into())
204 }
205}
206
207impl<'a> From<&'a [u8]> for HttpMethod<'a> {
208 fn from(s: &'a [u8]) -> Self {
209 Self(String::from_utf8_lossy(s).into())
210 }
211}
212
213impl<'a> From<String> for HttpMethod<'a> {
214 fn from(s: String) -> Self {
215 Self(s.into())
216 }
217}
218
219impl<'a> From<Cow<'a, str>> for HttpMethod<'a> {
220 fn from(s: Cow<'a, str>) -> Self {
221 Self(s)
222 }
223}
224
225#[cfg(feature = "http")]
226impl From<http::Method> for HttpMethod<'static> {
227 fn from(method: http::Method) -> Self {
228 match method.as_str() {
229 "GET" => Self::GET,
231 "POST" => Self::POST,
232 "PUT" => Self::PUT,
233 "DELETE" => Self::DELETE,
234 "HEAD" => Self::HEAD,
235 "OPTIONS" => Self::OPTIONS,
236 "CONNECT" => Self::CONNECT,
237 "PATCH" => Self::PATCH,
238 "TRACE" => Self::TRACE,
239 other => Self(other.to_owned().into())
241 }
242 }
243}
244
245#[cfg(feature = "http")]
246impl<'a> From<&'a http::Method> for HttpMethod<'a> {
247 fn from(method: &'a http::Method) -> HttpMethod<'a> {
248 Self(method.as_str().into())
249 }
250}
251
252#[cfg(test)]
253mod test {
254 use crate::error::Error::{BadCharset, BadQop, UnknownAlgorithm};
255 use crate::{Algorithm, AlgorithmType, Charset, HttpMethod, Qop, QopAlgo};
256 use std::borrow::Cow;
257 use std::str::FromStr;
258
259 #[test]
260 fn test_algorithm_type() {
261 assert_eq!(
263 Ok(Algorithm::new(AlgorithmType::MD5, false)),
264 Algorithm::from_str("MD5")
265 );
266 assert_eq!(
267 Ok(Algorithm::new(AlgorithmType::MD5, true)),
268 Algorithm::from_str("MD5-sess")
269 );
270 assert_eq!(
271 Ok(Algorithm::new(AlgorithmType::SHA2_256, false)),
272 Algorithm::from_str("SHA-256")
273 );
274 assert_eq!(
275 Ok(Algorithm::new(AlgorithmType::SHA2_256, true)),
276 Algorithm::from_str("SHA-256-sess")
277 );
278 assert_eq!(
279 Ok(Algorithm::new(AlgorithmType::SHA2_512_256, false)),
280 Algorithm::from_str("SHA-512-256")
281 );
282 assert_eq!(
283 Ok(Algorithm::new(AlgorithmType::SHA2_512_256, true)),
284 Algorithm::from_str("SHA-512-256-sess")
285 );
286 assert_eq!(
287 Err(UnknownAlgorithm("OTHER_ALGORITHM".to_string())),
288 Algorithm::from_str("OTHER_ALGORITHM")
289 );
290
291 assert_eq!(
293 "MD5".to_string(),
294 Algorithm::new(AlgorithmType::MD5, false).to_string()
295 );
296 assert_eq!(
297 "MD5-sess".to_string(),
298 Algorithm::new(AlgorithmType::MD5, true).to_string()
299 );
300 assert_eq!(
301 "SHA-256".to_string(),
302 Algorithm::new(AlgorithmType::SHA2_256, false).to_string()
303 );
304 assert_eq!(
305 "SHA-256-sess".to_string(),
306 Algorithm::new(AlgorithmType::SHA2_256, true).to_string()
307 );
308 assert_eq!(
309 "SHA-512-256".to_string(),
310 Algorithm::new(AlgorithmType::SHA2_512_256, false).to_string()
311 );
312 assert_eq!(
313 "SHA-512-256-sess".to_string(),
314 Algorithm::new(AlgorithmType::SHA2_512_256, true).to_string()
315 );
316
317 assert_eq!(
319 Algorithm::new(AlgorithmType::MD5, false),
320 Default::default()
321 );
322
323 assert_eq!(
325 "e2fc714c4727ee9395f324cd2e7f331f".to_string(),
326 Algorithm::new(AlgorithmType::MD5, false).hash("abcd".as_bytes())
327 );
328
329 assert_eq!(
330 "e2fc714c4727ee9395f324cd2e7f331f".to_string(),
331 Algorithm::new(AlgorithmType::MD5, false).hash_str("abcd")
332 );
333
334 assert_eq!(
335 "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589".to_string(),
336 Algorithm::new(AlgorithmType::SHA2_256, false).hash("abcd".as_bytes())
337 );
338
339 assert_eq!(
340 "d2891c7978be0e24948f37caa415b87cb5cbe2b26b7bad9dc6391b8a6f6ddcc9".to_string(),
341 Algorithm::new(AlgorithmType::SHA2_512_256, false).hash("abcd".as_bytes())
342 );
343 }
344
345 #[test]
346 fn test_qop() {
347 assert_eq!(Ok(Qop::AUTH), Qop::from_str("auth"));
348 assert_eq!(Ok(Qop::AUTH_INT), Qop::from_str("auth-int"));
349 assert_eq!(Err(BadQop("banana".to_string())), Qop::from_str("banana"));
350
351 assert_eq!("auth".to_string(), Qop::AUTH.to_string());
352 assert_eq!("auth-int".to_string(), Qop::AUTH_INT.to_string());
353 }
354
355 #[test]
356 fn test_qop_algo() {
357 assert_eq!(Option::<Qop>::None, QopAlgo::NONE.into());
358 assert_eq!(Some(Qop::AUTH), QopAlgo::AUTH.into());
359 assert_eq!(
360 Some(Qop::AUTH_INT),
361 QopAlgo::AUTH_INT("foo".as_bytes()).into()
362 );
363 }
364
365 #[test]
366 fn test_charset() {
367 assert_eq!(Ok(Charset::UTF8), Charset::from_str("UTF-8"));
368 assert_eq!(Err(BadCharset("ASCII".into())), Charset::from_str("ASCII"));
369
370 assert_eq!("UTF-8".to_string(), Charset::UTF8.to_string());
371 assert_eq!("ASCII".to_string(), Charset::ASCII.to_string());
372 }
373
374 #[test]
375 fn test_http_method() {
376 assert_eq!(HttpMethod::GET, "GET".into());
378 assert_eq!(HttpMethod::POST, "POST".into());
379 assert_eq!(HttpMethod::PUT, "PUT".into());
380 assert_eq!(HttpMethod::DELETE, "DELETE".into());
381 assert_eq!(HttpMethod::HEAD, "HEAD".into());
382 assert_eq!(HttpMethod::OPTIONS, "OPTIONS".into());
383 assert_eq!(HttpMethod::CONNECT, "CONNECT".into());
384 assert_eq!(HttpMethod::PATCH, "PATCH".into());
385 assert_eq!(HttpMethod::TRACE, "TRACE".into());
386 assert_eq!(HttpMethod::GET, "GET".as_bytes().into());
388 assert_eq!(
389 HttpMethod(Cow::Borrowed("ěščř")),
390 "ěščř".as_bytes().into()
391 );
392 assert_eq!(
393 HttpMethod(Cow::Owned("AB�".to_string())), (&[65u8, 66, 156][..]).into()
395 );
396 assert_eq!(HttpMethod::GET, String::from("GET").into());
398 assert_eq!(
400 HttpMethod(Cow::Borrowed("NonsenseMethod")),
401 "NonsenseMethod".into()
402 );
403 assert_eq!(
404 HttpMethod(Cow::Owned("NonsenseMethod".to_string())),
405 "NonsenseMethod".to_string().into()
406 );
407 assert_eq!(HttpMethod::HEAD, Cow::Borrowed("HEAD").into());
409 assert_eq!(
410 HttpMethod(Cow::Borrowed("NonsenseMethod")),
411 Cow::Borrowed("NonsenseMethod").into()
412 );
413 assert_eq!("GET".to_string(), HttpMethod::GET.to_string());
415 assert_eq!("POST".to_string(), HttpMethod::POST.to_string());
416 assert_eq!("PUT".to_string(), HttpMethod::PUT.to_string());
417 assert_eq!("DELETE".to_string(), HttpMethod::DELETE.to_string());
418 assert_eq!("HEAD".to_string(), HttpMethod::HEAD.to_string());
419 assert_eq!("OPTIONS".to_string(), HttpMethod::OPTIONS.to_string());
420 assert_eq!("CONNECT".to_string(), HttpMethod::CONNECT.to_string());
421 assert_eq!("PATCH".to_string(), HttpMethod::PATCH.to_string());
422 assert_eq!("TRACE".to_string(), HttpMethod::TRACE.to_string());
423
424 assert_eq!(
425 "NonsenseMethod".to_string(),
426 HttpMethod(Cow::Borrowed("NonsenseMethod")).to_string()
427 );
428 assert_eq!(
429 "NonsenseMethod".to_string(),
430 HttpMethod(Cow::Owned("NonsenseMethod".to_string())).to_string()
431 );
432 }
433
434 #[cfg(feature = "http")]
435 #[test]
436 fn test_http_crate() {
437 assert_eq!(HttpMethod::GET, http::Method::GET.clone().into());
438 assert_eq!(
439 HttpMethod(Cow::Owned("BANANA".to_string())),
440 http::Method::from_str("BANANA").unwrap().into()
441 );
442
443 assert_eq!(HttpMethod::GET, (&http::Method::GET).into());
444 let x = http::Method::from_str("BANANA").unwrap();
445 assert_eq!(
446 HttpMethod(Cow::Borrowed("BANANA")),
447 (&x).into()
448 );
449 }
450}