mirror of https://github.com/rwf2/Rocket.git
Introduce 'RawStrBuf', use in 'Origin::map_path()'.
'RawStrBuf' is the owned analog of 'RawStr'. Thus, 'Cow<RawStr>' is either 'RawStr' or 'RawStrBuf'.
This commit is contained in:
parent
c0564fa8dc
commit
e92b2adeaa
|
@ -53,5 +53,5 @@ pub mod private {
|
|||
|
||||
pub use crate::method::Method;
|
||||
pub use crate::status::{Status, StatusClass};
|
||||
pub use crate::raw_str::RawStr;
|
||||
pub use crate::raw_str::{RawStr, RawStrBuf};
|
||||
pub use crate::header::*;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::convert::AsRef;
|
||||
use std::cmp::Ordering;
|
||||
use std::str::Utf8Error;
|
||||
|
@ -50,6 +50,26 @@ use crate::uncased::UncasedStr;
|
|||
#[derive(RefCast, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct RawStr(str);
|
||||
|
||||
impl ToOwned for RawStr {
|
||||
type Owned = RawStrBuf;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
RawStrBuf(self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// An owned version of [`RawStr`].
|
||||
#[repr(transparent)]
|
||||
#[derive(RefCast, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct RawStrBuf(String);
|
||||
|
||||
impl RawStrBuf {
|
||||
/// Cost-free conversion from `self` into a `String`.
|
||||
pub fn into_string(self) -> String {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl RawStr {
|
||||
/// Constructs an `&RawStr` from a string-like type at no cost.
|
||||
///
|
||||
|
@ -68,6 +88,46 @@ impl RawStr {
|
|||
RawStr::ref_cast(string.as_ref())
|
||||
}
|
||||
|
||||
/// Constructs an `&RawStr` from a string-like type at no cost.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate rocket;
|
||||
/// use rocket::http::RawStr;
|
||||
///
|
||||
/// let raw_str = RawStr::new("Hello, world!");
|
||||
///
|
||||
/// // `into` can also be used; note that the type must be specified
|
||||
/// let raw_str: &RawStr = "Hello, world!".into();
|
||||
/// ```
|
||||
pub fn from_cow_str<'a>(cow: Cow<'a, str>) -> Cow<'a, RawStr> {
|
||||
match cow {
|
||||
Cow::Borrowed(b) => Cow::Borrowed(b.into()),
|
||||
Cow::Owned(b) => Cow::Owned(b.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs an `&RawStr` from a string-like type at no cost.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate rocket;
|
||||
/// use rocket::http::RawStr;
|
||||
///
|
||||
/// let raw_str = RawStr::new("Hello, world!");
|
||||
///
|
||||
/// // `into` can also be used; note that the type must be specified
|
||||
/// let raw_str: &RawStr = "Hello, world!".into();
|
||||
/// ```
|
||||
pub fn into_cow_str<'a>(cow: Cow<'a, RawStr>) -> Cow<'a, str> {
|
||||
match cow {
|
||||
Cow::Borrowed(b) => Cow::Borrowed(b.as_str()),
|
||||
Cow::Owned(b) => Cow::Owned(b.into_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs percent decoding.
|
||||
fn _percent_decode(&self) -> percent_encoding::PercentDecode<'_> {
|
||||
percent_encoding::percent_decode(self.as_bytes())
|
||||
|
@ -822,6 +882,64 @@ impl fmt::Display for RawStr {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsRef<RawStr> for RawStrBuf {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &RawStr {
|
||||
RawStr::new(self.0.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<RawStr> for RawStrBuf {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &RawStr {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for RawStrBuf {
|
||||
type Target = RawStr;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RawStrBuf {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for RawStrBuf {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for RawStrBuf {
|
||||
#[inline(always)]
|
||||
fn from(string: String) -> Self {
|
||||
RawStrBuf(string)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for RawStrBuf {
|
||||
#[inline(always)]
|
||||
fn from(string: &str) -> Self {
|
||||
string.to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&RawStr> for RawStrBuf {
|
||||
#[inline(always)]
|
||||
fn from(raw: &RawStr) -> Self {
|
||||
raw.to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::RawStr;
|
||||
|
|
|
@ -3,10 +3,10 @@ use std::convert::TryFrom;
|
|||
use std::fmt::{self, Display};
|
||||
|
||||
use crate::ext::IntoOwned;
|
||||
use crate::parse::{Indexed, Extent, IndexedStr};
|
||||
use crate::parse::{Indexed, Extent, IndexedStr, uri::tables::is_pchar};
|
||||
use crate::uri::{self, UriPart, Query, Path};
|
||||
use crate::uri::{Error, Segments, QuerySegments, as_utf8_unchecked};
|
||||
use crate::RawStr;
|
||||
use crate::{RawStr, RawStrBuf};
|
||||
|
||||
use state::Storage;
|
||||
|
||||
|
@ -104,6 +104,19 @@ impl<'a, 'b> PartialEq<Origin<'b>> for Origin<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for Origin<'_> {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
let (path, query) = RawStr::new(other).split_at_byte(b'?');
|
||||
self.path() == path && self.query().unwrap_or("".into()) == query
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Origin<'_>> for str {
|
||||
fn eq(&self, other: &Origin<'_>) -> bool {
|
||||
other.eq(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoOwned for Origin<'_> {
|
||||
type Owned = Origin<'static>;
|
||||
|
||||
|
@ -407,20 +420,27 @@ impl<'a> Origin<'a> {
|
|||
/// assert_eq!(old_uri.map_path(|p| format!("{}/", p)), Some(expected_uri));
|
||||
///
|
||||
/// let old_uri = Origin::parse("/a/b/c/").unwrap();
|
||||
/// let expected = Origin::parse("/b/c/").unwrap();
|
||||
/// assert_eq!(old_uri.map_path(|p| p.strip_prefix("/a").unwrap_or(p)), Some(expected));
|
||||
///
|
||||
/// let old_uri = Origin::parse("/a").unwrap();
|
||||
/// assert_eq!(old_uri.map_path(|p| p.strip_prefix("/a").unwrap_or(p)), None);
|
||||
///
|
||||
/// let old_uri = Origin::parse("/a/b/c/").unwrap();
|
||||
/// assert_eq!(old_uri.map_path(|p| format!("hi/{}", p)), None);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn map_path<F: FnOnce(&RawStr) -> String>(&self, f: F) -> Option<Self> {
|
||||
let path = f(self.path());
|
||||
if !path.starts_with('/')
|
||||
|| !path.bytes().all(|b| crate::parse::uri::tables::is_pchar(&b))
|
||||
{
|
||||
pub fn map_path<'s, F, P>(&'s self, f: F) -> Option<Self>
|
||||
where F: FnOnce(&'s RawStr) -> P, P: Into<RawStrBuf> + 's
|
||||
{
|
||||
let path = f(self.path()).into();
|
||||
if !path.starts_with('/') || !path.as_bytes().iter().all(|b| is_pchar(&b)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Origin {
|
||||
source: self.source.clone(),
|
||||
path: Cow::from(path).into(),
|
||||
path: Cow::from(path.into_string()).into(),
|
||||
query: self.query.clone(),
|
||||
decoded_path_segs: Storage::new(),
|
||||
decoded_query_segs: Storage::new(),
|
||||
|
@ -617,6 +637,15 @@ impl TryFrom<String> for Origin<'static> {
|
|||
}
|
||||
}
|
||||
|
||||
// Because inference doesn't take `&String` to `&str`.
|
||||
impl<'a> TryFrom<&'a String> for Origin<'a> {
|
||||
type Error = Error<'a>;
|
||||
|
||||
fn try_from(value: &'a String) -> Result<Self, Self::Error> {
|
||||
Origin::parse(value.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for Origin<'a> {
|
||||
type Error = Error<'a>;
|
||||
|
||||
|
|
Loading…
Reference in New Issue