actix_web/guard/
acceptable.rs1use super::{Guard, GuardContext};
2use crate::http::header::Accept;
3
4#[derive(Debug, Clone)]
20pub struct Acceptable {
21 mime: mime::Mime,
22
23 match_star_star: bool,
27}
28
29impl Acceptable {
30 pub fn new(mime: mime::Mime) -> Self {
32 Self {
33 mime,
34 match_star_star: false,
35 }
36 }
37
38 pub fn match_star_star(mut self) -> Self {
40 self.match_star_star = true;
41 self
42 }
43}
44
45impl Guard for Acceptable {
46 fn check(&self, ctx: &GuardContext<'_>) -> bool {
47 let accept = match ctx.header::<Accept>() {
48 Some(hdr) => hdr,
49 None => return false,
50 };
51
52 let target_type = self.mime.type_();
53 let target_subtype = self.mime.subtype();
54
55 for mime in accept.0.into_iter().map(|q| q.item) {
56 return match (mime.type_(), mime.subtype()) {
57 (typ, subtype) if typ == target_type && subtype == target_subtype => true,
58 (typ, mime::STAR) if typ == target_type => true,
59 (mime::STAR, mime::STAR) if self.match_star_star => true,
60 _ => continue,
61 };
62 }
63
64 false
65 }
66
67 #[cfg(feature = "experimental-introspection")]
68 fn name(&self) -> String {
69 if self.match_star_star {
70 format!("Acceptable({}, match_star_star=true)", self.mime)
71 } else {
72 format!("Acceptable({})", self.mime)
73 }
74 }
75
76 #[cfg(feature = "experimental-introspection")]
77 fn details(&self) -> Option<Vec<super::GuardDetail>> {
78 let mut details = Vec::new();
79 details.push(super::GuardDetail::Generic(format!("mime={}", self.mime)));
80 if self.match_star_star {
81 details.push(super::GuardDetail::Generic(
82 "match_star_star=true".to_string(),
83 ));
84 }
85 Some(details)
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use crate::{http::header, test::TestRequest};
93
94 #[test]
95 fn test_acceptable() {
96 let req = TestRequest::default().to_srv_request();
97 assert!(!Acceptable::new(mime::APPLICATION_JSON).check(&req.guard_ctx()));
98
99 let req = TestRequest::default()
100 .insert_header((header::ACCEPT, "application/json"))
101 .to_srv_request();
102 assert!(Acceptable::new(mime::APPLICATION_JSON).check(&req.guard_ctx()));
103
104 let req = TestRequest::default()
105 .insert_header((header::ACCEPT, "text/html, application/json"))
106 .to_srv_request();
107 assert!(Acceptable::new(mime::APPLICATION_JSON).check(&req.guard_ctx()));
108 }
109
110 #[test]
111 fn test_acceptable_star() {
112 let req = TestRequest::default()
113 .insert_header((header::ACCEPT, "text/html, */*;q=0.8"))
114 .to_srv_request();
115
116 assert!(Acceptable::new(mime::APPLICATION_JSON)
117 .match_star_star()
118 .check(&req.guard_ctx()));
119 }
120
121 #[cfg(feature = "experimental-introspection")]
122 #[test]
123 fn acceptable_guard_details_include_mime() {
124 let guard = Acceptable::new(mime::APPLICATION_JSON).match_star_star();
125 let details = guard.details().expect("missing guard details");
126
127 assert!(details.iter().any(|detail| match detail {
128 crate::guard::GuardDetail::Generic(value) => value == "match_star_star=true",
129 _ => false,
130 }));
131 let expected = format!("mime={}", mime::APPLICATION_JSON);
132 assert!(details.iter().any(|detail| match detail {
133 crate::guard::GuardDetail::Generic(value) => value == &expected,
134 _ => false,
135 }));
136 assert_eq!(
137 guard.name(),
138 format!(
139 "Acceptable({}, match_star_star=true)",
140 mime::APPLICATION_JSON
141 )
142 );
143 }
144}