1use std::borrow::Cow;
2
3use crate::chars::{self, QUERY};
4use crate::error::InvalidUrl;
5use crate::quoting::{Component, unquote};
6
7#[repr(transparent)]
9pub struct Query(str);
10
11str_type!(Query);
12str_eq!(Query);
13
14impl Query {
15 pub fn new(src: &str) -> Result<&Query, InvalidUrl> {
17 chars::check(src, &QUERY)?;
18 Ok(Query::from_str_unchecked(src))
19 }
20
21 pub fn is_empty(&self) -> bool {
23 self.0.is_empty()
24 }
25
26 pub fn decode(&self) -> Cow<'_, str> {
29 unquote(&self.0, Component::Query)
30 }
31
32 pub fn pairs(&self) -> QueryPairs<'_> {
43 QueryPairs(self.0.split('&'))
44 }
45
46 pub fn get<'a>(&'a self, key: &str) -> Option<Cow<'a, str>> {
59 self.get_all(key).next()
60 }
61
62 pub fn get_all<'a>(&'a self, key: &str) -> impl Iterator<Item = Cow<'a, str>> {
64 self.raw_values(key)
65 .map(|v| unquote(v, Component::QueryPart))
66 }
67
68 pub fn contains_key(&self, key: &str) -> bool {
70 self.raw_values(key).next().is_some()
71 }
72
73 fn raw_values<'a>(&'a self, key: &str) -> impl Iterator<Item = &'a str> {
74 pieces(&self.0)
75 .map(split_pair)
76 .filter_map(move |(k, v)| (unquote(k, Component::QueryPart) == key).then_some(v))
77 }
78}
79
80pub(crate) fn pieces(query: &str) -> impl Iterator<Item = &str> {
82 query.split('&').filter(|piece| !piece.is_empty())
83}
84
85pub(crate) fn split_pair(piece: &str) -> (&str, &str) {
87 piece.split_once('=').unwrap_or((piece, ""))
88}
89
90#[derive(Clone, Debug)]
92pub struct QueryPairs<'a>(std::str::Split<'a, char>);
93
94impl<'a> Iterator for QueryPairs<'a> {
95 type Item = (Cow<'a, str>, Cow<'a, str>);
96
97 fn next(&mut self) -> Option<Self::Item> {
98 let (k, v) = split_pair(self.0.by_ref().find(|piece| !piece.is_empty())?);
99 Some((
100 unquote(k, Component::QueryPart),
101 unquote(v, Component::QueryPart),
102 ))
103 }
104}
105
106#[repr(transparent)]
108pub struct Fragment(str);
109
110str_type!(Fragment);
111str_eq!(Fragment);
112
113impl Fragment {
114 pub fn new(src: &str) -> Result<&Fragment, InvalidUrl> {
116 chars::check(src, &QUERY)?;
117 Ok(Fragment::from_str_unchecked(src))
118 }
119
120 pub fn decode(&self) -> Cow<'_, str> {
122 unquote(&self.0, Component::Fragment)
123 }
124}