mirror of https://github.com/rwf2/Rocket.git
Migrate to upstream 'uncased'.
This commit is contained in:
parent
dee11966b6
commit
adbf1caab2
|
@ -33,6 +33,7 @@ tokio = { version = "0.2.9", features = ["sync", "tcp", "time"] }
|
|||
unicode-xid = "0.2"
|
||||
log = "0.4"
|
||||
ref-cast = "1.0"
|
||||
uncased = "0.9"
|
||||
|
||||
[dependencies.cookie]
|
||||
git = "https://github.com/SergioBenitez/cookie-rs.git"
|
||||
|
|
|
@ -40,7 +40,14 @@ mod raw_str;
|
|||
mod parse;
|
||||
mod listener;
|
||||
|
||||
pub mod uncased;
|
||||
/// Case-preserving, ASCII case-insensitive string types.
|
||||
///
|
||||
/// An _uncased_ string is case-preserving. That is, the string itself contains
|
||||
/// cased characters, but comparison (including ordering, equality, and hashing)
|
||||
/// is ASCII case-insensitive. **Note:** the `alloc` feature _is_ enabled.
|
||||
pub mod uncased {
|
||||
#[doc(inline)] pub use uncased::*;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod private {
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::fmt;
|
|||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use crate::ext::IntoCollection;
|
||||
use crate::uncased::{uncased_eq, UncasedStr};
|
||||
use crate::uncased::UncasedStr;
|
||||
use crate::parse::{Indexed, IndexedString, parse_media_type};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
@ -210,7 +210,7 @@ macro_rules! from_extension {
|
|||
/// ```
|
||||
pub fn from_extension(ext: &str) -> Option<MediaType> {
|
||||
match ext {
|
||||
$(x if uncased_eq(x, $ext) => Some(MediaType::$name)),*,
|
||||
$(x if uncased::eq(x, $ext) => Some(MediaType::$name)),*,
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
@ -276,7 +276,7 @@ macro_rules! parse_flexible {
|
|||
/// ```
|
||||
pub fn parse_flexible(name: &str) -> Option<MediaType> {
|
||||
match name {
|
||||
$(x if uncased_eq(x, $short) => Some(MediaType::$name)),*,
|
||||
$(x if uncased::eq(x, $short) => Some(MediaType::$name)),*,
|
||||
_ => MediaType::from_str(name).ok(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::uncased::uncased_eq;
|
||||
|
||||
use self::Method::*;
|
||||
|
||||
// TODO: Support non-standard methods, here and in codegen.
|
||||
|
@ -100,15 +98,15 @@ impl FromStr for Method {
|
|||
// clients don't follow this, so we just do a case-insensitive match here.
|
||||
fn from_str(s: &str) -> Result<Method, ()> {
|
||||
match s {
|
||||
x if uncased_eq(x, Get.as_str()) => Ok(Get),
|
||||
x if uncased_eq(x, Put.as_str()) => Ok(Put),
|
||||
x if uncased_eq(x, Post.as_str()) => Ok(Post),
|
||||
x if uncased_eq(x, Delete.as_str()) => Ok(Delete),
|
||||
x if uncased_eq(x, Options.as_str()) => Ok(Options),
|
||||
x if uncased_eq(x, Head.as_str()) => Ok(Head),
|
||||
x if uncased_eq(x, Trace.as_str()) => Ok(Trace),
|
||||
x if uncased_eq(x, Connect.as_str()) => Ok(Connect),
|
||||
x if uncased_eq(x, Patch.as_str()) => Ok(Patch),
|
||||
x if uncased::eq(x, Get.as_str()) => Ok(Get),
|
||||
x if uncased::eq(x, Put.as_str()) => Ok(Put),
|
||||
x if uncased::eq(x, Post.as_str()) => Ok(Post),
|
||||
x if uncased::eq(x, Delete.as_str()) => Ok(Delete),
|
||||
x if uncased::eq(x, Options.as_str()) => Ok(Options),
|
||||
x if uncased::eq(x, Head.as_str()) => Ok(Head),
|
||||
x if uncased::eq(x, Trace.as_str()) => Ok(Trace),
|
||||
x if uncased::eq(x, Connect.as_str()) => Ok(Connect),
|
||||
x if uncased::eq(x, Patch.as_str()) => Ok(Patch),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,411 +0,0 @@
|
|||
//! Contains types that encapsulate uncased ASCII strings.
|
||||
//!
|
||||
//! An 'uncased' ASCII string is case-preserving. That is, the string itself
|
||||
//! contains cased characters, but comparison (including ordering, equality, and
|
||||
//! hashing) is case-insensitive.
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::borrow::{Cow, Borrow};
|
||||
use std::cmp::Ordering;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::fmt;
|
||||
|
||||
use ref_cast::RefCast;
|
||||
|
||||
/// A reference to an uncased (case-preserving) ASCII string. This is typically
|
||||
/// created from an `&str` as follows:
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate rocket;
|
||||
/// use rocket::http::uncased::UncasedStr;
|
||||
///
|
||||
/// let ascii_ref: &UncasedStr = "Hello, world!".into();
|
||||
/// ```
|
||||
#[derive(Debug, RefCast)]
|
||||
#[repr(transparent)]
|
||||
pub struct UncasedStr(str);
|
||||
|
||||
impl UncasedStr {
|
||||
/// Returns a reference to an `UncasedStr` from an `&str`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate rocket;
|
||||
/// use rocket::http::uncased::UncasedStr;
|
||||
///
|
||||
/// let uncased_str = UncasedStr::new("Hello!");
|
||||
/// assert_eq!(uncased_str, "hello!");
|
||||
/// assert_eq!(uncased_str, "Hello!");
|
||||
/// assert_eq!(uncased_str, "HeLLo!");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn new(string: &str) -> &UncasedStr {
|
||||
UncasedStr::ref_cast(string)
|
||||
}
|
||||
|
||||
/// Returns `self` as an `&str`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate rocket;
|
||||
/// use rocket::http::uncased::UncasedStr;
|
||||
///
|
||||
/// let uncased_str = UncasedStr::new("Hello!");
|
||||
/// assert_eq!(uncased_str.as_str(), "Hello!");
|
||||
/// assert_ne!(uncased_str.as_str(), "hELLo!");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Converts a `Box<UncasedStr>` into an `Uncased` without copying or
|
||||
/// allocating.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate rocket;
|
||||
/// use rocket::http::uncased::Uncased;
|
||||
///
|
||||
/// let uncased = Uncased::new("Hello!");
|
||||
/// let boxed = uncased.clone().into_boxed_uncased();
|
||||
/// assert_eq!(boxed.into_uncased(), uncased);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn into_uncased(self: Box<UncasedStr>) -> Uncased<'static> {
|
||||
// This is the inverse of a `newtype`-like transformation. The
|
||||
// `repr(transparent)` ensures that this is safe and correct.
|
||||
unsafe {
|
||||
let raw_str = Box::into_raw(self) as *mut str;
|
||||
Uncased::from(Box::from_raw(raw_str).into_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for UncasedStr {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &UncasedStr) -> bool {
|
||||
self.0.eq_ignore_ascii_case(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for UncasedStr {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.0.eq_ignore_ascii_case(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<UncasedStr> for str {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &UncasedStr) -> bool {
|
||||
other.0.eq_ignore_ascii_case(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for UncasedStr {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.0.eq_ignore_ascii_case(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<UncasedStr> for &str {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &UncasedStr) -> bool {
|
||||
other.0.eq_ignore_ascii_case(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for &'a UncasedStr {
|
||||
#[inline(always)]
|
||||
fn from(string: &'a str) -> &'a UncasedStr {
|
||||
UncasedStr::new(string)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for UncasedStr { }
|
||||
|
||||
impl Hash for UncasedStr {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
for byte in self.0.bytes() {
|
||||
hasher.write_u8(byte.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for UncasedStr {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &UncasedStr) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for UncasedStr {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let self_chars = self.0.chars().map(|c| c.to_ascii_lowercase());
|
||||
let other_chars = other.0.chars().map(|c| c.to_ascii_lowercase());
|
||||
self_chars.cmp(other_chars)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UncasedStr {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// An uncased (case-preserving), owned _or_ borrowed ASCII string.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Uncased<'s> {
|
||||
#[doc(hidden)]
|
||||
pub string: Cow<'s, str>
|
||||
}
|
||||
|
||||
impl<'s> Uncased<'s> {
|
||||
/// Creates a new `Uncased` string from `string` without allocating.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate rocket;
|
||||
/// use rocket::http::uncased::Uncased;
|
||||
///
|
||||
/// let uncased = Uncased::new("Content-Type");
|
||||
/// assert_eq!(uncased, "content-type");
|
||||
/// assert_eq!(uncased, "CONTENT-Type");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn new<S: Into<Cow<'s, str>>>(string: S) -> Uncased<'s> {
|
||||
Uncased { string: string.into() }
|
||||
}
|
||||
|
||||
/// Converts `self` into an owned `String`, allocating if necessary.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate rocket;
|
||||
/// use rocket::http::uncased::Uncased;
|
||||
///
|
||||
/// let uncased = Uncased::new("Content-Type");
|
||||
/// let string = uncased.into_string();
|
||||
/// assert_eq!(string, "Content-Type".to_string());
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn into_string(self) -> String {
|
||||
self.string.into_owned()
|
||||
}
|
||||
|
||||
/// Converts `self` into a `Box<UncasedStr>`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate rocket;
|
||||
/// use rocket::http::uncased::Uncased;
|
||||
///
|
||||
/// let boxed = Uncased::new("Content-Type").into_boxed_uncased();
|
||||
/// assert_eq!(&*boxed, "content-type");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn into_boxed_uncased(self) -> Box<UncasedStr> {
|
||||
// This is simply a `newtype`-like transformation. The `repr(C)` ensures
|
||||
// that this is safe and correct. Note this exact pattern appears often
|
||||
// in the standard library.
|
||||
unsafe {
|
||||
let raw_str = Box::into_raw(self.string.into_owned().into_boxed_str());
|
||||
Box::from_raw(raw_str as *mut UncasedStr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the inner `Cow`.
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
pub fn into_cow(self) -> Cow<'s, str> {
|
||||
self.string
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Uncased<'_> {
|
||||
type Target = UncasedStr;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &UncasedStr {
|
||||
UncasedStr::new(self.string.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<UncasedStr> for Uncased<'_>{
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &UncasedStr {
|
||||
UncasedStr::new(self.string.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<UncasedStr> for Uncased<'_> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &UncasedStr {
|
||||
self.as_str().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, 'c: 's> From<&'c str> for Uncased<'s> {
|
||||
#[inline(always)]
|
||||
fn from(string: &'c str) -> Self {
|
||||
Uncased::new(string)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Uncased<'static> {
|
||||
#[inline(always)]
|
||||
fn from(string: String) -> Self {
|
||||
Uncased::new(string)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, 'c: 's> From<Cow<'c, str>> for Uncased<'s> {
|
||||
#[inline(always)]
|
||||
fn from(string: Cow<'c, str>) -> Self {
|
||||
Uncased::new(string)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> PartialOrd<Uncased<'b>> for Uncased<'_> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Uncased<'b>) -> Option<Ordering> {
|
||||
self.as_ref().partial_cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Uncased<'_> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.as_ref().cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Uncased<'_> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.string.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> PartialEq<Uncased<'b>> for Uncased<'_> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Uncased<'b>) -> bool {
|
||||
self.as_ref().eq(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for Uncased<'_> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_ref().eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Uncased<'_>> for str {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Uncased<'_>) -> bool {
|
||||
other.as_ref().eq(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> PartialEq<&'b str> for Uncased<'_> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: & &'b str) -> bool {
|
||||
self.as_ref().eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> PartialEq<Uncased<'b>> for &str {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Uncased<'b>) -> bool {
|
||||
other.as_ref().eq(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Uncased<'_> { }
|
||||
|
||||
impl Hash for Uncased<'_> {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
self.as_ref().hash(hasher)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `s1` and `s2` are equal without considering case.
|
||||
///
|
||||
/// That is, for ASCII strings, this function returns `s1.to_lower() ==
|
||||
/// s2.to_lower()`, but does it in a much faster way.
|
||||
#[inline(always)]
|
||||
pub fn uncased_eq<S1: AsRef<str>, S2: AsRef<str>>(s1: S1, s2: S2) -> bool {
|
||||
UncasedStr::new(s1.as_ref()) == UncasedStr::new(s2.as_ref())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Uncased;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
|
||||
fn hash<T: Hash>(t: &T) -> u64 {
|
||||
let mut s = DefaultHasher::new();
|
||||
t.hash(&mut s);
|
||||
s.finish()
|
||||
}
|
||||
|
||||
macro_rules! assert_uncased_eq {
|
||||
($($string:expr),+) => ({
|
||||
let mut strings = Vec::new();
|
||||
$(strings.push($string);)+
|
||||
|
||||
for i in 0..strings.len() {
|
||||
for j in i..strings.len() {
|
||||
let (str_a, str_b) = (strings[i], strings[j]);
|
||||
let ascii_a = Uncased::from(str_a);
|
||||
let ascii_b = Uncased::from(str_b);
|
||||
assert_eq!(ascii_a, ascii_b);
|
||||
assert_eq!(hash(&ascii_a), hash(&ascii_b));
|
||||
assert_eq!(ascii_a, str_a);
|
||||
assert_eq!(ascii_b, str_b);
|
||||
assert_eq!(ascii_a, str_b);
|
||||
assert_eq!(ascii_b, str_a);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_case_insensitive() {
|
||||
assert_uncased_eq!["a", "A"];
|
||||
assert_uncased_eq!["foobar", "FOOBAR", "FooBar", "fOObAr", "fooBAR"];
|
||||
assert_uncased_eq!["", ""];
|
||||
assert_uncased_eq!["content-type", "Content-Type", "CONTENT-TYPE"];
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_case_cmp() {
|
||||
assert!(Uncased::from("foobar") == Uncased::from("FOOBAR"));
|
||||
assert!(Uncased::from("a") == Uncased::from("A"));
|
||||
|
||||
assert!(Uncased::from("a") < Uncased::from("B"));
|
||||
assert!(Uncased::from("A") < Uncased::from("B"));
|
||||
assert!(Uncased::from("A") < Uncased::from("b"));
|
||||
|
||||
assert!(Uncased::from("aa") > Uncased::from("a"));
|
||||
assert!(Uncased::from("aa") > Uncased::from("A"));
|
||||
assert!(Uncased::from("AA") > Uncased::from("a"));
|
||||
assert!(Uncased::from("AA") > Uncased::from("a"));
|
||||
assert!(Uncased::from("Aa") > Uncased::from("a"));
|
||||
assert!(Uncased::from("Aa") > Uncased::from("A"));
|
||||
assert!(Uncased::from("aA") > Uncased::from("a"));
|
||||
assert!(Uncased::from("aA") > Uncased::from("A"));
|
||||
}
|
||||
}
|
|
@ -206,11 +206,11 @@ pub use self::builder::ConfigBuilder;
|
|||
pub use crate::logger::LoggingLevel;
|
||||
pub(crate) use self::toml_ext::LoggedValue;
|
||||
|
||||
use crate::logger::COLORS_ENV;
|
||||
use crate::http::uncased;
|
||||
use self::Environment::*;
|
||||
use self::environment::CONFIG_ENV;
|
||||
use crate::logger::COLORS_ENV;
|
||||
use self::toml_ext::parse_simple_toml_value;
|
||||
use crate::http::uncased::uncased_eq;
|
||||
|
||||
const CONFIG_FILENAME: &str = "Rocket.toml";
|
||||
const GLOBAL_ENV_NAME: &str = "global";
|
||||
|
@ -365,12 +365,12 @@ impl FullConfig {
|
|||
for (key, val) in env::vars() {
|
||||
if key.len() < ENV_VAR_PREFIX.len() {
|
||||
continue
|
||||
} else if !uncased_eq(&key[..ENV_VAR_PREFIX.len()], ENV_VAR_PREFIX) {
|
||||
} else if !uncased::eq(&key[..ENV_VAR_PREFIX.len()], ENV_VAR_PREFIX) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip environment variables that are handled elsewhere.
|
||||
if PREHANDLED_VARS.iter().any(|var| uncased_eq(&key, var)) {
|
||||
if PREHANDLED_VARS.iter().any(|var| uncased::eq(&key, var)) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue