rocket_http/raw_str.rs
1use std::borrow::{Borrow, Cow};
2use std::convert::AsRef;
3use std::cmp::Ordering;
4use std::str::Utf8Error;
5use std::fmt;
6
7use ref_cast::RefCast;
8use stable_pattern::{Pattern, Searcher, ReverseSearcher, Split, SplitInternal};
9use crate::uri::fmt::{DEFAULT_ENCODE_SET, percent_encode, percent_encode_bytes};
10
11use crate::uncased::UncasedStr;
12
13/// A reference to a string inside of a raw HTTP message.
14///
15/// A `RawStr` is an unsanitized, unvalidated, and undecoded raw string from an
16/// HTTP message. It exists to separate validated string inputs, represented by
17/// the `String`, `&str`, and `Cow<str>` types, from unvalidated inputs,
18/// represented by `&RawStr`.
19///
20/// # Validation
21///
22/// An `&RawStr` should be converted into one of the validated string input
23/// types through methods on `RawStr`. These methods are summarized below:
24///
25/// * **[`url_decode()`]** - used to decode a raw string in a form value
26/// context
27/// * **[`percent_decode()`], [`percent_decode_lossy()`]** - used to
28/// percent-decode a raw string, typically in a URL context
29/// * **[`html_escape()`]** - used to decode a string for use in HTML
30/// templates
31/// * **[`as_str()`]** - used when the `RawStr` is known to be safe in the
32/// context of its intended use. Use sparingly and with care!
33/// * **[`as_uncased_str()`]** - used when the `RawStr` is known to be safe in
34/// the context of its intended, uncased use
35///
36/// **Note:** Template engines like Tera and Handlebars all functions like
37/// [`html_escape()`] on all rendered template outputs by default.
38///
39/// [`as_str()`]: RawStr::as_str()
40/// [`as_uncased_str()`]: RawStr::as_uncased_str()
41/// [`url_decode()`]: RawStr::url_decode()
42/// [`html_escape()`]: RawStr::html_escape()
43/// [`percent_decode()`]: RawStr::percent_decode()
44/// [`percent_decode_lossy()`]: RawStr::percent_decode_lossy()
45///
46/// # Usage
47///
48/// A `RawStr` is a dynamically sized type (just like `str`). It is always used
49/// through a reference an as `&RawStr` (just like &str).
50#[repr(transparent)]
51#[derive(RefCast, PartialEq, Eq, PartialOrd, Ord, Hash)]
52pub struct RawStr(str);
53
54impl ToOwned for RawStr {
55 type Owned = RawStrBuf;
56
57 fn to_owned(&self) -> Self::Owned {
58 RawStrBuf(self.to_string())
59 }
60}
61
62/// An owned version of [`RawStr`].
63#[repr(transparent)]
64#[derive(RefCast, PartialEq, Eq, PartialOrd, Ord, Hash)]
65pub struct RawStrBuf(String);
66
67impl RawStrBuf {
68 /// Cost-free conversion from `self` into a `String`.
69 ///
70 /// # Example
71 ///
72 /// ```rust
73 /// # extern crate rocket;
74 /// use rocket::http::RawStrBuf;
75 ///
76 /// let raw = RawStrBuf::from(format!("hello {}", "world"));
77 /// let string = raw.into_string();
78 /// ```
79 pub fn into_string(self) -> String {
80 self.0
81 }
82}
83
84impl RawStr {
85 /// Constructs an `&RawStr` from a string-like type at no cost.
86 ///
87 /// # Example
88 ///
89 /// ```rust
90 /// # extern crate rocket;
91 /// use rocket::http::RawStr;
92 ///
93 /// let raw_str = RawStr::new("Hello, world!");
94 ///
95 /// // `into` can also be used; note that the type must be specified
96 /// let raw_str: &RawStr = "Hello, world!".into();
97 /// ```
98 pub fn new<S: AsRef<str> + ?Sized>(string: &S) -> &RawStr {
99 RawStr::ref_cast(string.as_ref())
100 }
101
102 /// Construct a `Cow<RawStr>` from a `Cow<Str>`. Does not allocate.
103 ///
104 /// See [`RawStr::into_cow_str()`] for the inverse operation.
105 ///
106 /// # Example
107 ///
108 /// ```rust
109 /// # extern crate rocket;
110 /// use std::borrow::Cow;
111 /// use rocket::http::RawStr;
112 ///
113 /// let cow_str = Cow::from("hello!");
114 /// let cow_raw = RawStr::from_cow_str(cow_str);
115 /// assert_eq!(cow_raw.as_str(), "hello!");
116 /// ```
117 pub fn from_cow_str(cow: Cow<'_, str>) -> Cow<'_, RawStr> {
118 match cow {
119 Cow::Borrowed(b) => Cow::Borrowed(b.into()),
120 Cow::Owned(b) => Cow::Owned(b.into()),
121 }
122 }
123
124 /// Construct a `Cow<str>` from a `Cow<RawStr>`. Does not allocate.
125 ///
126 /// See [`RawStr::from_cow_str()`] for the inverse operation.
127 ///
128 /// # Example
129 ///
130 /// ```rust
131 /// # extern crate rocket;
132 /// use std::borrow::Cow;
133 /// use rocket::http::RawStr;
134 ///
135 /// let cow_raw = Cow::from(RawStr::new("hello!"));
136 /// let cow_str = RawStr::into_cow_str(cow_raw);
137 /// assert_eq!(&*cow_str, "hello!");
138 /// ```
139 pub fn into_cow_str(cow: Cow<'_, RawStr>) -> Cow<'_, str> {
140 match cow {
141 Cow::Borrowed(b) => Cow::Borrowed(b.as_str()),
142 Cow::Owned(b) => Cow::Owned(b.into_string()),
143 }
144 }
145
146 /// Percent-decodes `self`.
147 fn _percent_decode(&self) -> percent_encoding::PercentDecode<'_> {
148 percent_encoding::percent_decode(self.as_bytes())
149 }
150
151 /// Returns a percent-decoded version of the string.
152 ///
153 /// # Errors
154 ///
155 /// Returns an `Err` if the percent encoded values are not valid UTF-8.
156 ///
157 /// # Example
158 ///
159 /// With a valid string:
160 ///
161 /// ```rust
162 /// # extern crate rocket;
163 /// use rocket::http::RawStr;
164 ///
165 /// let raw_str = RawStr::new("Hello%21");
166 /// let decoded = raw_str.percent_decode();
167 /// assert_eq!(decoded, Ok("Hello!".into()));
168 /// ```
169 ///
170 /// With an invalid string:
171 ///
172 /// ```rust
173 /// # extern crate rocket;
174 /// use rocket::http::RawStr;
175 ///
176 /// let bad_raw_str = RawStr::new("%FF");
177 /// assert!(bad_raw_str.percent_decode().is_err());
178 /// ```
179 #[inline(always)]
180 pub fn percent_decode(&self) -> Result<Cow<'_, str>, Utf8Error> {
181 self._percent_decode().decode_utf8()
182 }
183
184 /// Returns a percent-decoded version of the string. Any invalid UTF-8
185 /// percent-encoded byte sequences will be replaced � U+FFFD, the
186 /// replacement character.
187 ///
188 /// # Example
189 ///
190 /// With a valid string:
191 ///
192 /// ```rust
193 /// # extern crate rocket;
194 /// use rocket::http::RawStr;
195 ///
196 /// let raw_str = RawStr::new("Hello%21");
197 /// let decoded = raw_str.percent_decode_lossy();
198 /// assert_eq!(decoded, "Hello!");
199 /// ```
200 ///
201 /// With an invalid string:
202 ///
203 /// ```rust
204 /// # extern crate rocket;
205 /// use rocket::http::RawStr;
206 ///
207 /// let bad_raw_str = RawStr::new("a=%FF");
208 /// assert_eq!(bad_raw_str.percent_decode_lossy(), "a=�");
209 /// ```
210 #[inline(always)]
211 pub fn percent_decode_lossy(&self) -> Cow<'_, str> {
212 self._percent_decode().decode_utf8_lossy()
213 }
214
215 /// Replaces '+' with ' ' in `self`, allocating only when necessary.
216 fn _replace_plus(&self) -> Cow<'_, str> {
217 let string = self.as_str();
218 let mut allocated = String::new(); // this is allocation free
219 for i in memchr::memchr_iter(b'+', string.as_bytes()) {
220 if allocated.is_empty() {
221 allocated = string.into();
222 }
223
224 // SAFETY:
225 //
226 // 1. The caller must ensure that the content of the slice is valid
227 // UTF-8 before the borrow ends and the underlying `str` is used.
228 //
229 // `allocated[i]` is `+` since that is what we searched for. The
230 // `+` char is ASCII => the character is one byte wide. ' ' is
231 // also one byte and ASCII => UTF-8. The replacement of `+` with
232 // ` ` thus yields a valid UTF-8 string.
233 unsafe { allocated.as_bytes_mut()[i] = b' '; }
234 }
235
236 match allocated.is_empty() {
237 true => Cow::Borrowed(string),
238 false => Cow::Owned(allocated)
239 }
240 }
241
242 /// Returns a percent-encoded version of the string.
243 ///
244 /// # Example
245 ///
246 /// With a valid string:
247 ///
248 /// ```rust
249 /// # extern crate rocket;
250 /// use rocket::http::RawStr;
251 ///
252 /// let raw_str = RawStr::new("Hello%21");
253 /// let decoded = raw_str.percent_decode();
254 /// assert_eq!(decoded, Ok("Hello!".into()));
255 /// ```
256 ///
257 /// With an invalid string:
258 ///
259 /// ```rust
260 /// # extern crate rocket;
261 /// use rocket::http::RawStr;
262 ///
263 /// let bad_raw_str = RawStr::new("%FF");
264 /// assert!(bad_raw_str.percent_decode().is_err());
265 /// ```
266 #[inline(always)]
267 pub fn percent_encode(&self) -> Cow<'_, RawStr> {
268 Self::from_cow_str(percent_encode::<DEFAULT_ENCODE_SET>(self))
269 }
270
271 /// Returns a percent-encoded version of `bytes`.
272 ///
273 /// # Example
274 ///
275 /// ```rust
276 /// # extern crate rocket;
277 /// use rocket::http::RawStr;
278 ///
279 /// // Note: Rocket should never hand you a bad `&RawStr`.
280 /// let bytes = &[93, 12, 0, 13, 1];
281 /// let encoded = RawStr::percent_encode_bytes(&bytes[..]);
282 /// ```
283 #[inline(always)]
284 pub fn percent_encode_bytes(bytes: &[u8]) -> Cow<'_, RawStr> {
285 Self::from_cow_str(percent_encode_bytes::<DEFAULT_ENCODE_SET>(bytes))
286 }
287
288 /// Returns a URL-decoded version of the string. This is identical to
289 /// percent decoding except that `+` characters are converted into spaces.
290 /// This is the encoding used by form values.
291 ///
292 /// # Errors
293 ///
294 /// Returns an `Err` if the percent encoded values are not valid UTF-8.
295 ///
296 /// # Example
297 ///
298 /// ```rust
299 /// # extern crate rocket;
300 /// use rocket::http::RawStr;
301 ///
302 /// let raw_str = RawStr::new("Hello%2C+world%21");
303 /// let decoded = raw_str.url_decode();
304 /// assert_eq!(decoded.unwrap(), "Hello, world!");
305 /// ```
306 pub fn url_decode(&self) -> Result<Cow<'_, str>, Utf8Error> {
307 let string = self._replace_plus();
308 match percent_encoding::percent_decode(string.as_bytes()).decode_utf8()? {
309 Cow::Owned(s) => Ok(Cow::Owned(s)),
310 Cow::Borrowed(_) => Ok(string)
311 }
312 }
313
314 /// Returns a URL-decoded version of the string.
315 ///
316 /// Any invalid UTF-8 percent-encoded byte sequences will be replaced �
317 /// U+FFFD, the replacement character. This is identical to lossy percent
318 /// decoding except that `+` characters are converted into spaces. This is
319 /// the encoding used by form values.
320 ///
321 /// # Example
322 ///
323 /// With a valid string:
324 ///
325 /// ```rust
326 /// # extern crate rocket;
327 /// use rocket::http::RawStr;
328 ///
329 /// let raw_str: &RawStr = "Hello%2C+world%21".into();
330 /// let decoded = raw_str.url_decode_lossy();
331 /// assert_eq!(decoded, "Hello, world!");
332 /// ```
333 ///
334 /// With an invalid string:
335 ///
336 /// ```rust
337 /// # extern crate rocket;
338 /// use rocket::http::RawStr;
339 ///
340 /// let bad_raw_str = RawStr::new("a+b=%FF");
341 /// assert_eq!(bad_raw_str.url_decode_lossy(), "a b=�");
342 /// ```
343 pub fn url_decode_lossy(&self) -> Cow<'_, str> {
344 let string = self._replace_plus();
345 match percent_encoding::percent_decode(string.as_bytes()).decode_utf8_lossy() {
346 Cow::Owned(s) => Cow::Owned(s),
347 Cow::Borrowed(_) => string
348 }
349 }
350
351 /// Returns an HTML escaped version of `self`. Allocates only when
352 /// characters need to be escaped.
353 ///
354 /// The following characters are escaped: `&`, `<`, `>`, `"`, `'`, `/`,
355 /// <code>`</code>. **This suffices as long as the escaped string is not
356 /// used in an execution context such as inside of <script> or <style>
357 /// tags!** See the [OWASP XSS Prevention Rules] for more information.
358 ///
359 /// [OWASP XSS Prevention Rules]: https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet#XSS_Prevention_Rules
360 ///
361 /// # Example
362 ///
363 /// Strings with HTML sequences are escaped:
364 ///
365 /// ```rust
366 /// # extern crate rocket;
367 /// use rocket::http::RawStr;
368 ///
369 /// let raw_str: &RawStr = "<b>Hi!</b>".into();
370 /// let escaped = raw_str.html_escape();
371 /// assert_eq!(escaped, "<b>Hi!</b>");
372 ///
373 /// let raw_str: &RawStr = "Hello, <i>world!</i>".into();
374 /// let escaped = raw_str.html_escape();
375 /// assert_eq!(escaped, "Hello, <i>world!</i>");
376 /// ```
377 ///
378 /// Strings without HTML sequences remain untouched:
379 ///
380 /// ```rust
381 /// # extern crate rocket;
382 /// use rocket::http::RawStr;
383 ///
384 /// let raw_str: &RawStr = "Hello!".into();
385 /// let escaped = raw_str.html_escape();
386 /// assert_eq!(escaped, "Hello!");
387 ///
388 /// let raw_str: &RawStr = "大阪".into();
389 /// let escaped = raw_str.html_escape();
390 /// assert_eq!(escaped, "大阪");
391 /// ```
392 // NOTE: This is the ~fastest (a table-based implementation is slightly
393 // faster) implementation benchmarked for dense-ish escaping. For sparser
394 // texts, a regex-based-find solution is much faster.
395 pub fn html_escape(&self) -> Cow<'_, str> {
396 let mut escaped = false;
397 let mut allocated = Vec::new(); // this is allocation free
398 for c in self.as_bytes() {
399 match *c {
400 b'&' | b'<' | b'>' | b'"' | b'\'' | b'/' | b'`' => {
401 if !escaped {
402 let i = (c as *const u8 as usize) - (self.as_ptr() as usize);
403 allocated = Vec::with_capacity(self.len() * 2);
404 allocated.extend_from_slice(&self.as_bytes()[..i]);
405 }
406
407 match *c {
408 b'&' => allocated.extend_from_slice(b"&"),
409 b'<' => allocated.extend_from_slice(b"<"),
410 b'>' => allocated.extend_from_slice(b">"),
411 b'"' => allocated.extend_from_slice(b"""),
412 b'\'' => allocated.extend_from_slice(b"'"),
413 b'/' => allocated.extend_from_slice(b"/"),
414 // Old versions of IE treat a ` as a '.
415 b'`' => allocated.extend_from_slice(b"`"),
416 _ => unreachable!()
417 }
418
419 escaped = true;
420 }
421 _ if escaped => allocated.push(*c),
422 _ => { }
423 }
424 }
425
426 if escaped {
427 // This use of `unsafe` is only correct if the bytes in `allocated`
428 // form a valid UTF-8 string. We prove this by cases:
429 //
430 // 1. In the `!escaped` branch, capacity for the vector is first
431 // allocated. No characters are pushed to `allocated` prior to
432 // this branch running since the `escaped` flag isn't set. To
433 // enter this branch, the current byte must be a valid ASCII
434 // character. This implies that every byte preceding forms a
435 // valid UTF-8 string since ASCII characters in UTF-8 are never
436 // part of a multi-byte sequence. Thus, extending the `allocated`
437 // vector with these bytes results in a valid UTF-8 string in
438 // `allocated`.
439 //
440 // 2. After the `!escaped` branch, `allocated` is extended with a
441 // byte string of valid ASCII characters. Thus, `allocated` is
442 // still a valid UTF-8 string.
443 //
444 // 3. In the `_ if escaped` branch, the byte is simply pushed into
445 // the `allocated` vector. At this point, `allocated` may contain
446 // an invalid UTF-8 string as we are not a known boundary.
447 // However, note that this byte is part of a known valid string
448 // (`self`). If the byte is not part of a multi-byte sequence, it
449 // is ASCII, and no further justification is needed. If the byte
450 // _is_ part of a multi-byte sequence, it is _not_ ASCII, and
451 // thus will not fall into the escaped character set and it will
452 // be pushed into `allocated` subsequently, resulting in a valid
453 // UTF-8 string in `allocated`.
454 unsafe { Cow::Owned(String::from_utf8_unchecked(allocated)) }
455 } else {
456 Cow::Borrowed(self.as_str())
457 }
458 }
459
460 /// Returns the length of `self`.
461 ///
462 /// This length is in bytes, not [`char`]s or graphemes. In other words,
463 /// it may not be what a human considers the length of the string.
464 ///
465 /// # Example
466 ///
467 /// ```rust
468 /// # extern crate rocket;
469 /// use rocket::http::RawStr;
470 ///
471 /// let raw_str = RawStr::new("Hello, world!");
472 /// assert_eq!(raw_str.len(), 13);
473 /// ```
474 #[inline]
475 pub const fn len(&self) -> usize {
476 self.0.len()
477 }
478
479 /// Returns `true` if `self` has a length of zero bytes.
480 ///
481 /// # Example
482 ///
483 /// ```rust
484 /// # extern crate rocket;
485 /// use rocket::http::RawStr;
486 ///
487 /// let raw_str = RawStr::new("Hello, world!");
488 /// assert!(!raw_str.is_empty());
489 ///
490 /// let raw_str = RawStr::new("");
491 /// assert!(raw_str.is_empty());
492 /// ```
493 #[inline]
494 pub const fn is_empty(&self) -> bool {
495 self.len() == 0
496 }
497
498 /// Converts `self` into an `&str`.
499 ///
500 /// This method should be used sparingly. **Only use this method when you
501 /// are absolutely certain that doing so is safe.**
502 ///
503 /// # Example
504 ///
505 /// ```rust
506 /// # extern crate rocket;
507 /// use rocket::http::RawStr;
508 ///
509 /// let raw_str = RawStr::new("Hello, world!");
510 /// assert_eq!(raw_str.as_str(), "Hello, world!");
511 /// ```
512 #[inline(always)]
513 pub const fn as_str(&self) -> &str {
514 &self.0
515 }
516
517 /// Converts `self` into an `&[u8]`.
518 ///
519 /// # Example
520 ///
521 /// ```rust
522 /// # extern crate rocket;
523 /// use rocket::http::RawStr;
524 ///
525 /// let raw_str = RawStr::new("hi");
526 /// assert_eq!(raw_str.as_bytes(), &[0x68, 0x69]);
527 /// ```
528 #[inline(always)]
529 pub const fn as_bytes(&self) -> &[u8] {
530 self.0.as_bytes()
531 }
532
533 /// Converts a string slice to a raw pointer.
534 ///
535 /// As string slices are a slice of bytes, the raw pointer points to a
536 /// [`u8`]. This pointer will be pointing to the first byte of the string
537 /// slice.
538 ///
539 /// The caller must ensure that the returned pointer is never written to.
540 /// If you need to mutate the contents of the string slice, use [`as_mut_ptr`].
541 ///
542 /// [`as_mut_ptr`]: str::as_mut_ptr
543 ///
544 /// # Examples
545 ///
546 /// Basic usage:
547 ///
548 /// ```
549 /// # extern crate rocket;
550 /// use rocket::http::RawStr;
551 ///
552 /// let raw_str = RawStr::new("hi");
553 /// let ptr = raw_str.as_ptr();
554 /// ```
555 pub const fn as_ptr(&self) -> *const u8 {
556 self.as_str().as_ptr()
557 }
558
559 /// Converts `self` into an `&UncasedStr`.
560 ///
561 /// This method should be used sparingly. **Only use this method when you
562 /// are absolutely certain that doing so is safe.**
563 ///
564 /// # Example
565 ///
566 /// ```rust
567 /// # extern crate rocket;
568 /// use rocket::http::RawStr;
569 ///
570 /// let raw_str = RawStr::new("Content-Type");
571 /// assert!(raw_str.as_uncased_str() == "content-TYPE");
572 /// ```
573 #[inline(always)]
574 pub fn as_uncased_str(&self) -> &UncasedStr {
575 self.as_str().into()
576 }
577
578 /// Returns `true` if the given pattern matches a sub-slice of
579 /// this string slice.
580 ///
581 /// Returns `false` if it does not.
582 ///
583 /// The pattern can be a `&str`, [`char`], a slice of [`char`]s, or a
584 /// function or closure that determines if a character matches.
585 ///
586 /// [`char`]: prim@char
587 ///
588 /// # Examples
589 ///
590 /// Basic usage:
591 ///
592 /// ```
593 /// # extern crate rocket;
594 /// use rocket::http::RawStr;
595 ///
596 /// let bananas = RawStr::new("bananas");
597 ///
598 /// assert!(bananas.contains("nana"));
599 /// assert!(!bananas.contains("apples"));
600 /// ```
601 #[inline]
602 pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool {
603 pat.is_contained_in(self.as_str())
604 }
605
606 /// Returns `true` if the given pattern matches a prefix of this
607 /// string slice.
608 ///
609 /// Returns `false` if it does not.
610 ///
611 /// The pattern can be a `&str`, [`char`], a slice of [`char`]s, or a
612 /// function or closure that determines if a character matches.
613 ///
614 /// [`char`]: prim@char
615 ///
616 /// # Examples
617 ///
618 /// Basic usage:
619 ///
620 /// ```
621 /// # extern crate rocket;
622 /// use rocket::http::RawStr;
623 ///
624 /// let bananas = RawStr::new("bananas");
625 ///
626 /// assert!(bananas.starts_with("bana"));
627 /// assert!(!bananas.starts_with("nana"));
628 /// ```
629 pub fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool {
630 pat.is_prefix_of(self.as_str())
631 }
632
633 /// Returns `true` if the given pattern matches a suffix of this
634 /// string slice.
635 ///
636 /// Returns `false` if it does not.
637 ///
638 /// The pattern can be a `&str`, [`char`], a slice of [`char`]s, or a
639 /// function or closure that determines if a character matches.
640 ///
641 /// [`char`]: prim@char
642 ///
643 /// # Examples
644 ///
645 /// Basic usage:
646 ///
647 /// ```
648 /// # extern crate rocket;
649 /// use rocket::http::RawStr;
650 ///
651 /// let bananas = RawStr::new("bananas");
652 ///
653 /// assert!(bananas.ends_with("anas"));
654 /// assert!(!bananas.ends_with("nana"));
655 /// ```
656 pub fn ends_with<'a, P>(&'a self, pat: P) -> bool
657 where P: Pattern<'a>, <P as Pattern<'a>>::Searcher: ReverseSearcher<'a>
658 {
659 pat.is_suffix_of(self.as_str())
660 }
661
662
663 /// Returns the byte index of the first character of this string slice that
664 /// matches the pattern.
665 ///
666 /// Returns [`None`] if the pattern doesn't match.
667 ///
668 /// The pattern can be a `&str`, [`char`], a slice of [`char`]s, or a
669 /// function or closure that determines if a character matches.
670 ///
671 /// [`char`]: prim@char
672 ///
673 /// # Example
674 ///
675 /// ```
676 /// # extern crate rocket;
677 /// use rocket::http::RawStr;
678 ///
679 /// let s = RawStr::new("Löwe 老虎 Léopard Gepardi");
680 ///
681 /// assert_eq!(s.find('L'), Some(0));
682 /// assert_eq!(s.find('é'), Some(14));
683 /// assert_eq!(s.find("pard"), Some(17));
684 /// ```
685 #[inline]
686 pub fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize> {
687 pat.into_searcher(self.as_str()).next_match().map(|(i, _)| i)
688 }
689
690 /// An iterator over substrings of this string slice, separated by
691 /// characters matched by a pattern.
692 ///
693 /// The pattern can be a `&str`, [`char`], a slice of [`char`]s, or a
694 /// function or closure that determines if a character matches.
695 ///
696 /// [`char`]: prim@char
697 ///
698 /// # Examples
699 ///
700 /// Simple patterns:
701 ///
702 /// ```
703 /// # extern crate rocket;
704 /// use rocket::http::RawStr;
705 ///
706 /// let v: Vec<_> = RawStr::new("Mary had a little lamb")
707 /// .split(' ')
708 /// .map(|r| r.as_str())
709 /// .collect();
710 ///
711 /// assert_eq!(v, ["Mary", "had", "a", "little", "lamb"]);
712 /// ```
713 #[inline]
714 pub fn split<'a, P>(&'a self, pat: P) -> impl Iterator<Item = &'a RawStr>
715 where P: Pattern<'a>
716 {
717 let split: Split<'_, P> = Split(SplitInternal {
718 start: 0,
719 end: self.len(),
720 matcher: pat.into_searcher(self.as_str()),
721 allow_trailing_empty: true,
722 finished: false,
723 });
724
725 split.map(|s| s.into())
726 }
727
728 /// Splits `self` into two pieces: the piece _before_ the first byte `b` and
729 /// the piece _after_ (not including `b`). Returns the tuple (`before`,
730 /// `after`). If `b` is not in `self`, or `b` is not an ASCII characters,
731 /// returns the entire string `self` as `before` and the empty string as
732 /// `after`.
733 ///
734 /// # Example
735 ///
736 /// ```rust
737 /// # extern crate rocket;
738 /// use rocket::http::RawStr;
739 ///
740 /// let haystack = RawStr::new("a good boy!");
741 ///
742 /// let (before, after) = haystack.split_at_byte(b'a');
743 /// assert_eq!(before, "");
744 /// assert_eq!(after, " good boy!");
745 ///
746 /// let (before, after) = haystack.split_at_byte(b' ');
747 /// assert_eq!(before, "a");
748 /// assert_eq!(after, "good boy!");
749 ///
750 /// let (before, after) = haystack.split_at_byte(b'o');
751 /// assert_eq!(before, "a g");
752 /// assert_eq!(after, "od boy!");
753 ///
754 /// let (before, after) = haystack.split_at_byte(b'!');
755 /// assert_eq!(before, "a good boy");
756 /// assert_eq!(after, "");
757 ///
758 /// let (before, after) = haystack.split_at_byte(b'?');
759 /// assert_eq!(before, "a good boy!");
760 /// assert_eq!(after, "");
761 ///
762 /// let haystack = RawStr::new("");
763 /// let (before, after) = haystack.split_at_byte(b' ');
764 /// assert_eq!(before, "");
765 /// assert_eq!(after, "");
766 /// ```
767 #[inline]
768 pub fn split_at_byte(&self, b: u8) -> (&RawStr, &RawStr) {
769 if !b.is_ascii() {
770 return (self, &self[0..0]);
771 }
772
773 match memchr::memchr(b, self.as_bytes()) {
774 // SAFETY: `b` is a character boundary since it's ASCII, `i` is in
775 // bounds in `self` (or else None), and i is at most len - 1, so i +
776 // 1 is at most len.
777 Some(i) => unsafe {
778 let s = self.as_str();
779 let start = s.get_unchecked(0..i);
780 let end = s.get_unchecked((i + 1)..self.len());
781 (start.into(), end.into())
782 },
783 None => (self, &self[0..0])
784 }
785 }
786
787 /// Returns a string slice with the prefix removed.
788 ///
789 /// If the string starts with the pattern `prefix`, returns substring after
790 /// the prefix, wrapped in `Some`. This method removes the prefix exactly
791 /// once.
792 ///
793 /// If the string does not start with `prefix`, returns `None`.
794 ///
795 /// The pattern can be a `&str`, `char`, a slice of `char`s, or a function
796 /// or closure that determines if a character matches.
797 ///
798 /// # Examples
799 ///
800 /// ```
801 /// # extern crate rocket;
802 /// use rocket::http::RawStr;
803 ///
804 /// assert_eq!(RawStr::new("foo:bar").strip_prefix("foo:").unwrap(), "bar");
805 /// assert_eq!(RawStr::new("foofoo").strip_prefix("foo").unwrap(), "foo");
806 /// assert!(RawStr::new("foo:bar").strip_prefix("bar").is_none());
807 /// ```
808 #[inline]
809 pub fn strip_prefix<'a, P: Pattern<'a>>(&'a self, prefix: P) -> Option<&'a RawStr> {
810 prefix.strip_prefix_of(self.as_str()).map(RawStr::new)
811 }
812
813 /// Returns a string slice with the suffix removed.
814 ///
815 /// If the string ends with the pattern `suffix`, returns the substring
816 /// before the suffix, wrapped in `Some`. Unlike `trim_end_matches`, this
817 /// method removes the suffix exactly once.
818 ///
819 /// If the string does not end with `suffix`, returns `None`.
820 ///
821 /// The pattern can be a `&str`, `char`, a slice of `char`s, or a function
822 /// or closure that determines if a character matches.
823 ///
824 /// # Examples
825 ///
826 /// ```
827 /// # extern crate rocket;
828 /// use rocket::http::RawStr;
829 ///
830 /// assert_eq!(RawStr::new("bar:foo").strip_suffix(":foo").unwrap(), "bar");
831 /// assert_eq!(RawStr::new("foofoo").strip_suffix("foo").unwrap(), "foo");
832 /// assert!(RawStr::new("bar:foo").strip_suffix("bar").is_none());
833 /// ```
834 #[inline]
835 pub fn strip_suffix<'a, P>(&'a self, suffix: P) -> Option<&'a RawStr>
836 where P: Pattern<'a>,<P as Pattern<'a>>::Searcher: ReverseSearcher<'a>,
837 {
838 suffix.strip_suffix_of(self.as_str()).map(RawStr::new)
839 }
840
841 /// Parses this string slice into another type.
842 ///
843 /// Because `parse` is so general, it can cause problems with type
844 /// inference. As such, `parse` is one of the few times you'll see
845 /// the syntax affectionately known as the 'turbofish': `::<>`. This
846 /// helps the inference algorithm understand specifically which type
847 /// you're trying to parse into.
848 ///
849 /// # Errors
850 ///
851 /// Will return `Err` if it's not possible to parse this string slice into
852 /// the desired type.
853 ///
854 /// # Examples
855 ///
856 /// Basic usage
857 ///
858 /// ```
859 /// # extern crate rocket;
860 /// use rocket::http::RawStr;
861 ///
862 /// let four: u32 = RawStr::new("4").parse().unwrap();
863 ///
864 /// assert_eq!(4, four);
865 /// ```
866 #[inline]
867 pub fn parse<F: std::str::FromStr>(&self) -> Result<F, F::Err> {
868 std::str::FromStr::from_str(self.as_str())
869 }
870}
871
872#[cfg(feature = "serde")]
873mod serde {
874 use serde_::{ser, de, Serialize, Deserialize};
875
876 use super::*;
877
878 impl Serialize for RawStr {
879 fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
880 where S: ser::Serializer
881 {
882 self.as_str().serialize(ser)
883 }
884 }
885
886 impl<'de: 'a, 'a> Deserialize<'de> for &'a RawStr {
887 fn deserialize<D>(de: D) -> Result<Self, D::Error>
888 where D: de::Deserializer<'de>
889 {
890 <&'a str as Deserialize<'de>>::deserialize(de).map(RawStr::new)
891 }
892 }
893
894}
895
896impl fmt::Debug for RawStr {
897 #[inline(always)]
898 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
899 self.0.fmt(f)
900 }
901}
902
903impl<'a> From<&'a str> for &'a RawStr {
904 #[inline(always)]
905 fn from(string: &'a str) -> &'a RawStr {
906 RawStr::new(string)
907 }
908}
909
910impl<'a> From<&'a RawStr> for Cow<'a, RawStr> {
911 fn from(raw: &'a RawStr) -> Self {
912 Cow::Borrowed(raw)
913 }
914}
915
916impl From<RawStrBuf> for Cow<'_, RawStr> {
917 fn from(raw: RawStrBuf) -> Self {
918 Cow::Owned(raw)
919 }
920}
921
922macro_rules! impl_partial {
923 ($A:ty : $B:ty as $T:ty) => (
924 impl PartialEq<$A> for $B {
925 #[inline(always)]
926 fn eq(&self, other: &$A) -> bool {
927 let left: $T = self.as_ref();
928 let right: $T = other.as_ref();
929 left == right
930 }
931 }
932
933 impl PartialOrd<$A> for $B {
934 #[inline(always)]
935 fn partial_cmp(&self, other: &$A) -> Option<Ordering> {
936 let left: $T = self.as_ref();
937 let right: $T = other.as_ref();
938 left.partial_cmp(right)
939 }
940 }
941 );
942 ($A:ty : $B:ty) => (impl_partial!($A : $B as &str);)
943}
944
945impl_partial!(RawStr : &RawStr);
946impl_partial!(&RawStr : RawStr);
947
948impl_partial!(str : RawStr);
949impl_partial!(str : &RawStr);
950impl_partial!(&str : RawStr);
951impl_partial!(&&str : RawStr);
952
953impl_partial!(Cow<'_, str> : RawStr);
954impl_partial!(Cow<'_, str> : &RawStr);
955impl_partial!(RawStr : Cow<'_, str>);
956impl_partial!(&RawStr : Cow<'_, str>);
957
958impl_partial!(Cow<'_, RawStr> : RawStr as &RawStr);
959impl_partial!(Cow<'_, RawStr> : &RawStr as &RawStr);
960impl_partial!(RawStr : Cow<'_, RawStr> as &RawStr);
961impl_partial!(&RawStr : Cow<'_, RawStr> as &RawStr);
962
963impl_partial!(String : RawStr);
964impl_partial!(String : &RawStr);
965
966impl_partial!(RawStr : String);
967impl_partial!(&RawStr : String);
968
969impl_partial!(RawStr : str);
970impl_partial!(RawStr : &str);
971impl_partial!(RawStr : &&str);
972impl_partial!(&RawStr : str);
973
974impl AsRef<str> for RawStr {
975 #[inline(always)]
976 fn as_ref(&self) -> &str {
977 self.as_str()
978 }
979}
980
981impl AsRef<RawStr> for str {
982 #[inline(always)]
983 fn as_ref(&self) -> &RawStr {
984 RawStr::new(self)
985 }
986}
987
988impl AsRef<RawStr> for RawStr {
989 #[inline(always)]
990 fn as_ref(&self) -> &RawStr {
991 self
992 }
993}
994
995impl AsRef<[u8]> for RawStr {
996 #[inline(always)]
997 fn as_ref(&self) -> &[u8] {
998 self.as_bytes()
999 }
1000}
1001
1002impl<I: core::slice::SliceIndex<str, Output=str>> core::ops::Index<I> for RawStr {
1003 type Output = RawStr;
1004
1005 #[inline]
1006 fn index(&self, index: I) -> &Self::Output {
1007 self.as_str()[index].into()
1008 }
1009}
1010
1011impl std::borrow::Borrow<str> for RawStr {
1012 #[inline(always)]
1013 fn borrow(&self) -> &str {
1014 self.as_str()
1015 }
1016}
1017
1018impl std::borrow::Borrow<RawStr> for &str {
1019 #[inline(always)]
1020 fn borrow(&self) -> &RawStr {
1021 (*self).into()
1022 }
1023}
1024
1025impl fmt::Display for RawStr {
1026 #[inline(always)]
1027 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1028 self.0.fmt(f)
1029 }
1030}
1031
1032impl AsRef<RawStr> for RawStrBuf {
1033 #[inline(always)]
1034 fn as_ref(&self) -> &RawStr {
1035 RawStr::new(self.0.as_str())
1036 }
1037}
1038
1039impl Borrow<RawStr> for RawStrBuf {
1040 #[inline(always)]
1041 fn borrow(&self) -> &RawStr {
1042 self.as_ref()
1043 }
1044}
1045
1046impl std::ops::Deref for RawStrBuf {
1047 type Target = RawStr;
1048
1049 #[inline(always)]
1050 fn deref(&self) -> &Self::Target {
1051 self.as_ref()
1052 }
1053}
1054
1055impl fmt::Display for RawStrBuf {
1056 #[inline(always)]
1057 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1058 self.0.fmt(f)
1059 }
1060}
1061
1062impl fmt::Debug for RawStrBuf {
1063 #[inline(always)]
1064 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1065 self.0.fmt(f)
1066 }
1067}
1068
1069impl From<String> for RawStrBuf {
1070 #[inline(always)]
1071 fn from(string: String) -> Self {
1072 RawStrBuf(string)
1073 }
1074}
1075
1076impl From<&str> for RawStrBuf {
1077 #[inline(always)]
1078 fn from(string: &str) -> Self {
1079 string.to_string().into()
1080 }
1081}
1082
1083impl From<&RawStr> for RawStrBuf {
1084 #[inline(always)]
1085 fn from(raw: &RawStr) -> Self {
1086 raw.to_string().into()
1087 }
1088}
1089
1090#[cfg(test)]
1091mod tests {
1092 use super::RawStr;
1093
1094 #[test]
1095 fn can_compare() {
1096 let raw_str = RawStr::new("abc");
1097 assert_eq!(raw_str, "abc");
1098 assert_eq!("abc", raw_str.as_str());
1099 assert_eq!(raw_str, RawStr::new("abc"));
1100 assert_eq!(raw_str, "abc".to_string());
1101 assert_eq!("abc".to_string(), raw_str.as_str());
1102 }
1103}