use super::{Guard, GuardContext};
use crate::http::header::Accept;
#[derive(Debug, Clone)]
pub struct Acceptable {
mime: mime::Mime,
match_star_star: bool,
}
impl Acceptable {
pub fn new(mime: mime::Mime) -> Self {
Self {
mime,
match_star_star: false,
}
}
pub fn match_star_star(mut self) -> Self {
self.match_star_star = true;
self
}
}
impl Guard for Acceptable {
fn check(&self, ctx: &GuardContext<'_>) -> bool {
let accept = match ctx.header::<Accept>() {
Some(hdr) => hdr,
None => return false,
};
let target_type = self.mime.type_();
let target_subtype = self.mime.subtype();
for mime in accept.0.into_iter().map(|q| q.item) {
return match (mime.type_(), mime.subtype()) {
(typ, subtype) if typ == target_type && subtype == target_subtype => true,
(typ, mime::STAR) if typ == target_type => true,
(mime::STAR, mime::STAR) if self.match_star_star => true,
_ => continue,
};
}
false
}
#[cfg(feature = "experimental-introspection")]
fn name(&self) -> String {
if self.match_star_star {
format!("Acceptable({}, match_star_star=true)", self.mime)
} else {
format!("Acceptable({})", self.mime)
}
}
#[cfg(feature = "experimental-introspection")]
fn details(&self) -> Option<Vec<super::GuardDetail>> {
let mut details = Vec::new();
details.push(super::GuardDetail::Generic(format!("mime={}", self.mime)));
if self.match_star_star {
details.push(super::GuardDetail::Generic(
"match_star_star=true".to_string(),
));
}
Some(details)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{http::header, test::TestRequest};
#[test]
fn test_acceptable() {
let req = TestRequest::default().to_srv_request();
assert!(!Acceptable::new(mime::APPLICATION_JSON).check(&req.guard_ctx()));
let req = TestRequest::default()
.insert_header((header::ACCEPT, "application/json"))
.to_srv_request();
assert!(Acceptable::new(mime::APPLICATION_JSON).check(&req.guard_ctx()));
let req = TestRequest::default()
.insert_header((header::ACCEPT, "text/html, application/json"))
.to_srv_request();
assert!(Acceptable::new(mime::APPLICATION_JSON).check(&req.guard_ctx()));
}
#[test]
fn test_acceptable_star() {
let req = TestRequest::default()
.insert_header((header::ACCEPT, "text/html, */*;q=0.8"))
.to_srv_request();
assert!(Acceptable::new(mime::APPLICATION_JSON)
.match_star_star()
.check(&req.guard_ctx()));
}
#[cfg(feature = "experimental-introspection")]
#[test]
fn acceptable_guard_details_include_mime() {
let guard = Acceptable::new(mime::APPLICATION_JSON).match_star_star();
let details = guard.details().expect("missing guard details");
assert!(details.iter().any(|detail| match detail {
crate::guard::GuardDetail::Generic(value) => value == "match_star_star=true",
_ => false,
}));
let expected = format!("mime={}", mime::APPLICATION_JSON);
assert!(details.iter().any(|detail| match detail {
crate::guard::GuardDetail::Generic(value) => value == &expected,
_ => false,
}));
assert_eq!(
guard.name(),
format!(
"Acceptable({}, match_star_star=true)",
mime::APPLICATION_JSON
)
);
}
}