rocket_http/uri/path_query.rs
1use std::hash::Hash;
2use std::borrow::Cow;
3use std::fmt::Write;
4
5use state::InitCell;
6
7use crate::{RawStr, ext::IntoOwned};
8use crate::uri::Segments;
9use crate::uri::fmt::{self, Part};
10use crate::parse::{IndexedStr, Extent};
11
12// INTERNAL DATA STRUCTURE.
13#[doc(hidden)]
14#[derive(Debug, Clone)]
15pub struct Data<'a, P: Part> {
16 pub(crate) value: IndexedStr<'a>,
17 pub(crate) decoded_segments: InitCell<Vec<P::Raw>>,
18}
19
20impl<'a, P: Part> Data<'a, P> {
21 pub(crate) fn raw(value: Extent<&'a [u8]>) -> Self {
22 Data { value: value.into(), decoded_segments: InitCell::new() }
23 }
24
25 // INTERNAL METHOD.
26 #[doc(hidden)]
27 pub fn new<S: Into<Cow<'a, str>>>(value: S) -> Self {
28 Data {
29 value: IndexedStr::from(value.into()),
30 decoded_segments: InitCell::new(),
31 }
32 }
33}
34
35/// A URI path: `/foo/bar`, `foo/bar`, etc.
36#[derive(Debug, Clone, Copy)]
37pub struct Path<'a> {
38 pub(crate) source: &'a Option<Cow<'a, str>>,
39 pub(crate) data: &'a Data<'a, fmt::Path>,
40}
41
42/// A URI query: `?foo&bar`.
43#[derive(Debug, Clone, Copy)]
44pub struct Query<'a> {
45 pub(crate) source: &'a Option<Cow<'a, str>>,
46 pub(crate) data: &'a Data<'a, fmt::Query>,
47}
48
49fn decode_to_indexed_str<P: fmt::Part>(
50 value: &RawStr,
51 (indexed, source): (&IndexedStr<'_>, &RawStr)
52) -> IndexedStr<'static> {
53 let decoded = match P::KIND {
54 fmt::Kind::Path => value.percent_decode_lossy(),
55 fmt::Kind::Query => value.url_decode_lossy(),
56 };
57
58 match decoded {
59 Cow::Borrowed(b) if indexed.is_indexed() => {
60 let indexed = IndexedStr::checked_from(b, source.as_str());
61 debug_assert!(indexed.is_some());
62 indexed.unwrap_or_else(|| IndexedStr::from(Cow::Borrowed("")))
63 }
64 cow => IndexedStr::from(Cow::Owned(cow.into_owned())),
65 }
66}
67
68impl<'a> Path<'a> {
69 /// Returns the raw path value.
70 ///
71 /// # Example
72 ///
73 /// ```rust
74 /// # #[macro_use] extern crate rocket;
75 /// let uri = uri!("/foo%20bar%2dbaz");
76 /// assert_eq!(uri.path(), "/foo%20bar%2dbaz");
77 /// assert_eq!(uri.path().raw(), "/foo%20bar%2dbaz");
78 /// ```
79 pub fn raw(&self) -> &'a RawStr {
80 self.data.value.from_cow_source(self.source).into()
81 }
82
83 /// Returns the raw, undecoded path value as an `&str`.
84 ///
85 /// # Example
86 ///
87 /// ```rust
88 /// # #[macro_use] extern crate rocket;
89 /// let uri = uri!("/foo%20bar%2dbaz");
90 /// assert_eq!(uri.path(), "/foo%20bar%2dbaz");
91 /// assert_eq!(uri.path().as_str(), "/foo%20bar%2dbaz");
92 /// ```
93 pub fn as_str(&self) -> &'a str {
94 self.raw().as_str()
95 }
96
97 /// Whether `self` is normalized, i.e, it has no empty segments.
98 ///
99 /// If `absolute`, then a starting `/` is required.
100 pub(crate) fn is_normalized(&self, absolute: bool) -> bool {
101 (!absolute || self.raw().starts_with('/'))
102 && self.raw_segments().all(|s| !s.is_empty())
103 }
104
105 /// Normalizes `self`. If `absolute`, a starting `/` is required.
106 pub(crate) fn to_normalized(self, absolute: bool) -> Data<'static, fmt::Path> {
107 let mut path = String::with_capacity(self.raw().len());
108 let absolute = absolute || self.raw().starts_with('/');
109 for (i, seg) in self.raw_segments().filter(|s| !s.is_empty()).enumerate() {
110 if absolute || i != 0 { path.push('/'); }
111 let _ = write!(path, "{}", seg);
112 }
113
114 if path.is_empty() && absolute {
115 path.push('/');
116 }
117
118 Data {
119 value: IndexedStr::from(Cow::Owned(path)),
120 decoded_segments: InitCell::new(),
121 }
122 }
123
124 /// Returns an iterator over the raw, undecoded segments. Segments may be
125 /// empty.
126 ///
127 /// ### Example
128 ///
129 /// ```rust
130 /// # #[macro_use] extern crate rocket;
131 /// use rocket::http::uri::Origin;
132 ///
133 /// let uri = Origin::parse("/").unwrap();
134 /// assert_eq!(uri.path().raw_segments().count(), 0);
135 ///
136 /// let uri = Origin::parse("//").unwrap();
137 /// let segments: Vec<_> = uri.path().raw_segments().collect();
138 /// assert_eq!(segments, &["", ""]);
139 ///
140 /// // Recall that `uri!()` normalizes static inputs.
141 /// let uri = uri!("//");
142 /// assert_eq!(uri.path().raw_segments().count(), 0);
143 ///
144 /// let uri = Origin::parse("/a").unwrap();
145 /// let segments: Vec<_> = uri.path().raw_segments().collect();
146 /// assert_eq!(segments, &["a"]);
147 ///
148 /// let uri = Origin::parse("/a//b///c/d?query¶m").unwrap();
149 /// let segments: Vec<_> = uri.path().raw_segments().collect();
150 /// assert_eq!(segments, &["a", "", "b", "", "", "c", "d"]);
151 /// ```
152 #[inline(always)]
153 pub fn raw_segments(&self) -> impl Iterator<Item = &'a RawStr> {
154 let path = match self.raw() {
155 p if p.is_empty() || p == "/" => None,
156 p if p.starts_with(fmt::Path::DELIMITER) => Some(&p[1..]),
157 p => Some(p)
158 };
159
160 path.map(|p| p.split(fmt::Path::DELIMITER))
161 .into_iter()
162 .flatten()
163 }
164
165 /// Returns a (smart) iterator over the non-empty, percent-decoded segments.
166 ///
167 /// # Example
168 ///
169 /// ```rust
170 /// # #[macro_use] extern crate rocket;
171 /// use rocket::http::uri::Origin;
172 ///
173 /// let uri = Origin::parse("/a%20b/b%2Fc/d//e?query=some").unwrap();
174 /// let path_segs: Vec<&str> = uri.path().segments().collect();
175 /// assert_eq!(path_segs, &["a b", "b/c", "d", "e"]);
176 /// ```
177 pub fn segments(&self) -> Segments<'a, fmt::Path> {
178 let cached = self.data.decoded_segments.get_or_init(|| {
179 let (indexed, path) = (&self.data.value, self.raw());
180 self.raw_segments()
181 .filter(|r| !r.is_empty())
182 .map(|s| decode_to_indexed_str::<fmt::Path>(s, (indexed, path)))
183 .collect()
184 });
185
186 Segments::new(self.raw(), cached)
187 }
188}
189
190impl<'a> Query<'a> {
191 /// Returns the raw, undecoded query value.
192 ///
193 /// # Example
194 ///
195 /// ```rust
196 /// # #[macro_use] extern crate rocket;
197 /// let uri = uri!("/foo?baz+bar");
198 /// assert_eq!(uri.query().unwrap(), "baz+bar");
199 /// assert_eq!(uri.query().unwrap().raw(), "baz+bar");
200 /// ```
201 pub fn raw(&self) -> &'a RawStr {
202 self.data.value.from_cow_source(self.source).into()
203 }
204
205 /// Returns the raw, undecoded query value as an `&str`.
206 ///
207 /// # Example
208 ///
209 /// ```rust
210 /// # #[macro_use] extern crate rocket;
211 /// let uri = uri!("/foo/bar?baz+bar");
212 /// assert_eq!(uri.query().unwrap(), "baz+bar");
213 /// assert_eq!(uri.query().unwrap().as_str(), "baz+bar");
214 /// ```
215 pub fn as_str(&self) -> &'a str {
216 self.raw().as_str()
217 }
218
219 /// Whether `self` is normalized, i.e, it has no empty segments.
220 pub(crate) fn is_normalized(&self) -> bool {
221 !self.is_empty() && self.raw_segments().all(|s| !s.is_empty())
222 }
223
224 /// Normalizes `self`.
225 pub(crate) fn to_normalized(self) -> Option<Data<'static, fmt::Query>> {
226 let mut query = String::with_capacity(self.raw().len());
227 for (i, seg) in self.raw_segments().filter(|s| !s.is_empty()).enumerate() {
228 if i != 0 { query.push('&'); }
229 let _ = write!(query, "{}", seg);
230 }
231
232 if query.is_empty() {
233 return None;
234 }
235
236 Some(Data {
237 value: IndexedStr::from(Cow::Owned(query)),
238 decoded_segments: InitCell::new(),
239 })
240 }
241
242 /// Returns an iterator over the non-empty, undecoded `(name, value)` pairs
243 /// of this query. If there is no query, the iterator is empty. Segments may
244 /// be empty.
245 ///
246 /// # Example
247 ///
248 /// ```rust
249 /// # #[macro_use] extern crate rocket;
250 /// use rocket::http::uri::Origin;
251 ///
252 /// let uri = Origin::parse("/").unwrap();
253 /// assert!(uri.query().is_none());
254 ///
255 /// let uri = Origin::parse("/?a=b&dog").unwrap();
256 /// let query_segs: Vec<_> = uri.query().unwrap().raw_segments().collect();
257 /// assert_eq!(query_segs, &["a=b", "dog"]);
258 ///
259 /// // This is not normalized, so the query is `""`, the empty string.
260 /// let uri = Origin::parse("/?&").unwrap();
261 /// let query_segs: Vec<_> = uri.query().unwrap().raw_segments().collect();
262 /// assert_eq!(query_segs, &["", ""]);
263 ///
264 /// // Recall that `uri!()` normalizes.
265 /// let uri = uri!("/?&");
266 /// assert!(uri.query().is_none());
267 ///
268 /// // These are raw and undecoded. Use `segments()` for decoded variant.
269 /// let uri = Origin::parse("/foo/bar?a+b%2F=some+one%40gmail.com&&%26%3D2").unwrap();
270 /// let query_segs: Vec<_> = uri.query().unwrap().raw_segments().collect();
271 /// assert_eq!(query_segs, &["a+b%2F=some+one%40gmail.com", "", "%26%3D2"]);
272 /// ```
273 #[inline]
274 pub fn raw_segments(&self) -> impl Iterator<Item = &'a RawStr> {
275 let query = match self.raw() {
276 q if q.is_empty() => None,
277 q => Some(q)
278 };
279
280 query.map(|p| p.split(fmt::Query::DELIMITER))
281 .into_iter()
282 .flatten()
283 }
284
285 /// Returns a (smart) iterator over the non-empty, url-decoded `(name,
286 /// value)` pairs of this query. If there is no query, the iterator is
287 /// empty.
288 ///
289 /// # Example
290 ///
291 /// ```rust
292 /// # #[macro_use] extern crate rocket;
293 /// use rocket::http::uri::Origin;
294 ///
295 /// let uri = Origin::parse("/").unwrap();
296 /// assert!(uri.query().is_none());
297 ///
298 /// let uri = Origin::parse("/foo/bar?a+b%2F=some+one%40gmail.com&&%26%3D2").unwrap();
299 /// let query_segs: Vec<_> = uri.query().unwrap().segments().collect();
300 /// assert_eq!(query_segs, &[("a b/", "some one@gmail.com"), ("&=2", "")]);
301 /// ```
302 pub fn segments(&self) -> Segments<'a, fmt::Query> {
303 let cached = self.data.decoded_segments.get_or_init(|| {
304 let (indexed, query) = (&self.data.value, self.raw());
305 self.raw_segments()
306 .filter(|s| !s.is_empty())
307 .map(|s| s.split_at_byte(b'='))
308 .map(|(k, v)| {
309 let key = decode_to_indexed_str::<fmt::Query>(k, (indexed, query));
310 let val = decode_to_indexed_str::<fmt::Query>(v, (indexed, query));
311 (key, val)
312 })
313 .collect()
314 });
315
316 Segments::new(self.raw(), cached)
317 }
318}
319
320macro_rules! impl_partial_eq {
321 ($A:ty = $B:ty) => (
322 impl PartialEq<$A> for $B {
323 #[inline(always)]
324 fn eq(&self, other: &$A) -> bool {
325 let left: &RawStr = self.as_ref();
326 let right: &RawStr = other.as_ref();
327 left == right
328 }
329 }
330 )
331}
332
333macro_rules! impl_traits {
334 ($T:ident) => (
335 impl Hash for $T<'_> {
336 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
337 self.raw().hash(state);
338 }
339 }
340
341 impl Eq for $T<'_> { }
342
343 impl IntoOwned for Data<'_, fmt::$T> {
344 type Owned = Data<'static, fmt::$T>;
345
346 fn into_owned(self) -> Self::Owned {
347 Data {
348 value: self.value.into_owned(),
349 decoded_segments: self.decoded_segments.map(|v| v.into_owned()),
350 }
351 }
352 }
353
354 impl std::ops::Deref for $T<'_> {
355 type Target = RawStr;
356
357 fn deref(&self) -> &Self::Target {
358 self.raw()
359 }
360 }
361
362 impl AsRef<RawStr> for $T<'_> {
363 fn as_ref(&self) -> &RawStr {
364 self.raw()
365 }
366 }
367
368 impl std::fmt::Display for $T<'_> {
369 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
370 write!(f, "{}", self.raw())
371 }
372 }
373
374 impl_partial_eq!($T<'_> = $T<'_>);
375 impl_partial_eq!(str = $T<'_>);
376 impl_partial_eq!(&str = $T<'_>);
377 impl_partial_eq!($T<'_> = str);
378 impl_partial_eq!($T<'_> = &str);
379 impl_partial_eq!(RawStr = $T<'_>);
380 impl_partial_eq!(&RawStr = $T<'_>);
381 impl_partial_eq!($T<'_> = RawStr);
382 impl_partial_eq!($T<'_> = &RawStr);
383 )
384}
385
386impl_traits!(Path);
387impl_traits!(Query);