rjango 0.1.1

A full-stack Rust backend framework inspired by Django
Documentation
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");
    }
}