use std::fmt;
use std::str::FromStr;
use neo_mime_parse::{Mime, Parse};
use crate::{InvalidMime, MediaType, Value};
#[derive(Clone)]
pub struct MediaRange {
pub(super) mime: Mime,
}
impl MediaRange {
#[inline]
pub fn parse(source: impl Parse) -> Result<Self, InvalidMime> {
neo_mime_parse::Parser::can_range()
.parse(source)
.map(|mime| MediaRange { mime })
.map_err(|e| InvalidMime { inner: e })
}
#[inline]
pub fn type_(&self) -> &str {
self.mime.type_()
}
#[inline]
pub fn subtype(&self) -> &str {
self.mime.subtype()
}
#[inline]
pub fn suffix(&self) -> Option<&str> {
self.mime.suffix()
}
pub fn matches(&self, mt: &MediaType) -> bool {
let type_ = self.type_();
if type_ == crate::STAR {
debug_assert_eq!(self.subtype(), crate::STAR);
return self.matches_params(mt);
}
if type_ != mt.type_() {
return false;
}
let subtype = self.subtype();
if subtype == crate::STAR {
return self.matches_params(mt);
}
if subtype != mt.subtype() {
return false;
}
self.matches_params(mt)
}
fn matches_params(&self, mt: &MediaType) -> bool {
for (name, value) in self.params() {
if name != "q" && mt.param(name) != Some(value) {
return false;
}
}
true
}
pub fn param<'a>(&'a self, attr: &str) -> Option<Value<'a>> {
crate::value::param(&self.mime, attr)
}
#[inline]
pub fn params(&self) -> impl Iterator<Item = (&str, Value)> {
crate::value::params(&self.mime)
}
#[inline]
pub fn has_params(&self) -> bool {
self.mime.has_params()
}
#[cfg(test)]
pub(super) fn test_assert_asterisks(&self) {
}
}
impl From<MediaType> for MediaRange {
fn from(mt: MediaType) -> MediaRange {
MediaRange {
mime: mt.mime,
}
}
}
impl PartialEq for MediaRange {
fn eq(&self, other: &MediaRange) -> bool {
crate::cmp::mime_eq(&self.mime, &other.mime)
}
}
impl PartialEq<str> for MediaRange {
fn eq(&self, s: &str) -> bool {
crate::cmp::str_eq(&self.mime, s)
}
}
impl<'a> PartialEq<&'a str> for MediaRange {
#[inline]
fn eq(&self, s: & &'a str) -> bool {
self == *s
}
}
impl<'a> PartialEq<MediaRange> for &'a str {
#[inline]
fn eq(&self, mr: &MediaRange) -> bool {
mr == self
}
}
impl PartialEq<MediaRange> for str {
#[inline]
fn eq(&self, mr: &MediaRange) -> bool {
mr == self
}
}
impl FromStr for MediaRange {
type Err = InvalidMime;
fn from_str(s: &str) -> Result<MediaRange, Self::Err> {
MediaRange::parse(s)
}
}
impl AsRef<str> for MediaRange {
fn as_ref(&self) -> &str {
self.mime.as_ref()
}
}
impl fmt::Debug for MediaRange {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.mime, f)
}
}
impl fmt::Display for MediaRange {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.mime, f)
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn media_range_from_str() {
assert_eq!(MediaRange::parse("text/plain").unwrap(), MediaRange::from(TEXT_PLAIN));
let any = "*/*".parse::<MediaRange>().unwrap();
assert_eq!(any, "*/*");
assert_eq!(any, STAR_STAR);
assert_eq!("image/*".parse::<MediaRange>().unwrap(), "image/*");
assert_eq!("text/*; charset=utf-8".parse::<MediaRange>().unwrap(), "text/*; charset=utf-8");
MediaRange::parse("text/*plain").unwrap_err();
}
#[test]
fn media_range_matches() {
assert!(STAR_STAR.matches(&TEXT_PLAIN), "*/* matches everything");
assert!(TEXT_STAR.matches(&TEXT_PLAIN), "text/* matches text/plain");
assert!(TEXT_STAR.matches(&TEXT_HTML), "text/* matches text/html");
assert!(TEXT_STAR.matches(&TEXT_HTML_UTF_8), "text/* matches text/html; charset=utf-8");
assert!(!TEXT_STAR.matches(&IMAGE_GIF), "text/* doesn't match image/gif");
}
#[test]
fn media_range_matches_params() {
let text_any_utf8 = MediaRange::parse("text/*; charset=utf-8").unwrap();
assert!(text_any_utf8.matches(&TEXT_PLAIN_UTF_8));
assert!(text_any_utf8.matches(&TEXT_HTML_UTF_8));
assert!(!text_any_utf8.matches(&TEXT_HTML));
let many_params = MediaType::parse("text/plain; charset=utf-8; foo=bar").unwrap();
assert!(text_any_utf8.matches(&many_params));
let text_plain = MediaRange::parse("text/plain").unwrap();
assert!(text_plain.matches(&many_params));
}
#[test]
fn media_range_matches_skips_q() {
let range = MediaRange::parse("text/*; q=0.8").unwrap();
assert!(range.matches(&TEXT_PLAIN_UTF_8));
assert!(range.matches(&TEXT_HTML_UTF_8));
let range = MediaRange::parse("text/*; charset=utf-8; q=0.8").unwrap();
assert!(range.matches(&TEXT_PLAIN_UTF_8));
assert!(range.matches(&TEXT_HTML_UTF_8));
assert!(!range.matches(&TEXT_HTML));
}
}