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