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::method::Method;
|
||||||
pub use crate::status::{Status, StatusClass};
|
pub use crate::status::{Status, StatusClass};
|
||||||
pub use crate::raw_str::RawStr;
|
pub use crate::raw_str::{RawStr, RawStrBuf};
|
||||||
pub use crate::header::*;
|
pub use crate::header::*;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::{Borrow, Cow};
|
||||||
use std::convert::AsRef;
|
use std::convert::AsRef;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
|
@ -50,6 +50,26 @@ use crate::uncased::UncasedStr;
|
||||||
#[derive(RefCast, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(RefCast, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct RawStr(str);
|
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 {
|
impl RawStr {
|
||||||
/// Constructs an `&RawStr` from a string-like type at no cost.
|
/// Constructs an `&RawStr` from a string-like type at no cost.
|
||||||
///
|
///
|
||||||
|
@ -68,6 +88,46 @@ impl RawStr {
|
||||||
RawStr::ref_cast(string.as_ref())
|
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.
|
/// Performs percent decoding.
|
||||||
fn _percent_decode(&self) -> percent_encoding::PercentDecode<'_> {
|
fn _percent_decode(&self) -> percent_encoding::PercentDecode<'_> {
|
||||||
percent_encoding::percent_decode(self.as_bytes())
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::RawStr;
|
use super::RawStr;
|
||||||
|
|
|
@ -3,10 +3,10 @@ use std::convert::TryFrom;
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
|
|
||||||
use crate::ext::IntoOwned;
|
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::{self, UriPart, Query, Path};
|
||||||
use crate::uri::{Error, Segments, QuerySegments, as_utf8_unchecked};
|
use crate::uri::{Error, Segments, QuerySegments, as_utf8_unchecked};
|
||||||
use crate::RawStr;
|
use crate::{RawStr, RawStrBuf};
|
||||||
|
|
||||||
use state::Storage;
|
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<'_> {
|
impl IntoOwned for Origin<'_> {
|
||||||
type Owned = Origin<'static>;
|
type Owned = Origin<'static>;
|
||||||
|
|
||||||
|
@ -407,20 +420,27 @@ impl<'a> Origin<'a> {
|
||||||
/// assert_eq!(old_uri.map_path(|p| format!("{}/", p)), Some(expected_uri));
|
/// assert_eq!(old_uri.map_path(|p| format!("{}/", p)), Some(expected_uri));
|
||||||
///
|
///
|
||||||
/// let old_uri = Origin::parse("/a/b/c/").unwrap();
|
/// 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);
|
/// assert_eq!(old_uri.map_path(|p| format!("hi/{}", p)), None);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn map_path<F: FnOnce(&RawStr) -> String>(&self, f: F) -> Option<Self> {
|
pub fn map_path<'s, F, P>(&'s self, f: F) -> Option<Self>
|
||||||
let path = f(self.path());
|
where F: FnOnce(&'s RawStr) -> P, P: Into<RawStrBuf> + 's
|
||||||
if !path.starts_with('/')
|
{
|
||||||
|| !path.bytes().all(|b| crate::parse::uri::tables::is_pchar(&b))
|
let path = f(self.path()).into();
|
||||||
{
|
if !path.starts_with('/') || !path.as_bytes().iter().all(|b| is_pchar(&b)) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Origin {
|
Some(Origin {
|
||||||
source: self.source.clone(),
|
source: self.source.clone(),
|
||||||
path: Cow::from(path).into(),
|
path: Cow::from(path.into_string()).into(),
|
||||||
query: self.query.clone(),
|
query: self.query.clone(),
|
||||||
decoded_path_segs: Storage::new(),
|
decoded_path_segs: Storage::new(),
|
||||||
decoded_query_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> {
|
impl<'a> TryFrom<&'a str> for Origin<'a> {
|
||||||
type Error = Error<'a>;
|
type Error = Error<'a>;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue