Add 'const' constructor for 'MediaType'.

This commit is contained in:
Sergio Benitez 2020-10-14 23:54:37 -07:00
parent 079e458b62
commit 5cf249581f
6 changed files with 231 additions and 237 deletions

View File

@ -70,8 +70,8 @@ impl FromMeta for ContentType {
impl ToTokens for ContentType { impl ToTokens for ContentType {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
// Yeah, yeah. (((((i))).kn0w())) let http_media_type = self.0.media_type().clone();
let media_type = MediaType((self.0).clone().0); let media_type = MediaType(http_media_type);
tokens.extend(quote!(::rocket::http::ContentType(#media_type))); tokens.extend(quote!(::rocket::http::ContentType(#media_type)));
} }
} }
@ -94,27 +94,13 @@ impl FromMeta for MediaType {
impl ToTokens for MediaType { impl ToTokens for MediaType {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
use std::iter::repeat;
let (top, sub) = (self.0.top().as_str(), self.0.sub().as_str()); let (top, sub) = (self.0.top().as_str(), self.0.sub().as_str());
let (keys, values) = self.0.params().split2(); let (keys, values) = self.0.params().split2();
let http = quote!(::rocket::http);
let cow = quote!(::std::borrow::Cow); tokens.extend(quote! {
let (pub_http, http) = (quote!(::rocket::http), quote!(::rocket::http::private)); #http::MediaType::const_new(#top, #sub, &[#((#keys, #values)),*])
let (http_, http__) = (repeat(&http), repeat(&http)); });
let (cow_, cow__) = (repeat(&cow), repeat(&cow));
// TODO: Produce less code when possible (for known media types).
tokens.extend(quote!(#pub_http::MediaType {
source: #http::Source::None,
top: #http::Indexed::Concrete(#cow::Borrowed(#top)),
sub: #http::Indexed::Concrete(#cow::Borrowed(#sub)),
params: #http::MediaParams::Static(&[
#((
#http_::Indexed::Concrete(#cow_::Borrowed(#keys)),
#http__::Indexed::Concrete(#cow__::Borrowed(#values))
)),*
])
}))
} }
} }

View File

@ -35,6 +35,7 @@ log = "0.4"
ref-cast = "1.0" ref-cast = "1.0"
uncased = "0.9" uncased = "0.9"
parking_lot = "0.11" parking_lot = "0.11"
either = "1"
[dependencies.cookie] [dependencies.cookie]
git = "https://github.com/SergioBenitez/cookie-rs.git" git = "https://github.com/SergioBenitez/cookie-rs.git"

View File

@ -3,120 +3,12 @@ use std::str::FromStr;
use std::fmt; use std::fmt;
use smallvec::SmallVec; use smallvec::SmallVec;
use either::Either;
use crate::{Header, MediaType}; use crate::{Header, MediaType};
use crate::ext::IntoCollection; use crate::ext::IntoCollection;
use crate::parse::parse_accept; use crate::parse::parse_accept;
/// A `MediaType` with an associated quality value.
#[derive(Debug, Clone, PartialEq)]
pub struct QMediaType(pub MediaType, pub Option<f32>);
impl QMediaType {
/// Retrieve the weight of the media type, if there is any.
///
/// # Example
///
/// ```rust
/// # extern crate rocket;
/// use rocket::http::{MediaType, QMediaType};
///
/// let q_type = QMediaType(MediaType::HTML, Some(0.3));
/// assert_eq!(q_type.weight(), Some(0.3));
/// ```
#[inline(always)]
pub fn weight(&self) -> Option<f32> {
self.1
}
/// Retrieve the weight of the media type or a given default value.
///
/// # Example
///
/// ```rust
/// # extern crate rocket;
/// use rocket::http::{MediaType, QMediaType};
///
/// let q_type = QMediaType(MediaType::HTML, Some(0.3));
/// assert_eq!(q_type.weight_or(0.9), 0.3);
///
/// let q_type = QMediaType(MediaType::HTML, None);
/// assert_eq!(q_type.weight_or(0.9), 0.9);
/// ```
#[inline(always)]
pub fn weight_or(&self, default: f32) -> f32 {
self.1.unwrap_or(default)
}
/// Borrow the internal `MediaType`.
///
/// # Example
///
/// ```rust
/// # extern crate rocket;
/// use rocket::http::{MediaType, QMediaType};
///
/// let q_type = QMediaType(MediaType::HTML, Some(0.3));
/// assert_eq!(q_type.media_type(), &MediaType::HTML);
/// ```
#[inline(always)]
pub fn media_type(&self) -> &MediaType {
&self.0
}
}
impl From<MediaType> for QMediaType {
#[inline(always)]
fn from(media_type: MediaType) -> QMediaType {
QMediaType(media_type, None)
}
}
impl Deref for QMediaType {
type Target = MediaType;
#[inline(always)]
fn deref(&self) -> &MediaType {
&self.0
}
}
// FIXME: `Static` is needed for `const` items. Need `const SmallVec::new`.
#[derive(Debug, Clone)]
pub enum AcceptParams {
Static(&'static [QMediaType]),
Dynamic(SmallVec<[QMediaType; 1]>)
}
impl Default for AcceptParams {
fn default() -> Self {
AcceptParams::Dynamic(SmallVec::new())
}
}
impl Extend<QMediaType> for AcceptParams {
fn extend<T: IntoIterator<Item = QMediaType>>(&mut self, iter: T) {
match self {
AcceptParams::Static(..) => panic!("can't add to static collection!"),
AcceptParams::Dynamic(ref mut v) => v.extend(iter)
}
}
}
impl PartialEq for AcceptParams {
fn eq(&self, other: &AcceptParams) -> bool {
#[inline(always)]
fn inner_types(params: &AcceptParams) -> &[QMediaType] {
match *params {
AcceptParams::Static(params) => params,
AcceptParams::Dynamic(ref vec) => vec,
}
}
inner_types(self) == inner_types(other)
}
}
/// The HTTP Accept header. /// The HTTP Accept header.
/// ///
/// An `Accept` header is composed of zero or more media types, each of which /// An `Accept` header is composed of zero or more media types, each of which
@ -160,9 +52,20 @@ impl PartialEq for AcceptParams {
/// ///
/// let response = Response::build().header(Accept::JSON).finalize(); /// let response = Response::build().header(Accept::JSON).finalize();
/// ``` /// ```
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone)]
pub struct Accept(pub(crate) AcceptParams); pub struct Accept(pub(crate) AcceptParams);
/// A `MediaType` with an associated quality value.
#[derive(Debug, Clone, PartialEq)]
pub struct QMediaType(pub MediaType, pub Option<f32>);
// NOTE: `Static` is needed for `const` items. Need `const SmallVec::new`.
#[derive(Debug, Clone)]
pub enum AcceptParams {
Static(QMediaType),
Dynamic(SmallVec<[QMediaType; 1]>)
}
macro_rules! accept_constructor { macro_rules! accept_constructor {
($($name:ident ($check:ident): $str:expr, $t:expr, ($($name:ident ($check:ident): $str:expr, $t:expr,
$s:expr $(; $k:expr => $v:expr)*,)+) => { $s:expr $(; $k:expr => $v:expr)*,)+) => {
@ -173,19 +76,12 @@ macro_rules! accept_constructor {
#[doc="</i>"] #[doc="</i>"]
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
pub const $name: Accept = Accept( pub const $name: Accept = Accept(
AcceptParams::Static(&[QMediaType(MediaType::$name, None)]) AcceptParams::Static(QMediaType(MediaType::$name, None))
); );
)+ )+
}; };
} }
impl<T: IntoCollection<MediaType>> From<T> for Accept {
#[inline(always)]
fn from(items: T) -> Accept {
Accept(AcceptParams::Dynamic(items.mapped(|item| item.into())))
}
}
impl Accept { impl Accept {
/// Constructs a new `Accept` header from one or more media types. /// Constructs a new `Accept` header from one or more media types.
/// ///
@ -314,12 +210,10 @@ impl Accept {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn iter<'a>(&'a self) -> impl Iterator<Item=&'a QMediaType> + 'a { pub fn iter<'a>(&'a self) -> impl Iterator<Item=&'a QMediaType> + 'a {
let slice = match self.0 { match self.0 {
AcceptParams::Static(slice) => slice, AcceptParams::Static(ref val) => Either::Left(Some(val).into_iter()),
AcceptParams::Dynamic(ref vec) => &vec[..], AcceptParams::Dynamic(ref vec) => Either::Right(vec.iter())
}; }
slice.iter()
} }
/// Returns an iterator over all of the (bare) media types in `self`. Media /// Returns an iterator over all of the (bare) media types in `self`. Media
@ -351,6 +245,19 @@ impl Accept {
known_media_types!(accept_constructor); known_media_types!(accept_constructor);
} }
impl<T: IntoCollection<MediaType>> From<T> for Accept {
#[inline(always)]
fn from(items: T) -> Accept {
Accept(AcceptParams::Dynamic(items.mapped(|item| item.into())))
}
}
impl PartialEq for Accept {
fn eq(&self, other: &Accept) -> bool {
self.iter().eq(other.iter())
}
}
impl fmt::Display for Accept { impl fmt::Display for Accept {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, media_type) in self.iter().enumerate() { for (i, media_type) in self.iter().enumerate() {
@ -384,6 +291,90 @@ impl Into<Header<'static>> for Accept {
} }
} }
impl QMediaType {
/// Retrieve the weight of the media type, if there is any.
///
/// # Example
///
/// ```rust
/// # extern crate rocket;
/// use rocket::http::{MediaType, QMediaType};
///
/// let q_type = QMediaType(MediaType::HTML, Some(0.3));
/// assert_eq!(q_type.weight(), Some(0.3));
/// ```
#[inline(always)]
pub fn weight(&self) -> Option<f32> {
self.1
}
/// Retrieve the weight of the media type or a given default value.
///
/// # Example
///
/// ```rust
/// # extern crate rocket;
/// use rocket::http::{MediaType, QMediaType};
///
/// let q_type = QMediaType(MediaType::HTML, Some(0.3));
/// assert_eq!(q_type.weight_or(0.9), 0.3);
///
/// let q_type = QMediaType(MediaType::HTML, None);
/// assert_eq!(q_type.weight_or(0.9), 0.9);
/// ```
#[inline(always)]
pub fn weight_or(&self, default: f32) -> f32 {
self.1.unwrap_or(default)
}
/// Borrow the internal `MediaType`.
///
/// # Example
///
/// ```rust
/// # extern crate rocket;
/// use rocket::http::{MediaType, QMediaType};
///
/// let q_type = QMediaType(MediaType::HTML, Some(0.3));
/// assert_eq!(q_type.media_type(), &MediaType::HTML);
/// ```
#[inline(always)]
pub fn media_type(&self) -> &MediaType {
&self.0
}
}
impl From<MediaType> for QMediaType {
#[inline(always)]
fn from(media_type: MediaType) -> QMediaType {
QMediaType(media_type, None)
}
}
impl Deref for QMediaType {
type Target = MediaType;
#[inline(always)]
fn deref(&self) -> &MediaType {
&self.0
}
}
impl Default for AcceptParams {
fn default() -> Self {
AcceptParams::Dynamic(SmallVec::new())
}
}
impl Extend<QMediaType> for AcceptParams {
fn extend<T: IntoIterator<Item = QMediaType>>(&mut self, iter: T) {
match self {
AcceptParams::Static(..) => panic!("can't add to static collection!"),
AcceptParams::Dynamic(ref mut v) => v.extend(iter)
}
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{Accept, MediaType}; use crate::{Accept, MediaType};

View File

@ -49,21 +49,17 @@ pub mod uncased {
#[doc(inline)] pub use uncased::*; #[doc(inline)] pub use uncased::*;
} }
// Types that we expose for use by core.
#[doc(hidden)] #[doc(hidden)]
pub mod private { pub mod private {
// We need to export these for codegen, but otherwise it's unnecessary.
// TODO: Expose a `const fn` from ContentType when possible. (see RFC#1817)
pub use crate::parse::Indexed; pub use crate::parse::Indexed;
pub use crate::media_type::{MediaParams, Source};
pub use smallvec::{SmallVec, Array}; pub use smallvec::{SmallVec, Array};
// These we need to expose for core.
pub mod cookie { pub mod cookie {
pub use cookie::*; pub use cookie::*;
pub use crate::cookies::Key; pub use crate::cookies::Key;
} }
// These as well.
pub use crate::listener::{Incoming, Listener, Connection, bind_tcp}; pub use crate::listener::{Incoming, Listener, Connection, bind_tcp};
} }

View File

@ -3,72 +3,14 @@ use std::str::FromStr;
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use either::Either;
use crate::ext::IntoCollection; use crate::ext::IntoCollection;
use crate::uncased::UncasedStr; use crate::uncased::UncasedStr;
use crate::parse::{Indexed, IndexedString, parse_media_type}; use crate::parse::{Indexed, IndexedString, parse_media_type};
use smallvec::SmallVec; use smallvec::SmallVec;
#[derive(Debug, Clone)]
struct MediaParam {
key: IndexedString,
value: IndexedString,
}
// FIXME: `Static` is needed for `const` items. Need `const SmallVec::new`.
#[derive(Debug, Clone)]
pub enum MediaParams {
Static(&'static [(IndexedString, IndexedString)]),
Dynamic(SmallVec<[(IndexedString, IndexedString); 2]>)
}
impl Default for MediaParams {
fn default() -> Self {
MediaParams::Dynamic(SmallVec::new())
}
}
impl Extend<(IndexedString, IndexedString)> for MediaParams {
fn extend<T: IntoIterator<Item = (IndexedString, IndexedString)>>(&mut self, iter: T) {
match self {
MediaParams::Static(..) => panic!("can't add to static collection!"),
MediaParams::Dynamic(ref mut v) => v.extend(iter)
}
}
}
impl PartialEq for MediaParams {
fn eq(&self, other: &MediaParams) -> bool {
#[inline(always)]
fn inner_types(params: &MediaParams) -> &[(IndexedString, IndexedString)] {
match *params {
MediaParams::Static(params) => params,
MediaParams::Dynamic(ref vec) => vec,
}
}
inner_types(self) == inner_types(other)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Source {
Known(&'static str),
Custom(Cow<'static, str>),
None
}
impl Source {
#[inline]
fn as_str(&self) -> Option<&str> {
match *self {
Source::Known(s) => Some(s),
Source::Custom(ref s) => Some(s.borrow()),
Source::None => None
}
}
}
/// An HTTP media type. /// An HTTP media type.
/// ///
/// # Usage /// # Usage
@ -110,21 +52,33 @@ impl Source {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MediaType { pub struct MediaType {
/// Storage for the entire media type string. /// Storage for the entire media type string.
#[doc(hidden)] pub(crate) source: Source,
pub source: Source,
/// The top-level type. /// The top-level type.
#[doc(hidden)] pub(crate) top: IndexedString,
pub top: IndexedString,
/// The subtype. /// The subtype.
#[doc(hidden)] pub(crate) sub: IndexedString,
pub sub: IndexedString,
/// The parameters, if any. /// The parameters, if any.
#[doc(hidden)] pub(crate) params: MediaParams
pub params: MediaParams
} }
macro_rules! media_str { #[derive(Debug, Clone)]
($string:expr) => (Indexed::Concrete(Cow::Borrowed($string))) struct MediaParam {
key: IndexedString,
value: IndexedString,
}
// FIXME: `Static` is needed for `const` items. Need `const SmallVec::new`.
#[derive(Debug, Clone)]
pub(crate) enum MediaParams {
Static(&'static [(&'static str, &'static str)]),
Dynamic(SmallVec<[(IndexedString, IndexedString); 2]>)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum Source {
Known(&'static str),
Custom(Cow<'static, str>),
None
} }
macro_rules! media_types { macro_rules! media_types {
@ -136,12 +90,10 @@ macro_rules! media_types {
$(; @{$k}! @[=]! @{$v}!)* @{"`"}!. $(; @{$k}! @[=]! @{$v}!)* @{"`"}!.
]; ];
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
pub const $name: MediaType = MediaType { pub const $name: MediaType = MediaType::new_known(
source: Source::Known(concat!($t, "/", $s, $("; ", $k, "=", $v),*)), concat!($t, "/", $s, $("; ", $k, "=", $v),*),
top: media_str!($t), $t, $s, &[$(($k, $v)),*]
sub: media_str!($s), );
params: MediaParams::Static(&[$((media_str!($k), media_str!($v))),*])
};
); );
)+ )+
@ -357,6 +309,50 @@ impl MediaType {
} }
} }
/// A `const` variant of [`MediaType::with_params()`]. Creates a new
/// `MediaType` with top-level type `top`, subtype `sub`, and parameters
/// `params`, which may be empty.
///
/// # Example
///
/// Create a custom `application/x-person` media type:
///
/// ```rust
/// use rocket::http::MediaType;
///
/// let custom = MediaType::const_new("application", "x-person", &[]);
/// assert_eq!(custom.top(), "application");
/// assert_eq!(custom.sub(), "x-person");
/// ```
#[inline]
pub const fn const_new(
top: &'static str,
sub: &'static str,
params: &'static [(&'static str, &'static str)]
) -> MediaType {
MediaType {
source: Source::None,
top: Indexed::Concrete(Cow::Borrowed(top)),
sub: Indexed::Concrete(Cow::Borrowed(sub)),
params: MediaParams::Static(params),
}
}
#[inline]
pub(crate) const fn new_known(
source: &'static str,
top: &'static str,
sub: &'static str,
params: &'static [(&'static str, &'static str)]
) -> MediaType {
MediaType {
source: Source::Known(source),
top: Indexed::Concrete(Cow::Borrowed(top)),
sub: Indexed::Concrete(Cow::Borrowed(sub)),
params: MediaParams::Static(params),
}
}
known_shorthands!(parse_flexible); known_shorthands!(parse_flexible);
known_extensions!(from_extension); known_extensions!(from_extension);
@ -501,16 +497,15 @@ impl MediaType {
/// ``` /// ```
#[inline] #[inline]
pub fn params<'a>(&'a self) -> impl Iterator<Item=(&'a str, &'a str)> + 'a { pub fn params<'a>(&'a self) -> impl Iterator<Item=(&'a str, &'a str)> + 'a {
let param_slice = match self.params { match self.params {
MediaParams::Static(slice) => slice, MediaParams::Static(ref slice) => Either::Left(slice.iter().cloned()),
MediaParams::Dynamic(ref vec) => &vec[..], MediaParams::Dynamic(ref vec) => {
}; Either::Right(vec.iter().map(move |&(ref key, ref val)| {
let source_str = self.source.as_str();
param_slice.iter() (key.from_source(source_str), val.from_source(source_str))
.map(move |&(ref key, ref val)| { }))
let source_str = self.source.as_str(); }
(key.from_source(source_str), val.from_source(source_str)) }
})
} }
known_media_types!(media_types); known_media_types!(media_types);
@ -561,3 +556,29 @@ impl fmt::Display for MediaType {
} }
} }
} }
impl Default for MediaParams {
fn default() -> Self {
MediaParams::Dynamic(SmallVec::new())
}
}
impl Extend<(IndexedString, IndexedString)> for MediaParams {
fn extend<T: IntoIterator<Item = (IndexedString, IndexedString)>>(&mut self, iter: T) {
match self {
MediaParams::Static(..) => panic!("can't add to static collection!"),
MediaParams::Dynamic(ref mut v) => v.extend(iter)
}
}
}
impl Source {
#[inline]
fn as_str(&self) -> Option<&str> {
match *self {
Source::Known(s) => Some(s),
Source::Custom(ref s) => Some(s.borrow()),
Source::None => None
}
}
}

View File

@ -8,5 +8,4 @@ pub use self::accept::*;
pub mod uri; pub mod uri;
// Exposed for codegen. pub use self::indexed::*;
#[doc(hidden)] pub use self::indexed::*;