gotham/helpers/http/request/
query_string.rs

1//! Defines helper functions for the Request query string
2
3use std::collections::HashMap;
4
5use crate::helpers::http::{form_url_decode, FormUrlDecoded};
6
7/// Provides a mapping of keys from `Request` query string to their supplied values
8pub(crate) type QueryStringMapping = HashMap<String, Vec<FormUrlDecoded>>;
9
10/// Splits a query string into pairs and provides a mapping of keys to values.
11///
12/// For keys which are represented 1..n times in the query string the mapped `Vec` will be
13/// populated with each value provided.
14///
15/// Keys that are provided but with no value associated are skipped.
16pub(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}