1use std::{
4 borrow::{Borrow, Cow},
5 fmt::{self, Display, Formatter},
6 ops::Deref,
7 str::{FromStr, Utf8Error},
8};
9
10#[derive(Debug, Clone)]
12pub struct QueryDictKey {
13 inner: Cow<'static, str>,
14}
15
16impl QueryDictKey {
17 #[inline]
19 pub const fn from_static(inner: &'static str) -> Self {
20 Self {
21 inner: Cow::Borrowed(inner),
22 }
23 }
24
25 pub fn new<T>(key: T) -> Self
27 where
28 T: Into<String>,
29 {
30 Self {
31 inner: Cow::Owned(key.into()),
32 }
33 }
34}
35
36impl AsRef<str> for QueryDictKey {
37 #[inline]
38 fn as_ref(&self) -> &str {
39 &self.inner
40 }
41}
42
43impl Borrow<str> for QueryDictKey {
44 #[inline]
45 fn borrow(&self) -> &str {
46 &self.inner
47 }
48}
49
50impl Deref for QueryDictKey {
51 type Target = str;
52
53 #[inline]
54 fn deref(&self) -> &Self::Target {
55 &self.inner
56 }
57}
58
59impl PartialEq for QueryDictKey {
60 #[inline]
61 fn eq(&self, other: &Self) -> bool {
62 self.inner.eq_ignore_ascii_case(&other.inner)
63 }
64}
65
66impl Eq for QueryDictKey {}
67
68impl PartialEq<str> for QueryDictKey {
69 #[inline]
70 fn eq(&self, other: &str) -> bool {
71 self.inner.eq_ignore_ascii_case(other)
72 }
73}
74
75impl PartialEq<QueryDictKey> for str {
76 #[inline]
77 fn eq(&self, other: &QueryDictKey) -> bool {
78 self.eq_ignore_ascii_case(&other.inner)
79 }
80}
81
82impl From<&'static str> for QueryDictKey {
83 #[inline]
84 fn from(key: &'static str) -> Self {
85 Self::from_static(key)
86 }
87}
88
89impl From<String> for QueryDictKey {
90 #[inline]
91 fn from(key: String) -> Self {
92 Self::new(key)
93 }
94}
95
96#[derive(Debug, Clone)]
98pub struct QueryDictValue {
99 inner: Cow<'static, str>,
100}
101
102impl QueryDictValue {
103 #[inline]
105 pub const fn from_static(inner: &'static str) -> Self {
106 Self {
107 inner: Cow::Borrowed(inner),
108 }
109 }
110
111 pub fn new<T>(key: T) -> Self
113 where
114 T: Into<String>,
115 {
116 Self {
117 inner: Cow::Owned(key.into()),
118 }
119 }
120}
121
122impl AsRef<str> for QueryDictValue {
123 #[inline]
124 fn as_ref(&self) -> &str {
125 &self.inner
126 }
127}
128
129impl Borrow<str> for QueryDictValue {
130 #[inline]
131 fn borrow(&self) -> &str {
132 &self.inner
133 }
134}
135
136impl Deref for QueryDictValue {
137 type Target = str;
138
139 #[inline]
140 fn deref(&self) -> &Self::Target {
141 &self.inner
142 }
143}
144
145impl From<&'static str> for QueryDictValue {
146 #[inline]
147 fn from(key: &'static str) -> Self {
148 Self::from_static(key)
149 }
150}
151
152impl From<String> for QueryDictValue {
153 #[inline]
154 fn from(key: String) -> Self {
155 Self::new(key)
156 }
157}
158
159#[derive(Debug, Clone)]
161pub struct QueryDictItem {
162 key: QueryDictKey,
163 value: Option<QueryDictValue>,
164}
165
166impl QueryDictItem {
167 #[inline]
169 pub fn key(&self) -> &QueryDictKey {
170 &self.key
171 }
172
173 #[inline]
175 pub fn value(&self) -> Option<&QueryDictValue> {
176 self.value.as_ref()
177 }
178}
179
180impl<K> From<(K,)> for QueryDictItem
181where
182 K: Into<QueryDictKey>,
183{
184 fn from(item: (K,)) -> Self {
185 let (key,) = item;
186
187 Self {
188 key: key.into(),
189 value: None,
190 }
191 }
192}
193
194impl<K, V> From<(K, V)> for QueryDictItem
195where
196 K: Into<QueryDictKey>,
197 V: Into<QueryDictValue>,
198{
199 fn from(item: (K, V)) -> Self {
200 let (key, value) = item;
201
202 Self {
203 key: key.into(),
204 value: Some(value.into()),
205 }
206 }
207}
208
209impl Display for QueryDictItem {
210 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
211 let key = crate::url_encode(self.key.as_ref());
212
213 if let Some(value) = self.value.as_ref() {
214 let value = crate::url_encode(value.as_ref());
215
216 write!(f, "{key}={value}")
217 } else {
218 write!(f, "{key}")
219 }
220 }
221}
222
223impl FromStr for QueryDictItem {
224 type Err = Utf8Error;
225
226 fn from_str(s: &str) -> Result<Self, Self::Err> {
227 let (key, value) = s
228 .split_once('=')
229 .map(|(k, v)| (k, Some(v)))
230 .unwrap_or((s, None));
231
232 let key = String::from_utf8(Cow::into_owned(crate::url_decode(key)))
233 .map_err(|err| err.utf8_error())?
234 .into();
235
236 let value = value
237 .map(crate::url_decode)
238 .map(|decoded| String::from_utf8(decoded.into_owned()))
239 .transpose()
240 .map_err(|err| err.utf8_error())?
241 .map(QueryDictValue::from);
242
243 let item = Self { key, value };
244
245 Ok(item)
246 }
247}
248
249#[derive(Clone)]
259pub struct QueryDict {
260 items: Vec<QueryDictItem>,
261}
262
263impl QueryDict {
264 #[inline]
266 pub const fn new() -> Self {
267 Self { items: Vec::new() }
268 }
269
270 #[inline]
272 pub fn with_capacity(capacity: usize) -> Self {
273 Self {
274 items: Vec::with_capacity(capacity),
275 }
276 }
277
278 pub fn add<T>(&mut self, item: T)
282 where
283 T: Into<QueryDictItem>,
284 {
285 self.items.push(item.into());
286 }
287
288 pub fn set<T>(&mut self, item: T)
292 where
293 T: Into<QueryDictItem>,
294 {
295 fn inner(items: &mut Vec<QueryDictItem>, item: QueryDictItem) {
297 items.retain(|i| !i.key.eq_ignore_ascii_case(&item.key));
298 items.push(item);
299 }
300
301 inner(&mut self.items, item.into());
302 }
303
304 pub fn remove<N>(&mut self, key: &N)
308 where
309 N: AsRef<str> + ?Sized,
310 {
311 fn inner(fields: &mut Vec<QueryDictItem>, key: &str) {
313 fields.retain(|i| !i.key.eq_ignore_ascii_case(key));
314 }
315
316 inner(&mut self.items, key.as_ref());
317 }
318
319 pub fn get<'a, N>(&'a self, key: &'a N) -> KeyIter<'a>
323 where
324 N: AsRef<str> + ?Sized,
325 {
326 KeyIter {
327 inner: self.all(),
328 key: key.as_ref(),
329 }
330 }
331
332 pub fn last<'a, N>(&'a self, key: &'a N) -> Option<&'a QueryDictItem>
336 where
337 N: AsRef<str> + ?Sized,
338 {
339 fn inner<'a>(iter: &mut KeyIter<'a>) -> Option<&'a QueryDictItem> {
341 iter.next_back()
342 }
343
344 inner(&mut self.get(key))
345 }
346
347 pub fn last_value<'a, N>(&'a self, key: &'a N) -> Option<&'a QueryDictValue>
351 where
352 N: AsRef<str> + ?Sized,
353 {
354 fn inner<'a>(iter: &mut KeyIter<'a>) -> Option<&'a QueryDictValue> {
356 iter.next_back().and_then(|item| item.value())
357 }
358
359 inner(&mut self.get(key))
360 }
361
362 #[inline]
364 pub fn all(&self) -> Iter<'_> {
365 Iter {
366 inner: self.items.iter(),
367 }
368 }
369
370 #[inline]
372 pub fn is_empty(&self) -> bool {
373 self.items.is_empty()
374 }
375
376 #[inline]
378 pub fn len(&self) -> usize {
379 self.items.len()
380 }
381}
382
383impl Default for QueryDict {
384 #[inline]
385 fn default() -> Self {
386 Self::new()
387 }
388}
389
390impl Display for QueryDict {
391 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
392 let mut iter = self.items.iter();
393
394 if let Some(item) = iter.next() {
395 write!(f, "{item}")?;
396 }
397
398 for item in iter {
399 write!(f, "&{item}")?;
400 }
401
402 Ok(())
403 }
404}
405
406impl From<Vec<QueryDictItem>> for QueryDict {
407 #[inline]
408 fn from(items: Vec<QueryDictItem>) -> Self {
409 Self { items }
410 }
411}
412
413impl FromStr for QueryDict {
414 type Err = Utf8Error;
415
416 fn from_str(s: &str) -> Result<Self, Self::Err> {
417 let mut res = Self::new();
418
419 for item in s.split('&') {
420 let item = item.trim();
421
422 if item.is_empty() {
423 continue;
424 }
425
426 res.add(QueryDictItem::from_str(item)?);
427 }
428
429 Ok(res)
430 }
431}
432
433pub struct Iter<'a> {
435 inner: std::slice::Iter<'a, QueryDictItem>,
436}
437
438impl<'a> Iterator for Iter<'a> {
439 type Item = &'a QueryDictItem;
440
441 #[inline]
442 fn next(&mut self) -> Option<Self::Item> {
443 self.inner.next()
444 }
445}
446
447impl<'a> DoubleEndedIterator for Iter<'a> {
448 #[inline]
449 fn next_back(&mut self) -> Option<Self::Item> {
450 self.inner.next_back()
451 }
452}
453
454impl<'a> ExactSizeIterator for Iter<'a> {
455 #[inline]
456 fn len(&self) -> usize {
457 self.inner.len()
458 }
459}
460
461pub struct KeyIter<'a> {
463 inner: Iter<'a>,
464 key: &'a str,
465}
466
467impl<'a> Iterator for KeyIter<'a> {
468 type Item = &'a QueryDictItem;
469
470 fn next(&mut self) -> Option<Self::Item> {
471 #[allow(clippy::while_let_on_iterator)]
472 while let Some(item) = self.inner.next() {
473 if item.key.eq_ignore_ascii_case(self.key) {
474 return Some(item);
475 }
476 }
477
478 None
479 }
480}
481
482impl<'a> DoubleEndedIterator for KeyIter<'a> {
483 fn next_back(&mut self) -> Option<Self::Item> {
484 while let Some(item) = self.inner.next_back() {
485 if item.key.eq_ignore_ascii_case(self.key) {
486 return Some(item);
487 }
488 }
489
490 None
491 }
492}
493
494#[cfg(test)]
495mod tests {
496 use std::{borrow::Borrow, str::FromStr};
497
498 use super::{QueryDict, QueryDictValue};
499
500 fn qd_value_eq<A, B>(a: A, b: B) -> bool
501 where
502 A: Borrow<QueryDictValue>,
503 B: Borrow<QueryDictValue>,
504 {
505 let a = a.borrow();
506 let b = b.borrow();
507
508 a.as_ref() == b.as_ref()
509 }
510
511 fn qd_values_eq<A, B>(a: A, b: B) -> bool
512 where
513 A: AsRef<[QueryDictValue]>,
514 B: AsRef<[QueryDictValue]>,
515 {
516 let a = a.as_ref();
517 let b = b.as_ref();
518
519 a.len() == b.len() && a.iter().zip(b.iter()).all(|(a, b)| qd_value_eq(a, b))
520 }
521
522 #[test]
523 fn test_query_dict_from_string() {
524 let inputs = &[
525 "sss",
526 "aa=bb",
527 "aa=bb&cc=dd",
528 "aa=bb&aa=cc",
529 "email=some%40example%2Ecom",
530 ];
531
532 for item in inputs {
533 let dict = QueryDict::from_str(item);
534
535 assert!(dict.is_ok());
536
537 assert_eq!(format!("{}", dict.unwrap()), item.to_string());
538 }
539 }
540
541 #[test]
542 fn test_multiple_values() {
543 let mut dict = QueryDict::default();
544
545 dict.add(("slot", "first"));
546 dict.add(("slot", "second"));
547
548 assert_eq!(dict.last_value("slot").map(|v| v.as_ref()), Some("second"));
549
550 assert!(qd_values_eq(
551 dict.get("slot")
552 .filter_map(|e| e.value())
553 .cloned()
554 .collect::<Vec<_>>(),
555 vec![
556 QueryDictValue::from("first"),
557 QueryDictValue::from("second")
558 ]
559 ));
560 }
561
562 #[test]
563 fn test_multiple_values_from_string() {
564 let dict = QueryDict::from_str("bb[]=1&bb[]=2&&cc[]=3&bb[]=4").unwrap();
565
566 assert!(qd_values_eq(
567 dict.get("bb[]")
568 .filter_map(|e| e.value())
569 .cloned()
570 .collect::<Vec<_>>(),
571 vec![
572 QueryDictValue::from("1"),
573 QueryDictValue::from("2"),
574 QueryDictValue::from("4"),
575 ]
576 ));
577 }
578
579 #[test]
580 fn test_percent_decoding() {
581 let dict = QueryDict::from_str("email=some%40example%2Ecom").unwrap();
582
583 assert_eq!(
584 dict.last_value("email").map(|v| v.as_ref()),
585 Some("some@example.com")
586 );
587 }
588
589 #[test]
590 fn test_percent_encoding() {
591 let mut dict = QueryDict::new();
592
593 dict.add(("email", "some@example.com"));
594
595 assert_eq!(dict.to_string(), String::from("email=some%40example%2Ecom"));
596 }
597
598 #[test]
599 fn test_space_in_query() {
600 let dict = QueryDict::from_str("a=%20x%20z%20&b=y").unwrap();
601
602 assert_eq!(dict.last_value("a").map(|v| v.as_ref()), Some(" x z "));
603 }
604
605 #[test]
606 fn test_remove() {
607 let mut dict = QueryDict::from_str("bb[]=1&bb[]=2&cc[]=3&bb[]=4").unwrap();
608
609 assert_eq!(dict.get("bb[]").count(), 3);
610 assert_eq!(dict.all().count(), 4);
611
612 dict.remove("bb[]");
613
614 assert_eq!(dict.get("bb[]").count(), 0);
615 assert_eq!(dict.all().count(), 1);
616 }
617
618 #[test]
619 fn test_set() {
620 let mut dict = QueryDict::from_str("bb[]=1&bb[]=2&cc[]=3&bb[]=4").unwrap();
621
622 assert_eq!(dict.get("bb[]").count(), 3);
623 assert_eq!(dict.all().count(), 4);
624
625 dict.set(("bb[]", "5"));
626
627 assert_eq!(dict.get("bb[]").count(), 1);
628 assert_eq!(dict.all().count(), 2);
629 }
630}