126 lines
3.1 KiB
Rust
126 lines
3.1 KiB
Rust
use axum::http::header::ACCEPT;
|
|
use axum::http::HeaderMap;
|
|
use mime::{Mime, TEXT_HTML};
|
|
use std::cmp::Ordering;
|
|
|
|
pub fn accepts_html(headers: &HeaderMap) -> bool {
|
|
headers.get(ACCEPT).map_or(false, |val| {
|
|
let s = val.to_str().unwrap();
|
|
parse_accept(s).accepts(&TEXT_HTML)
|
|
})
|
|
}
|
|
|
|
pub fn best_match<'a>(headers: &HeaderMap, options: &'a [&'a Mime]) -> Option<&'a Mime> {
|
|
headers
|
|
.get(ACCEPT)
|
|
.map(|val| {
|
|
let accept = parse_accept(val.to_str().unwrap());
|
|
for &opt in options.iter() {
|
|
if accept.accepts(opt) {
|
|
return Some(opt);
|
|
}
|
|
}
|
|
None
|
|
})
|
|
.flatten()
|
|
}
|
|
|
|
fn parse_accept(s: &str) -> Accept {
|
|
let mut entries = s
|
|
.split(",")
|
|
.flat_map(|s| s.trim().parse::<Mime>().ok())
|
|
.collect::<Vec<_>>();
|
|
entries.sort_by(|a, b| {
|
|
if b.is_wildcard() || b.is_subtype_wildcard() {
|
|
Ordering::Less
|
|
} else {
|
|
b.quality()
|
|
.unwrap_or(1f32)
|
|
.partial_cmp(&a.quality().unwrap_or(1f32))
|
|
.unwrap()
|
|
}
|
|
});
|
|
Accept { entries }
|
|
}
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
struct Accept {
|
|
entries: Vec<Mime>,
|
|
}
|
|
|
|
impl Accept {
|
|
fn accepts(&self, mime: &Mime) -> bool {
|
|
self.entries.iter().any(|e| {
|
|
if e == mime {
|
|
true
|
|
} else if e.type_() == mime.type_() && e.is_subtype_wildcard() {
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
trait MimeExt {
|
|
fn quality(&self) -> Option<f32>;
|
|
fn is_wildcard(&self) -> bool;
|
|
fn is_subtype_wildcard(&self) -> bool;
|
|
}
|
|
|
|
impl MimeExt for Mime {
|
|
fn quality(&self) -> Option<f32> {
|
|
self.params()
|
|
.find(|p| p.0 == "q")
|
|
.map(|(_, val)| val.as_str().parse().unwrap())
|
|
}
|
|
|
|
fn is_wildcard(&self) -> bool {
|
|
*self == mime::STAR_STAR
|
|
}
|
|
|
|
fn is_subtype_wildcard(&self) -> bool {
|
|
self.subtype() == "*"
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use axum::http::HeaderMap;
|
|
|
|
use super::best_match;
|
|
|
|
#[test]
|
|
fn test_sort_accept() {
|
|
let accept =
|
|
super::parse_accept("text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c");
|
|
assert_eq!(
|
|
accept.entries,
|
|
vec![
|
|
"text/html",
|
|
"text/x-c",
|
|
"text/x-dvi; q=0.8",
|
|
"text/plain; q=0.5"
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_accept() {
|
|
let accept =
|
|
super::parse_accept("text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c");
|
|
assert!(accept.accepts(&"text/html".parse().unwrap()));
|
|
assert!(!accept.accepts(&"application/json".parse().unwrap()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_best_match() {
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert("Accept", "text/plain; q=0.5, text/html".parse().unwrap());
|
|
assert_eq!(
|
|
best_match(&headers, &[&mime::TEXT_HTML, &mime::APPLICATION_JAVASCRIPT]),
|
|
Some(&mime::TEXT_HTML)
|
|
);
|
|
}
|
|
}
|