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::{Param, Query};
120
121 #[test]
122 fn new() {
123 let query: Query = unsafe { Query::new("?the&query=params") };
124 assert_eq!(query.query, "?the&query=params");
125 }
126
127 #[test]
128 fn is_valid() {
129 let test_cases: &[(&str, bool)] = &[
130 ("", false),
131 ("?", true),
132 ("?#", false),
133 ("?&/=!@$%^&*()", true),
134 ("?azAZ09", true),
135 ];
136 for (query, expected) in test_cases {
137 let result: bool = Query::is_valid(query);
138 assert_eq!(result, *expected, "query={}", query);
139 }
140 }
141
142 #[test]
143 fn display() {
144 let query: Query = unsafe { Query::new("?the&query=params") };
145 assert_eq!(query.as_str(), "?the&query=params");
146 assert_eq!(query.as_ref(), "?the&query=params");
147 assert_eq!(query.to_string(), "?the&query=params");
148 }
149
150 #[test]
151 fn iter_params() {
152 let query: Query = unsafe { Query::new("?") };
153 let result: Vec<Param> = query.iter().collect();
154 assert_eq!(result, vec![unsafe { Param::new("", None) }]);
155
156 let query: Query = unsafe { Query::new("?&") };
157 let result: Vec<Param> = query.iter().collect();
158 assert_eq!(
159 result,
160 vec![unsafe { Param::new("", None) }, unsafe {
161 Param::new("", None)
162 }]
163 );
164
165 let query: Query = unsafe { Query::new("?the&query=params") };
166 let result: Vec<Param> = query.iter().collect();
167 assert_eq!(
168 result,
169 vec![unsafe { Param::new("the", None) }, unsafe {
170 Param::new("query", Some("params"))
171 }]
172 );
173 }
174}