gotham/helpers/http/request/
query_string.rs1use std::collections::HashMap;
4
5use crate::helpers::http::{form_url_decode, FormUrlDecoded};
6
7pub(crate) type QueryStringMapping = HashMap<String, Vec<FormUrlDecoded>>;
9
10pub(crate) fn split<'r>(query: Option<&'r str>) -> QueryStringMapping {
17 let mut query_string_mapping = QueryStringMapping::new();
18
19 if let Some(query) = query {
20 let pairs = query.split(is_separator).filter(|pair| pair.contains('='));
21
22 for p in pairs {
23 let mut sp = p.splitn(2, '=');
24 let (k, v) = (sp.next().unwrap(), sp.next().unwrap());
25
26 if let Ok(k) = form_url_decode(k) {
27 let vec = query_string_mapping.entry(k).or_default();
28 if let Some(dv) = FormUrlDecoded::new(v) {
29 vec.push(dv);
30 }
31 };
32 }
33 }
34
35 query_string_mapping
36}
37
38fn is_separator(c: char) -> bool {
39 c == '&' || c == ';'
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 fn to_pairs<'a>(qsm: &'a QueryStringMapping) -> Vec<(&'a str, Vec<&'a str>)> {
47 let mut pairs: Vec<(&str, Vec<&str>)> = qsm
48 .iter()
49 .map(|(k, v)| {
50 let mut values: Vec<&str> = v.iter().map(AsRef::as_ref).collect();
51 values.sort_unstable();
52
53 (k.as_str(), values)
54 })
55 .collect();
56
57 pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
58 pairs
59 }
60
61 #[test]
62 fn query_string_mapping_tests() {
63 let qsm = split(Some("a=b&c=d&e=f"));
64 assert_eq!(
65 to_pairs(&qsm),
66 vec![("a", vec!["b"]), ("c", vec!["d"]), ("e", vec!["f"])],
67 );
68
69 let qsm = split(Some("a=b&a=d&e=f"));
70 assert_eq!(
71 to_pairs(&qsm),
72 vec![("a", vec!["b", "d"]), ("e", vec!["f"])],
73 );
74
75 let qsm = split(Some("a&b"));
76 assert_eq!(to_pairs(&qsm), vec![],);
77
78 let qsm = split(Some("a=b;c=d&e=f"));
79 assert_eq!(
80 to_pairs(&qsm),
81 vec![("a", vec!["b"]), ("c", vec!["d"]), ("e", vec!["f"])],
82 );
83
84 let qsm = split(Some("a=b=c&d=e"));
85 assert_eq!(to_pairs(&qsm), vec![("a", vec!["b=c"]), ("d", vec!["e"])],);
86 }
87}