use super::media_type::MediaType;
#[derive(Debug, Clone)]
pub struct AcceptHeader {
pub media_types: Vec<MediaType>,
}
impl AcceptHeader {
pub fn parse(header: &str) -> Self {
let mut media_types: Vec<MediaType> = header
.split(',')
.filter_map(|s| MediaType::parse(s.trim()))
.collect();
media_types.sort_by(|a, b| {
b.quality
.partial_cmp(&a.quality)
.unwrap_or(std::cmp::Ordering::Equal)
});
Self { media_types }
}
pub fn empty() -> Self {
Self {
media_types: Vec::new(),
}
}
pub fn find_best_match(&self, available: &[MediaType]) -> Option<MediaType> {
for accepted in &self.media_types {
for available_type in available {
if accepted.matches(available_type) {
return Some(available_type.clone());
}
}
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
#[test]
fn test_parse_accept_header() {
let accept = AcceptHeader::parse("application/json, text/html; q=0.9");
assert_eq!(accept.media_types.len(), 2);
assert_eq!(accept.media_types[0].quality, 1.0);
}
#[test]
fn test_find_best_match() {
let accept = AcceptHeader::parse("application/json, text/html");
let available = vec![
MediaType::new("text", "html"),
MediaType::new("application", "xml"),
];
let best = accept.find_best_match(&available);
assert!(best.is_some());
}
#[rstest]
#[case("text/html;q=NaN", 0)]
#[case("text/html;q=NaN, application/json", 1)]
#[case("text/html, application/json;q=NaN", 1)]
fn test_parse_does_not_panic_on_nan_quality(#[case] input: &str, #[case] expected_len: usize) {
let accept = AcceptHeader::parse(input);
assert_eq!(accept.media_types.len(), expected_len);
}
}