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 {
fn to_tokens(&self, tokens: &mut TokenStream) {
// Yeah, yeah. (((((i))).kn0w()))
let media_type = MediaType((self.0).clone().0);
let http_media_type = self.0.media_type().clone();
let media_type = MediaType(http_media_type);
tokens.extend(quote!(::rocket::http::ContentType(#media_type)));
}
}
@ -94,27 +94,13 @@ impl FromMeta for MediaType {
impl ToTokens for MediaType {
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 (keys, values) = self.0.params().split2();
let http = quote!(::rocket::http);
let cow = quote!(::std::borrow::Cow);
let (pub_http, http) = (quote!(::rocket::http), quote!(::rocket::http::private));
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))
)),*
])
}))
tokens.extend(quote! {
#http::MediaType::const_new(#top, #sub, &[#((#keys, #values)),*])
});
}
}

View File

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

View File

@ -3,120 +3,12 @@ use std::str::FromStr;
use std::fmt;
use smallvec::SmallVec;
use either::Either;
use crate::{Header, MediaType};
use crate::ext::IntoCollection;
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.
///
/// 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();
/// ```
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone)]
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 {
($($name:ident ($check:ident): $str:expr, $t:expr,
$s:expr $(; $k:expr => $v:expr)*,)+) => {
@ -173,19 +76,12 @@ macro_rules! accept_constructor {
#[doc="</i>"]
#[allow(non_upper_case_globals)]
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 {
/// Constructs a new `Accept` header from one or more media types.
///
@ -314,12 +210,10 @@ impl Accept {
/// ```
#[inline(always)]
pub fn iter<'a>(&'a self) -> impl Iterator<Item=&'a QMediaType> + 'a {
let slice = match self.0 {
AcceptParams::Static(slice) => slice,
AcceptParams::Dynamic(ref vec) => &vec[..],
};
slice.iter()
match self.0 {
AcceptParams::Static(ref val) => Either::Left(Some(val).into_iter()),
AcceptParams::Dynamic(ref vec) => Either::Right(vec.iter())
}
}
/// Returns an iterator over all of the (bare) media types in `self`. Media
@ -351,6 +245,19 @@ impl Accept {
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 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
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)]
mod test {
use crate::{Accept, MediaType};

View File

@ -49,21 +49,17 @@ pub mod uncased {
#[doc(inline)] pub use uncased::*;
}
// Types that we expose for use by core.
#[doc(hidden)]
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::media_type::{MediaParams, Source};
pub use smallvec::{SmallVec, Array};
// These we need to expose for core.
pub mod cookie {
pub use cookie::*;
pub use crate::cookies::Key;
}
// These as well.
pub use crate::listener::{Incoming, Listener, Connection, bind_tcp};
}

View File

@ -3,72 +3,14 @@ use std::str::FromStr;
use std::fmt;
use std::hash::{Hash, Hasher};
use either::Either;
use crate::ext::IntoCollection;
use crate::uncased::UncasedStr;
use crate::parse::{Indexed, IndexedString, parse_media_type};
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.
///
/// # Usage
@ -110,21 +52,33 @@ impl Source {
#[derive(Debug, Clone)]
pub struct MediaType {
/// Storage for the entire media type string.
#[doc(hidden)]
pub source: Source,
pub(crate) source: Source,
/// The top-level type.
#[doc(hidden)]
pub top: IndexedString,
pub(crate) top: IndexedString,
/// The subtype.
#[doc(hidden)]
pub sub: IndexedString,
pub(crate) sub: IndexedString,
/// The parameters, if any.
#[doc(hidden)]
pub params: MediaParams
pub(crate) params: MediaParams
}
macro_rules! media_str {
($string:expr) => (Indexed::Concrete(Cow::Borrowed($string)))
#[derive(Debug, Clone)]
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 {
@ -136,12 +90,10 @@ macro_rules! media_types {
$(; @{$k}! @[=]! @{$v}!)* @{"`"}!.
];
#[allow(non_upper_case_globals)]
pub const $name: MediaType = MediaType {
source: Source::Known(concat!($t, "/", $s, $("; ", $k, "=", $v),*)),
top: media_str!($t),
sub: media_str!($s),
params: MediaParams::Static(&[$((media_str!($k), media_str!($v))),*])
};
pub const $name: MediaType = MediaType::new_known(
concat!($t, "/", $s, $("; ", $k, "=", $v),*),
$t, $s, &[$(($k, $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_extensions!(from_extension);
@ -501,16 +497,15 @@ impl MediaType {
/// ```
#[inline]
pub fn params<'a>(&'a self) -> impl Iterator<Item=(&'a str, &'a str)> + 'a {
let param_slice = match self.params {
MediaParams::Static(slice) => slice,
MediaParams::Dynamic(ref vec) => &vec[..],
};
param_slice.iter()
.map(move |&(ref key, ref val)| {
match self.params {
MediaParams::Static(ref slice) => Either::Left(slice.iter().cloned()),
MediaParams::Dynamic(ref vec) => {
Either::Right(vec.iter().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);
@ -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;
// Exposed for codegen.
#[doc(hidden)] pub use self::indexed::*;
pub use self::indexed::*;