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