http_types/content/
media_type_proposal.rsuse crate::ensure;
use crate::headers::HeaderValue;
use crate::Mime;
use std::ops::{Deref, DerefMut};
use std::{
cmp::{Ordering, PartialEq},
str::FromStr,
};
#[derive(Debug, Clone, PartialEq)]
pub struct MediaTypeProposal {
pub(crate) media_type: Mime,
weight: Option<f32>,
}
impl MediaTypeProposal {
pub fn new(media_type: impl Into<Mime>, weight: Option<f32>) -> crate::Result<Self> {
if let Some(weight) = weight {
ensure!(
weight.is_sign_positive() && weight <= 1.0,
"MediaTypeProposal should have a weight between 0.0 and 1.0"
)
}
Ok(Self {
media_type: media_type.into(),
weight,
})
}
pub fn media_type(&self) -> &Mime {
&self.media_type
}
pub fn weight(&self) -> Option<f32> {
self.weight
}
pub(crate) fn from_str(s: &str) -> crate::Result<Self> {
let mut media_type = Mime::from_str(s)?;
let weight = media_type
.remove_param("q")
.map(|param| param.as_str().parse())
.transpose()?;
Self::new(media_type, weight)
}
}
impl From<Mime> for MediaTypeProposal {
fn from(media_type: Mime) -> Self {
Self {
media_type,
weight: None,
}
}
}
impl From<MediaTypeProposal> for Mime {
fn from(accept: MediaTypeProposal) -> Self {
accept.media_type
}
}
impl PartialEq<Mime> for MediaTypeProposal {
fn eq(&self, other: &Mime) -> bool {
self.media_type == *other
}
}
impl PartialEq<Mime> for &MediaTypeProposal {
fn eq(&self, other: &Mime) -> bool {
self.media_type == *other
}
}
impl Deref for MediaTypeProposal {
type Target = Mime;
fn deref(&self) -> &Self::Target {
&self.media_type
}
}
impl DerefMut for MediaTypeProposal {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.media_type
}
}
impl PartialOrd for MediaTypeProposal {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match (self.weight, other.weight) {
(Some(left), Some(right)) => left.partial_cmp(&right),
(Some(_), None) => Some(Ordering::Greater),
(None, Some(_)) => Some(Ordering::Less),
(None, None) => None,
}
}
}
impl From<MediaTypeProposal> for HeaderValue {
fn from(entry: MediaTypeProposal) -> HeaderValue {
let s = match entry.weight {
Some(weight) => format!("{};q={:.3}", entry.media_type, weight),
None => entry.media_type.to_string(),
};
unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) }
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::mime;
#[test]
fn smoke() {
let _ = MediaTypeProposal::new(mime::JSON, Some(0.0)).unwrap();
let _ = MediaTypeProposal::new(mime::XML, Some(0.5)).unwrap();
let _ = MediaTypeProposal::new(mime::HTML, Some(1.0)).unwrap();
}
#[test]
fn error_code_500() {
let err = MediaTypeProposal::new(mime::JSON, Some(1.1)).unwrap_err();
assert_eq!(err.status(), 500);
let err = MediaTypeProposal::new(mime::XML, Some(-0.1)).unwrap_err();
assert_eq!(err.status(), 500);
let err = MediaTypeProposal::new(mime::HTML, Some(-0.0)).unwrap_err();
assert_eq!(err.status(), 500);
}
}