rocket_http_community/uri/
path_query.rs

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