rouille/input/
priority_header.rs1use std::f32;
11use std::str::FromStr;
12use std::str::Split;
13
14pub fn priority_header_preferred<'a, I>(input: &'a str, elements: I) -> Option<usize>
31where
32 I: Iterator<Item = &'a str>,
33{
34 let mut result = (None, f32::NEG_INFINITY);
35
36 for (index, req_elem) in elements.enumerate() {
37 for (header_elem, prio) in parse_priority_header(input) {
38 if prio <= result.1 {
39 continue;
40 }
41
42 if req_elem == header_elem {
43 result = (Some(index), prio);
44 continue;
45 }
46
47 let (req_elem_left, req_elem_right) = {
48 let mut parts = req_elem.split('/');
49 let left = parts.next();
50 let right = parts.next();
51 (left, right)
52 };
53
54 let (header_elem_left, header_elem_right) = {
55 let mut parts = header_elem.split('/');
56 let left = parts.next();
57 let right = parts.next();
58 (left, right)
59 };
60
61 if (req_elem_left == Some("*") || header_elem_left == Some("*"))
62 && (req_elem_right == header_elem_right
63 || req_elem_right == Some("*")
64 || header_elem_right == Some("*"))
65 {
66 result = (Some(index), prio);
67 continue;
68 }
69
70 if (req_elem_right == Some("*") || header_elem_right == Some("*"))
71 && (req_elem_left == header_elem_left
72 || req_elem_left == Some("*")
73 || header_elem_left == Some("*"))
74 {
75 result = (Some(index), prio);
76 continue;
77 }
78 }
79 }
80
81 result.0
82}
83
84#[inline]
99pub fn parse_priority_header(input: &str) -> PriorityHeaderIter {
100 PriorityHeaderIter {
101 iter: input.split(','),
102 }
103}
104
105pub struct PriorityHeaderIter<'a> {
109 iter: Split<'a, char>,
110}
111
112impl<'a> Iterator for PriorityHeaderIter<'a> {
113 type Item = (&'a str, f32);
114
115 fn next(&mut self) -> Option<Self::Item> {
116 loop {
117 let elem = match self.iter.next() {
118 Some(n) => n,
119 None => return None,
120 };
121
122 let mut params = elem.split(';');
123
124 let t = match params.next() {
125 Some(t) => t.trim(),
126 None => continue,
127 };
128
129 let mut value = 1.0f32;
130
131 for p in params {
132 let trimmed_p = p.trim_start();
133 if let Some(stripped) = trimmed_p.strip_prefix("q=") {
134 if let Ok(val) = FromStr::from_str(stripped.trim()) {
135 value = val;
136 break;
137 }
138 }
139 }
140
141 return Some((t, value));
142 }
143 }
144
145 #[inline]
146 fn size_hint(&self) -> (usize, Option<usize>) {
147 let (_, len) = self.iter.size_hint();
148 (0, len)
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::parse_priority_header;
155 use super::priority_header_preferred;
156
157 #[test]
158 fn parse_basic() {
159 let mut iter = parse_priority_header("text/plain; q=1.5, */*");
160 assert_eq!(iter.next().unwrap(), ("text/plain", 1.5));
161 assert_eq!(iter.next().unwrap(), ("*/*", 1.0));
162 assert_eq!(iter.next(), None);
163 }
164
165 #[test]
166 fn parse_white_spaces() {
167 let mut iter = parse_priority_header(" text/plain ; q= 1.5 , */* ");
168 assert_eq!(iter.next().unwrap(), ("text/plain", 1.5));
169 assert_eq!(iter.next().unwrap(), ("*/*", 1.0));
170 assert_eq!(iter.next(), None);
171 }
172
173 #[test]
174 fn preferred_basic() {
175 let header = "text/plain; q=1.2, image/png; q=2.0";
176 let handled = ["image/gif", "image/png", "text/plain"];
177 assert_eq!(
178 priority_header_preferred(header, handled.iter().cloned()),
179 Some(1)
180 );
181 }
182
183 #[test]
184 fn preferred_multimatch_first() {
185 let header = "text/plain";
186 let handled = ["text/plain", "text/plain"];
187 assert_eq!(
188 priority_header_preferred(header, handled.iter().cloned()),
189 Some(0)
190 );
191 }
192
193 #[test]
194 fn preferred_wildcard_header() {
195 let header = "text/plain; q=1.2, */*";
196 let handled = ["image/gif"];
197 assert_eq!(
198 priority_header_preferred(header, handled.iter().cloned()),
199 Some(0)
200 );
201 }
202
203 #[test]
204 fn preferred_wildcard_header_left() {
205 let header = "text/*; q=2.0, */*";
206 let handled = ["image/gif", "text/html"];
207 assert_eq!(
208 priority_header_preferred(header, handled.iter().cloned()),
209 Some(1)
210 );
211 }
212
213 #[test]
214 fn preferred_empty() {
215 let header = "*/*";
216 let handled = [];
217 assert_eq!(
218 priority_header_preferred(header, handled.iter().cloned()),
219 None
220 );
221 }
222}