fiberplane_models/
query_data.rs

1use std::borrow::Cow;
2
3const MIME_TYPE_PREFIX: &str = "application/x-www-form-urlencoded,";
4
5/// Returns the value of a field in the query data.
6///
7/// Returns an empty string if the field has no value.
8pub fn get_query_field<'a>(query_data: &'a str, field_name: &str) -> Cow<'a, str> {
9    if let Some(data) = query_data.strip_prefix(MIME_TYPE_PREFIX) {
10        for (key, value) in form_urlencoded::parse(data.as_bytes()) {
11            if key == field_name {
12                return value;
13            }
14        }
15    }
16
17    Cow::Borrowed("")
18}
19
20/// Returns whether the query data string contains any query data that we
21/// understand.
22pub fn has_query_data(query_data: &str) -> bool {
23    if let Some(data) = query_data.strip_prefix(MIME_TYPE_PREFIX) {
24        !data.is_empty()
25    } else {
26        false
27    }
28}
29
30/// Sets the value of a field in the query data.
31///
32/// Returns the new query data.
33///
34/// This functions maintains an alphabetical ordering of the keys in order to
35/// guarantee a consistent result when separate fields are set out of order.
36/// This is to maintain convergence for our OT algorithm.
37pub fn set_query_field(query_data: &str, field_name: &str, value: &str) -> String {
38    let mut new_query_data = MIME_TYPE_PREFIX.to_owned();
39    let mut inserted = false;
40    if let Some(data) = query_data.strip_prefix(MIME_TYPE_PREFIX) {
41        for (key, existing_value) in form_urlencoded::parse(data.as_bytes()) {
42            if !inserted && key.as_ref() >= field_name {
43                append_query_field(&mut new_query_data, field_name, value);
44                inserted = true;
45            }
46            if key != field_name {
47                append_query_field(&mut new_query_data, &key, &existing_value);
48            }
49        }
50    }
51    if !inserted {
52        append_query_field(&mut new_query_data, field_name, value);
53    }
54    new_query_data
55}
56
57/// Removes a field from the query data.
58///
59/// Returns the new query data.
60pub fn unset_query_field(query_data: &str, field_name: &str) -> String {
61    let mut new_query_data = MIME_TYPE_PREFIX.to_owned();
62    if let Some(data) = query_data.strip_prefix(MIME_TYPE_PREFIX) {
63        for (key, value) in form_urlencoded::parse(data.as_bytes()) {
64            if key != field_name {
65                append_query_field(&mut new_query_data, &key, &value);
66            }
67        }
68    }
69    new_query_data
70}
71
72fn append_query_field(query_data: &mut String, field_name: &str, value: &str) {
73    if query_data.len() > MIME_TYPE_PREFIX.len() {
74        query_data.push('&');
75    }
76
77    query_data.extend(form_urlencoded::byte_serialize(field_name.as_bytes()));
78    query_data.push('=');
79    query_data.extend(form_urlencoded::byte_serialize(value.as_bytes()));
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn test_get_query_field() {
88        assert_eq!(
89            get_query_field("application/x-www-form-urlencoded,trace_id=123", "trace_id"),
90            "123"
91        );
92        assert_eq!(
93            get_query_field("application/x-www-form-urlencoded,trace_id=123", "id"),
94            ""
95        );
96        assert_eq!(get_query_field("trace_id=123", "trace_id"), "");
97        assert_eq!(get_query_field("", "trace_id"), "");
98
99        assert_eq!(
100            get_query_field("application/x-www-form-urlencoded,hi+=%26there", "hi "),
101            "&there"
102        );
103    }
104
105    #[test]
106    fn test_set_query_field() {
107        assert_eq!(
108            &set_query_field(
109                "application/x-www-form-urlencoded,trace_id=123",
110                "trace_id",
111                "456"
112            ),
113            "application/x-www-form-urlencoded,trace_id=456"
114        );
115        assert_eq!(
116            &set_query_field(
117                "application/x-www-form-urlencoded,trace_id=123",
118                "id",
119                "456"
120            ),
121            "application/x-www-form-urlencoded,id=456&trace_id=123"
122        );
123        assert_eq!(
124            &set_query_field("trace_id=123", "trace_id", "456"),
125            "application/x-www-form-urlencoded,trace_id=456"
126        );
127        assert_eq!(
128            &set_query_field("", "trace_id", "456"),
129            "application/x-www-form-urlencoded,trace_id=456"
130        );
131
132        assert_eq!(
133            &set_query_field(
134                "application/x-www-form-urlencoded,hi+=%26there",
135                "hi!",
136                "-_.!~*'()#"
137            ),
138            "application/x-www-form-urlencoded,hi+=%26there&hi%21=-_.%21%7E*%27%28%29%23"
139        );
140    }
141
142    #[test]
143    fn test_unset_query_field() {
144        assert_eq!(
145            &unset_query_field("application/x-www-form-urlencoded,trace_id=123", "trace_id"),
146            "application/x-www-form-urlencoded,"
147        );
148        assert_eq!(
149            &unset_query_field("application/x-www-form-urlencoded,trace_id=123", "id"),
150            "application/x-www-form-urlencoded,trace_id=123"
151        );
152        assert_eq!(
153            &unset_query_field("trace_id=123", "trace_id"),
154            "application/x-www-form-urlencoded,"
155        );
156        assert_eq!(
157            &unset_query_field("", "trace_id"),
158            "application/x-www-form-urlencoded,"
159        );
160
161        assert_eq!(
162            &unset_query_field("application/x-www-form-urlencoded,hi+=%26there", "hi "),
163            "application/x-www-form-urlencoded,"
164        );
165    }
166}