use super::*;
use std::fmt;
#[derive(Debug, Clone)]
pub struct Accept(Vec<MediaTypeRange>);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MediaTypeRange {
pub type_: Option<MediaType>,
pub subtype: Option<String>,
pub params: Vec<(String, Option<String>)>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MediaType {
Text,
Image,
Audio,
Video,
Application,
Message,
Multipart,
Extension(String),
}
impl MediaType {
pub fn as_str(&self) -> &str {
match self {
MediaType::Text => "text",
MediaType::Image => "image",
MediaType::Audio => "audio",
MediaType::Video => "video",
MediaType::Application => "application",
MediaType::Message => "message",
MediaType::Multipart => "Multipart",
MediaType::Extension(ref s) => s.as_str(),
}
}
}
impl fmt::Display for MediaType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl std::str::FromStr for MediaType {
type Err = HeaderParseError;
fn from_str(s: &str) -> Result<Self, HeaderParseError> {
match s {
"text" => Ok(MediaType::Text),
"image" => Ok(MediaType::Image),
"audio" => Ok(MediaType::Audio),
"video" => Ok(MediaType::Video),
"application" => Ok(MediaType::Application),
"message" => Ok(MediaType::Message),
"multipart" => Ok(MediaType::Multipart),
_ => Ok(MediaType::Extension(String::from(s))),
}
}
}
impl std::ops::Deref for Accept {
type Target = Vec<MediaTypeRange>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for Accept {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl AsRef<Vec<MediaTypeRange>> for Accept {
fn as_ref(&self) -> &Vec<MediaTypeRange> {
&self.0
}
}
impl AsMut<Vec<MediaTypeRange>> for Accept {
fn as_mut(&mut self) -> &mut Vec<MediaTypeRange> {
&mut self.0
}
}
impl From<Vec<MediaTypeRange>> for Accept {
fn from(v: Vec<MediaTypeRange>) -> Self {
Accept(v)
}
}
impl<'a> From<&'a [MediaTypeRange]> for Accept {
fn from(v: &'a [MediaTypeRange]) -> Self {
Accept(v.to_vec())
}
}
impl Accept {
pub fn builder() -> AcceptBuilder {
AcceptBuilder(Vec::new())
}
}
#[derive(Debug, Clone)]
pub struct AcceptBuilder(Vec<MediaTypeRange>);
impl AcceptBuilder {
pub fn media_type(mut self, media_type: MediaTypeRange) -> Self {
self.0.push(media_type);
self
}
pub fn build(self) -> Accept {
Accept(self.0)
}
}
impl super::TypedHeader for Accept {
fn from_headers(headers: impl AsRef<Headers>) -> Result<Option<Self>, HeaderParseError> {
use super::parser_helpers::split_once;
let headers = headers.as_ref();
let header = match headers.get(&ACCEPT) {
None => return Ok(None),
Some(header) => header,
};
let mut media_types = Vec::new();
for media_type_range in header.as_str().split(',') {
let media_type_range = media_type_range.trim();
let mut iter = media_type_range.split(';');
let media_type = iter.next().ok_or(HeaderParseError)?.trim();
let (media_type, media_subtype) =
split_once(media_type, '/').ok_or(HeaderParseError)?;
let media_type = if media_type == "*" {
None
} else {
Some(media_type)
};
let media_subtype = if media_subtype == "*" {
None
} else {
Some(media_subtype)
};
let mut params = Vec::new();
for param in iter {
let param = param.trim();
if let Some((param, value)) = split_once(param, '=') {
params.push((String::from(param), Some(String::from(value))));
} else {
params.push((String::from(param), None));
}
}
media_types.push(MediaTypeRange {
type_: media_type
.map(|s| s.parse())
.transpose()
.map_err(|_| HeaderParseError)?,
subtype: media_subtype.map(String::from),
params,
});
}
Ok(Some(Accept(media_types)))
}
fn insert_into(&self, mut headers: impl AsMut<Headers>) {
use std::fmt::Write;
let headers = headers.as_mut();
let mut media_types = String::new();
for media_type in &self.0 {
if !media_types.is_empty() {
media_types.push_str(", ");
}
if let Some(ref t) = media_type.type_ {
write!(&mut media_types, "{t}").unwrap();
} else {
media_types.push('*');
}
media_types.push('/');
if let Some(ref t) = media_type.subtype {
media_types.push_str(t);
} else {
media_types.push('*');
}
for param in &media_type.params {
media_types.push(';');
if let Some(ref value) = param.1 {
write!(&mut media_types, "{}={}", param.0, value).unwrap();
} else {
media_types.push_str(¶m.0);
}
}
}
headers.insert(ACCEPT, media_types);
}
}
impl super::TypedAppendableHeader for Accept {
fn append_to(&self, mut headers: impl AsMut<Headers>) {
use std::fmt::Write;
let headers = headers.as_mut();
let mut media_types = String::new();
for media_type in &self.0 {
if !media_types.is_empty() {
media_types.push_str(", ");
}
if let Some(ref t) = media_type.type_ {
write!(&mut media_types, "{t}").unwrap();
} else {
media_types.push('*');
}
media_types.push('/');
if let Some(ref t) = media_type.subtype {
media_types.push_str(t);
} else {
media_types.push('*');
}
for param in &media_type.params {
media_types.push(';');
if let Some(ref value) = param.1 {
write!(&mut media_types, "{}={}", param.0, value).unwrap();
} else {
media_types.push_str(¶m.0);
}
}
}
headers.append(ACCEPT, media_types);
}
}