1use base64::{engine::general_purpose, Engine as _};
2use bytes::Bytes;
3use memchr::{memchr, memchr_iter};
4use rand::{distributions::Alphanumeric, thread_rng, Rng};
5use secrecy::SecretString;
6use std::collections::HashMap;
7
8pub fn generate_random_secret() -> SecretString {
9 let rand_string: String = thread_rng()
10 .sample_iter(&Alphanumeric)
11 .take(30)
12 .map(char::from)
13 .collect();
14
15 rand_string.into()
16}
17
18pub fn base64_encode(original: Vec<u8>) -> String {
19 general_purpose::STANDARD_NO_PAD.encode(original)
20}
21
22pub fn base64_decode(encoded: String) -> Result<Vec<u8>, anyhow::Error> {
23 Ok(general_purpose::STANDARD_NO_PAD.decode(encoded)?)
24}
25
26pub fn parse_query_string(query: &Bytes) -> Result<HashMap<String, String>, anyhow::Error> {
27 let mut map = HashMap::new();
28 if query.is_empty() {
29 return Ok(map);
30 }
31 let mut and_iter = memchr_iter(b'&', query);
33 let mut next_slice_start_at = 0;
34 let mut data_left = true;
35 while data_left {
36 let slice;
37 match and_iter.next() {
38 Some(next_and) => {
39 slice = query.slice(next_slice_start_at..next_and);
40 next_slice_start_at = next_and + 1;
41 }
42 None => {
43 slice = query.slice(next_slice_start_at..);
45 data_left = false;
46 }
47 }
48 if let Some(equal_i) = memchr(b'=', &slice) {
50 let key = String::from_utf8_lossy(&slice.slice(..equal_i)).to_string();
51 let value = String::from_utf8_lossy(&slice.slice(equal_i + 1..)).to_string();
52 map.insert(key, value);
53 } else {
54 return Err(anyhow::Error::msg("Invalid entry"));
55 }
56 }
57 Ok(map)
58}
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn test_query_string() {
65 let query_string = "flavor=vanilla&food=ice cream";
66 let query_bytes = Bytes::from_static(query_string.as_bytes());
67 let query = parse_query_string(&query_bytes).expect("Error Parsing Query String");
68 assert_eq!(
69 "vanilla",
70 query.get("flavor").expect("Flavor Missing in Map")
71 );
72 assert_eq!("ice cream", query.get("food").expect("Food Misisng in map"));
73 }
74}