1use crate::encoding::{decode_query_key, decode_query_value, encode_query_key, encode_query_value};
2use indexmap::IndexMap;
3use serde_json::Value;
4
5pub type QueryValue = Value;
6pub type QueryObject = IndexMap<String, QueryValue>;
7
8pub fn parse_query(raw_query: &str) -> QueryObject {
25 let mut object = QueryObject::new();
26 let params_pair = raw_query.strip_prefix('?').unwrap_or(raw_query);
28
29 for parameter in params_pair.split('&') {
31 if let Some((key, value)) = parameter.split_once('=') {
32 let key = decode_query_key(key);
33 let value = decode_query_value(value);
34
35 match object.get_mut(&key) {
37 None => {
38 object.insert(key, Value::String(value));
39 }
40 Some(existing) => {
41 let existing_str = existing.as_str().unwrap_or("");
42 *existing = Value::String(format!("{},{}", existing_str, value));
43 }
44 }
45 }
46 }
47 object
48}
49
50pub fn encode_query_item(key: &str, value: &QueryValue) -> String {
65 if value.is_null() || value.is_boolean() || value.is_number() {
67 return encode_query_key(key);
68 }
69
70 if let Some(arr) = value.as_array() {
72 return arr
73 .iter()
74 .map(|v| {
75 format!(
76 "{}={}",
77 encode_query_key(key),
78 encode_query_value(v.as_str().unwrap_or(""))
79 )
80 })
81 .collect::<Vec<String>>()
82 .join("&");
83 }
84
85 format!(
87 "{}={}",
88 encode_query_key(key),
89 encode_query_value(value.as_str().unwrap_or(""))
90 )
91}
92
93pub fn stringify_query(query: &QueryObject) -> String {
107 query
108 .iter()
109 .filter(|(_, v)| !v.is_null())
110 .map(|(k, v)| encode_query_item(k, v))
111 .filter(|s| !s.is_empty())
112 .collect::<Vec<String>>()
113 .join("&")
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119 use serde_json::json;
120
121 #[test]
122 fn test_parse_query_basic() {
123 let query = parse_query("foo=bar&baz=qux");
124 assert_eq!(query.get("foo").unwrap(), "bar");
125 assert_eq!(query.get("baz").unwrap(), "qux");
126 }
127
128 #[test]
129 fn test_parse_query_with_question_mark() {
130 let query = parse_query("?foo=bar&baz=qux");
131 assert_eq!(query.get("foo").unwrap(), "bar");
132 assert_eq!(query.get("baz").unwrap(), "qux");
133 }
134
135 #[test]
136 fn test_parse_query_empty() {
137 let query = parse_query("");
138 assert!(query.is_empty());
139 }
140
141 #[test]
142 fn test_parse_query_encoded() {
143 let query = parse_query("user=john%20doe&email=test%40example.com");
144 assert_eq!(query.get("user").unwrap(), "john doe");
145 assert_eq!(query.get("email").unwrap(), "test@example.com");
146 }
147
148 #[test]
149 fn test_parse_query_multiple_values() {
150 let query = parse_query("color=red&color=blue&color=green");
151 assert_eq!(query.get("color").unwrap(), "red,blue,green");
152 }
153
154 #[test]
155 fn test_encode_query_item_basic() {
156 let key = "test";
157 let value = json!("hello world");
158 assert_eq!(encode_query_item(key, &value), "test=hello+world");
159 }
160
161 #[test]
162 fn test_encode_query_item_array() {
163 let key = "test";
164 let array_value = json!(["a", "b", "c"]);
165 assert_eq!(encode_query_item(key, &array_value), "test=a&test=b&test=c");
166 }
167
168 #[test]
169 fn test_encode_query_item_special_chars() {
170 let key = "email";
171 let value = json!("test@example.com");
172 assert_eq!(encode_query_item(key, &value), "email=test%40example.com");
173 }
174
175 #[test]
176 fn test_encode_query_item_null() {
177 let key = "empty";
178 let value = json!(null);
179 assert_eq!(encode_query_item(key, &value), "empty");
180 }
181
182 #[test]
183 fn test_stringify_query_basic() {
184 let mut query = QueryObject::new();
185 query.insert("foo".to_string(), json!("bar"));
186 query.insert("baz".to_string(), json!("qux"));
187
188 let result = stringify_query(&query);
189 assert!(result.contains("foo=bar"));
190 assert!(result.contains("baz=qux"));
191 }
192
193 #[test]
194 fn test_stringify_query_complex() {
195 let mut query = QueryObject::new();
196 query.insert("user".to_string(), json!("john doe"));
197 query.insert("tags".to_string(), json!(["rust", "coding"]));
198 query.insert("active".to_string(), json!(true));
199 query.insert("empty".to_string(), json!(null));
200
201 let result = stringify_query(&query);
202 assert!(result.contains("user=john+doe"));
203 assert!(result.contains("tags=rust&tags=coding"));
204 assert!(result.contains("active"));
205 assert!(!result.contains("empty"));
206 }
207
208 #[test]
209 fn test_encode_query_item_emoji() {
210 let key = "message";
211 let value = json!("Hello 👋 World 🌍");
212 assert_eq!(
213 encode_query_item(key, &value),
214 "message=Hello+%F0%9F%91%8B+World+%F0%9F%8C%8D"
215 );
216
217 let key = "reaction";
218 let value = json!("❤️");
219 assert_eq!(
220 encode_query_item(key, &value),
221 "reaction=%E2%9D%A4%EF%B8%8F"
222 );
223
224 let key = "faces";
225 let value = json!(["😀", "😎", "🤔"]);
226 assert_eq!(
227 encode_query_item(key, &value),
228 "faces=%F0%9F%98%80&faces=%F0%9F%98%8E&faces=%F0%9F%A4%94"
229 );
230 }
231
232 #[test]
233 fn test_decode_query_item_emoji() {
234 let encoded = "http://example.com?message=Hello+%F0%9F%91%8B+World+%F0%9F%8C%8D";
235 let url = url::Url::parse(encoded).unwrap();
236 let pairs: Vec<(String, String)> = url.query_pairs().into_owned().collect();
237 assert_eq!(
238 pairs[0],
239 ("message".to_string(), "Hello 👋 World 🌍".to_string())
240 );
241
242 let encoded = "http://example.com?reaction=%E2%9D%A4%EF%B8%8F";
243 let url = url::Url::parse(encoded).unwrap();
244 let pairs: Vec<(String, String)> = url.query_pairs().into_owned().collect();
245 assert_eq!(pairs[0], ("reaction".to_string(), "❤️".to_string()));
246
247 let encoded = "http://example.com?faces=%F0%9F%98%80";
248 let url = url::Url::parse(encoded).unwrap();
249 let pairs: Vec<(String, String)> = url.query_pairs().into_owned().collect();
250 assert_eq!(pairs[0], ("faces".to_string(), "😀".to_string()));
251 }
252}