1use std::convert::TryFrom;
2use std::str::FromStr;
3use std::{cmp, fmt, hash, str};
4
5use bytes::Bytes;
6
7use super::{ErrorKind, InvalidUri};
8use crate::byte_str::ByteStr;
9
10#[derive(Clone)]
12pub struct PathAndQuery {
13 pub(super) data: ByteStr,
14 pub(super) query: u16,
15}
16
17const NONE: u16 = u16::MAX;
18
19impl PathAndQuery {
20 pub(super) fn from_shared(mut src: Bytes) -> Result<Self, InvalidUri> {
22 let mut query = NONE;
23 let mut fragment = None;
24
25 {
27 let mut iter = src.as_ref().iter().enumerate();
28
29 for (i, &b) in &mut iter {
31 match b {
33 b'?' => {
34 debug_assert_eq!(query, NONE);
35 query = i as u16;
36 break;
37 }
38 b'#' => {
39 fragment = Some(i);
40 break;
41 }
42
43 #[rustfmt::skip]
47 0x21 |
48 0x24..=0x3B |
49 0x3D |
50 0x40..=0x5F |
51 0x61..=0x7A |
52 0x7C |
53 0x7E => {}
54
55 #[rustfmt::skip]
65 b'"' |
66 b'{' | b'}' => {}
67
68 _ => return Err(ErrorKind::InvalidUriChar.into()),
69 }
70 }
71
72 if query != NONE {
74 for (i, &b) in iter {
75 match b {
76 #[rustfmt::skip]
82 0x21 |
83 0x24..=0x3B |
84 0x3D |
85 0x3F..=0x7E => {}
86
87 b'#' => {
88 fragment = Some(i);
89 break;
90 }
91
92 _ => return Err(ErrorKind::InvalidUriChar.into()),
93 }
94 }
95 }
96 }
97
98 if let Some(i) = fragment {
99 src.truncate(i);
100 }
101
102 Ok(PathAndQuery {
103 data: unsafe { ByteStr::from_utf8_unchecked(src) },
104 query,
105 })
106 }
107
108 #[inline]
127 pub fn from_static(src: &'static str) -> Self {
128 let src = Bytes::from_static(src.as_bytes());
129
130 PathAndQuery::from_shared(src).unwrap()
131 }
132
133 pub fn from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri>
138 where
139 T: AsRef<[u8]> + 'static,
140 {
141 if_downcast_into!(T, Bytes, src, {
142 return PathAndQuery::from_shared(src);
143 });
144
145 PathAndQuery::try_from(src.as_ref())
146 }
147
148 pub(super) fn empty() -> Self {
149 PathAndQuery {
150 data: ByteStr::new(),
151 query: NONE,
152 }
153 }
154
155 pub(super) fn slash() -> Self {
156 PathAndQuery {
157 data: ByteStr::from_static("/"),
158 query: NONE,
159 }
160 }
161
162 pub(super) fn star() -> Self {
163 PathAndQuery {
164 data: ByteStr::from_static("*"),
165 query: NONE,
166 }
167 }
168
169 #[inline]
192 pub fn path(&self) -> &str {
193 let ret = if self.query == NONE {
194 &self.data[..]
195 } else {
196 &self.data[..self.query as usize]
197 };
198
199 if ret.is_empty() {
200 return "/";
201 }
202
203 ret
204 }
205
206 #[inline]
241 pub fn query(&self) -> Option<&str> {
242 if self.query == NONE {
243 None
244 } else {
245 let i = self.query + 1;
246 Some(&self.data[i as usize..])
247 }
248 }
249
250 #[inline]
272 pub fn as_str(&self) -> &str {
273 let ret = &self.data[..];
274 if ret.is_empty() {
275 return "/";
276 }
277 ret
278 }
279}
280
281impl<'a> TryFrom<&'a [u8]> for PathAndQuery {
282 type Error = InvalidUri;
283 #[inline]
284 fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
285 PathAndQuery::from_shared(Bytes::copy_from_slice(s))
286 }
287}
288
289impl<'a> TryFrom<&'a str> for PathAndQuery {
290 type Error = InvalidUri;
291 #[inline]
292 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
293 TryFrom::try_from(s.as_bytes())
294 }
295}
296
297impl TryFrom<Vec<u8>> for PathAndQuery {
298 type Error = InvalidUri;
299 #[inline]
300 fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
301 PathAndQuery::from_shared(vec.into())
302 }
303}
304
305impl TryFrom<String> for PathAndQuery {
306 type Error = InvalidUri;
307 #[inline]
308 fn try_from(s: String) -> Result<Self, Self::Error> {
309 PathAndQuery::from_shared(s.into())
310 }
311}
312
313impl TryFrom<&String> for PathAndQuery {
314 type Error = InvalidUri;
315 #[inline]
316 fn try_from(s: &String) -> Result<Self, Self::Error> {
317 TryFrom::try_from(s.as_bytes())
318 }
319}
320
321impl FromStr for PathAndQuery {
322 type Err = InvalidUri;
323 #[inline]
324 fn from_str(s: &str) -> Result<Self, InvalidUri> {
325 TryFrom::try_from(s)
326 }
327}
328
329impl fmt::Debug for PathAndQuery {
330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331 fmt::Display::fmt(self, f)
332 }
333}
334
335impl fmt::Display for PathAndQuery {
336 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
337 if !self.data.is_empty() {
338 match self.data.as_bytes()[0] {
339 b'/' | b'*' => write!(fmt, "{}", &self.data[..]),
340 _ => write!(fmt, "/{}", &self.data[..]),
341 }
342 } else {
343 write!(fmt, "/")
344 }
345 }
346}
347
348impl hash::Hash for PathAndQuery {
349 fn hash<H: hash::Hasher>(&self, state: &mut H) {
350 self.data.hash(state);
351 }
352}
353
354impl PartialEq for PathAndQuery {
357 #[inline]
358 fn eq(&self, other: &PathAndQuery) -> bool {
359 self.data == other.data
360 }
361}
362
363impl Eq for PathAndQuery {}
364
365impl PartialEq<str> for PathAndQuery {
366 #[inline]
367 fn eq(&self, other: &str) -> bool {
368 self.as_str() == other
369 }
370}
371
372impl<'a> PartialEq<PathAndQuery> for &'a str {
373 #[inline]
374 fn eq(&self, other: &PathAndQuery) -> bool {
375 self == &other.as_str()
376 }
377}
378
379impl<'a> PartialEq<&'a str> for PathAndQuery {
380 #[inline]
381 fn eq(&self, other: &&'a str) -> bool {
382 self.as_str() == *other
383 }
384}
385
386impl PartialEq<PathAndQuery> for str {
387 #[inline]
388 fn eq(&self, other: &PathAndQuery) -> bool {
389 self == other.as_str()
390 }
391}
392
393impl PartialEq<String> for PathAndQuery {
394 #[inline]
395 fn eq(&self, other: &String) -> bool {
396 self.as_str() == other.as_str()
397 }
398}
399
400impl PartialEq<PathAndQuery> for String {
401 #[inline]
402 fn eq(&self, other: &PathAndQuery) -> bool {
403 self.as_str() == other.as_str()
404 }
405}
406
407impl PartialOrd for PathAndQuery {
408 #[inline]
409 fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
410 self.as_str().partial_cmp(other.as_str())
411 }
412}
413
414impl PartialOrd<str> for PathAndQuery {
415 #[inline]
416 fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
417 self.as_str().partial_cmp(other)
418 }
419}
420
421impl PartialOrd<PathAndQuery> for str {
422 #[inline]
423 fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
424 self.partial_cmp(other.as_str())
425 }
426}
427
428impl<'a> PartialOrd<&'a str> for PathAndQuery {
429 #[inline]
430 fn partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering> {
431 self.as_str().partial_cmp(*other)
432 }
433}
434
435impl<'a> PartialOrd<PathAndQuery> for &'a str {
436 #[inline]
437 fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
438 self.partial_cmp(&other.as_str())
439 }
440}
441
442impl PartialOrd<String> for PathAndQuery {
443 #[inline]
444 fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
445 self.as_str().partial_cmp(other.as_str())
446 }
447}
448
449impl PartialOrd<PathAndQuery> for String {
450 #[inline]
451 fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
452 self.as_str().partial_cmp(other.as_str())
453 }
454}
455
456#[cfg(test)]
457mod tests {
458 use super::*;
459
460 #[test]
461 fn equal_to_self_of_same_path() {
462 let p1: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
463 let p2: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
464 assert_eq!(p1, p2);
465 assert_eq!(p2, p1);
466 }
467
468 #[test]
469 fn not_equal_to_self_of_different_path() {
470 let p1: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
471 let p2: PathAndQuery = "/world&foo=bar".parse().unwrap();
472 assert_ne!(p1, p2);
473 assert_ne!(p2, p1);
474 }
475
476 #[test]
477 fn equates_with_a_str() {
478 let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
479 assert_eq!(&path_and_query, "/hello/world&foo=bar");
480 assert_eq!("/hello/world&foo=bar", &path_and_query);
481 assert_eq!(path_and_query, "/hello/world&foo=bar");
482 assert_eq!("/hello/world&foo=bar", path_and_query);
483 }
484
485 #[test]
486 fn not_equal_with_a_str_of_a_different_path() {
487 let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
488 assert_ne!(&path_and_query, "/hello&foo=bar");
490 assert_ne!("/hello&foo=bar", &path_and_query);
491 assert_ne!(path_and_query, "/hello&foo=bar");
493 assert_ne!("/hello&foo=bar", path_and_query);
494 }
495
496 #[test]
497 fn equates_with_a_string() {
498 let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
499 assert_eq!(path_and_query, "/hello/world&foo=bar".to_string());
500 assert_eq!("/hello/world&foo=bar".to_string(), path_and_query);
501 }
502
503 #[test]
504 fn not_equal_with_a_string_of_a_different_path() {
505 let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
506 assert_ne!(path_and_query, "/hello&foo=bar".to_string());
507 assert_ne!("/hello&foo=bar".to_string(), path_and_query);
508 }
509
510 #[test]
511 fn compares_to_self() {
512 let p1: PathAndQuery = "/a/world&foo=bar".parse().unwrap();
513 let p2: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
514 assert!(p1 < p2);
515 assert!(p2 > p1);
516 }
517
518 #[test]
519 fn compares_with_a_str() {
520 let path_and_query: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
521 assert!(&path_and_query < "/c/world&foo=bar");
523 assert!("/c/world&foo=bar" > &path_and_query);
524 assert!(&path_and_query > "/a/world&foo=bar");
525 assert!("/a/world&foo=bar" < &path_and_query);
526
527 assert!(path_and_query < "/c/world&foo=bar");
529 assert!("/c/world&foo=bar" > path_and_query);
530 assert!(path_and_query > "/a/world&foo=bar");
531 assert!("/a/world&foo=bar" < path_and_query);
532 }
533
534 #[test]
535 fn compares_with_a_string() {
536 let path_and_query: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
537 assert!(path_and_query < "/c/world&foo=bar".to_string());
538 assert!("/c/world&foo=bar".to_string() > path_and_query);
539 assert!(path_and_query > "/a/world&foo=bar".to_string());
540 assert!("/a/world&foo=bar".to_string() < path_and_query);
541 }
542
543 #[test]
544 fn ignores_valid_percent_encodings() {
545 assert_eq!("/a%20b", pq("/a%20b?r=1").path());
546 assert_eq!("qr=%31", pq("/a/b?qr=%31").query().unwrap());
547 }
548
549 #[test]
550 fn ignores_invalid_percent_encodings() {
551 assert_eq!("/a%%b", pq("/a%%b?r=1").path());
552 assert_eq!("/aaa%", pq("/aaa%").path());
553 assert_eq!("/aaa%", pq("/aaa%?r=1").path());
554 assert_eq!("/aa%2", pq("/aa%2").path());
555 assert_eq!("/aa%2", pq("/aa%2?r=1").path());
556 assert_eq!("qr=%3", pq("/a/b?qr=%3").query().unwrap());
557 }
558
559 #[test]
560 fn json_is_fine() {
561 assert_eq!(
562 r#"/{"bread":"baguette"}"#,
563 pq(r#"/{"bread":"baguette"}"#).path()
564 );
565 }
566
567 fn pq(s: &str) -> PathAndQuery {
568 s.parse().expect(&format!("parsing {}", s))
569 }
570}