1use std::fmt::{Display, Formatter};
2
3use crate::parse::Error;
4use crate::parse::Error::InvalidQuery;
5use crate::Param;
6
7#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
15pub struct Query<'a> {
16 query: &'a str,
17}
18
19impl<'a> Query<'a> {
20 pub unsafe fn new(query: &'a str) -> Self {
27 debug_assert!(Self::is_valid(query));
28
29 Self { query }
30 }
31}
32
33impl<'a> TryFrom<&'a str> for Query<'a> {
34 type Error = Error;
35
36 fn try_from(query: &'a str) -> Result<Self, Self::Error> {
37 if Self::is_valid(query) {
38 Ok(Self { query })
39 } else {
40 Err(InvalidQuery)
41 }
42 }
43}
44
45impl<'a> Query<'a> {
46 pub fn is_valid(query: &str) -> bool {
50 !query.is_empty()
51 && query.as_bytes()[0] == b'?'
52 && query.as_bytes()[1..]
53 .iter()
54 .all(|c| c.is_ascii_alphanumeric() || (c.is_ascii_punctuation() && *c != b'#'))
55 }
56}
57
58impl<'a> Query<'a> {
59 pub const fn as_str(&self) -> &str {
63 self.query
64 }
65}
66
67impl<'a> AsRef<str> for Query<'a> {
68 fn as_ref(&self) -> &str {
69 self.query
70 }
71}
72
73impl<'a> Display for Query<'a> {
74 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
75 write!(f, "{}", self.query)
76 }
77}
78
79impl<'a> Query<'a> {
80 pub const fn iter(&self) -> impl Iterator<Item = Param<'a>> {
84 ParamIterator {
85 remaining: self.query,
86 }
87 }
88}
89
90#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
92struct ParamIterator<'a> {
93 remaining: &'a str,
94}
95
96impl<'a> Iterator for ParamIterator<'a> {
97 type Item = Param<'a>;
98
99 fn next(&mut self) -> Option<Self::Item> {
100 if self.remaining.is_empty() {
101 None
102 } else {
103 self.remaining = &self.remaining[1..];
104 if let Some(amp) = self.remaining.as_bytes().iter().position(|c| *c == b'&') {
105 let result: Param = unsafe { Param::from_str(&self.remaining[..amp]) };
106 self.remaining = &self.remaining[amp..];
107 Some(result)
108 } else {
109 let result: Param = unsafe { Param::from_str(self.remaining) };
110 self.remaining = "";
111 Some(result)
112 }
113 }
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use crate::parse::Error::InvalidQuery;
120 use crate::{Param, Query};
121
122 #[test]
123 fn new() {
124 let query: Query = unsafe { Query::new("?the&query=params") };
125 assert_eq!(query.query, "?the&query=params");
126 }
127
128 #[test]
129 fn try_from_str() {
130 assert_eq!(Query::try_from("?").unwrap().as_str(), "?");
131 assert_eq!(
132 Query::try_from("?key=value").unwrap().as_str(),
133 "?key=value"
134 );
135 assert_eq!(Query::try_from(""), Err(InvalidQuery));
136 assert_eq!(Query::try_from("no-question"), Err(InvalidQuery));
137 assert_eq!(Query::try_from("?#"), Err(InvalidQuery));
138 }
139
140 #[test]
141 fn is_valid() {
142 let test_cases: &[(&str, bool)] = &[
143 ("", false),
144 ("?", true),
145 ("?#", false),
146 ("?&/=!@$%^&*()", true),
147 ("?azAZ09", true),
148 ];
149 for (query, expected) in test_cases {
150 let result: bool = Query::is_valid(query);
151 assert_eq!(result, *expected, "query={}", query);
152 }
153 }
154
155 #[test]
156 fn display() {
157 let query: Query = unsafe { Query::new("?the&query=params") };
158 assert_eq!(query.as_str(), "?the&query=params");
159 assert_eq!(query.as_ref(), "?the&query=params");
160 assert_eq!(query.to_string(), "?the&query=params");
161 }
162
163 #[test]
164 fn iter_params() {
165 let query: Query = unsafe { Query::new("?") };
166 let result: Vec<Param> = query.iter().collect();
167 assert_eq!(result, vec![unsafe { Param::new("", None) }]);
168
169 let query: Query = unsafe { Query::new("?&") };
170 let result: Vec<Param> = query.iter().collect();
171 assert_eq!(
172 result,
173 vec![unsafe { Param::new("", None) }, unsafe {
174 Param::new("", None)
175 }]
176 );
177
178 let query: Query = unsafe { Query::new("?the&query=params") };
179 let result: Vec<Param> = query.iter().collect();
180 assert_eq!(
181 result,
182 vec![unsafe { Param::new("the", None) }, unsafe {
183 Param::new("query", Some("params"))
184 }]
185 );
186 }
187}