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 &lt;script> or &lt;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, "&lt;b&gt;Hi!&lt;&#x2F;b&gt;");
374    ///
375    /// let raw_str: &RawStr = "Hello, <i>world!</i>".into();
376    /// let escaped = raw_str.html_escape();
377    /// assert_eq!(escaped, "Hello, &lt;i&gt;world!&lt;&#x2F;i&gt;");
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"&amp;"),
411                        b'<' => allocated.extend_from_slice(b"&lt;"),
412                        b'>' => allocated.extend_from_slice(b"&gt;"),
413                        b'"' => allocated.extend_from_slice(b"&quot;"),
414                        b'\'' => allocated.extend_from_slice(b"&#x27;"),
415                        b'/' => allocated.extend_from_slice(b"&#x2F;"),
416                        // Old versions of IE treat a ` as a '.
417                        b'`' => allocated.extend_from_slice(b"&#96;"),
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}