use crate::utils::datastructures::MultiValueDict;
use crate::utils::http::urlencode;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QueryDict {
inner: MultiValueDict<String, String>,
mutable: bool,
}
impl QueryDict {
#[must_use]
pub fn from_query_string(qs: &str) -> Self {
let mut inner = MultiValueDict::new();
for (key, value) in url::form_urlencoded::parse(qs.trim_start_matches('?').as_bytes()) {
inner.append(key.into_owned(), value.into_owned());
}
Self {
inner,
mutable: false,
}
}
pub fn get(&self, key: &str) -> Option<&str> {
self.inner.get(key).map(String::as_str)
}
#[must_use]
pub fn getlist(&self, key: &str) -> Vec<&str> {
self.inner
.get_list(key)
.iter()
.map(String::as_str)
.collect()
}
#[must_use]
pub fn urlencode(&self) -> String {
let pairs = self
.inner
.lists()
.flat_map(|(key, values)| {
values
.iter()
.map(move |value| (key.as_str(), value.as_str()))
})
.collect::<Vec<_>>();
urlencode(&pairs)
}
}
#[cfg(test)]
mod tests {
use super::QueryDict;
#[test]
fn from_query_string_keeps_last_value_and_full_list() {
let query = QueryDict::from_query_string("tag=rust&tag=axum&page=1");
assert_eq!(query.get("tag"), Some("axum"));
assert_eq!(query.getlist("tag"), vec!["rust", "axum"]);
assert_eq!(query.get("page"), Some("1"));
assert_eq!(query.get("missing"), None);
assert_eq!(query.getlist("missing"), Vec::<&str>::new());
}
#[test]
fn from_query_string_decodes_values() {
let query = QueryDict::from_query_string("?name=Adrian+Holovaty&empty=&city=New+York");
assert_eq!(query.get("name"), Some("Adrian Holovaty"));
assert_eq!(query.get("empty"), Some(""));
assert_eq!(query.get("city"), Some("New York"));
}
#[test]
fn urlencode_round_trips_preserving_order() {
let query = QueryDict::from_query_string("tag=rust&tag=axum&name=Adrian+Holovaty");
assert_eq!(query.urlencode(), "tag=rust&tag=axum&name=Adrian+Holovaty");
}
}