rouille_ng/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 if 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
71 if req_elem_right == Some("*") || header_elem_right == Some("*") {
72 if req_elem_left == header_elem_left
73 || req_elem_left == Some("*")
74 || header_elem_left == Some("*")
75 {
76 result = (Some(index), prio);
77 continue;
78 }
79 }
80 }
81 }
82
83 result.0
84}
85
86#[inline]
101pub fn parse_priority_header(input: &str) -> PriorityHeaderIter {
102 PriorityHeaderIter {
103 iter: input.split(','),
104 }
105}
106
107pub struct PriorityHeaderIter<'a> {
111 iter: Split<'a, char>,
112}
113
114impl<'a> Iterator for PriorityHeaderIter<'a> {
115 type Item = (&'a str, f32);
116
117 fn next(&mut self) -> Option<Self::Item> {
118 loop {
119 let elem = match self.iter.next() {
120 Some(n) => n,
121 None => return None,
122 };
123
124 let mut params = elem.split(';');
125
126 let t = match params.next() {
127 Some(t) => t.trim(),
128 None => continue,
129 };
130
131 let mut value = 1.0f32;
132
133 for p in params {
134 let trimmed_p = p.trim_start();
135 if trimmed_p.starts_with("q=") {
136 if let Ok(val) = FromStr::from_str(&trimmed_p[2..].trim()) {
137 value = val;
138 break;
139 }
140 }
141 }
142
143 return Some((t, value));
144 }
145 }
146
147 #[inline]
148 fn size_hint(&self) -> (usize, Option<usize>) {
149 let (_, len) = self.iter.size_hint();
150 (0, len)
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::parse_priority_header;
157 use super::priority_header_preferred;
158
159 #[test]
160 fn parse_basic() {
161 let mut iter = parse_priority_header("text/plain; q=1.5, */*");
162 assert_eq!(iter.next().unwrap(), ("text/plain", 1.5));
163 assert_eq!(iter.next().unwrap(), ("*/*", 1.0));
164 assert_eq!(iter.next(), None);
165 }
166
167 #[test]
168 fn parse_white_spaces() {
169 let mut iter = parse_priority_header(" text/plain ; q= 1.5 , */* ");
170 assert_eq!(iter.next().unwrap(), ("text/plain", 1.5));
171 assert_eq!(iter.next().unwrap(), ("*/*", 1.0));
172 assert_eq!(iter.next(), None);
173 }
174
175 #[test]
176 fn preferred_basic() {
177 let header = "text/plain; q=1.2, image/png; q=2.0";
178 let handled = ["image/gif", "image/png", "text/plain"];
179 assert_eq!(
180 priority_header_preferred(header, handled.iter().cloned()),
181 Some(1)
182 );
183 }
184
185 #[test]
186 fn preferred_multimatch_first() {
187 let header = "text/plain";
188 let handled = ["text/plain", "text/plain"];
189 assert_eq!(
190 priority_header_preferred(header, handled.iter().cloned()),
191 Some(0)
192 );
193 }
194
195 #[test]
196 fn preferred_wildcard_header() {
197 let header = "text/plain; q=1.2, */*";
198 let handled = ["image/gif"];
199 assert_eq!(
200 priority_header_preferred(header, handled.iter().cloned()),
201 Some(0)
202 );
203 }
204
205 #[test]
206 fn preferred_wildcard_header_left() {
207 let header = "text/*; q=2.0, */*";
208 let handled = ["image/gif", "text/html"];
209 assert_eq!(
210 priority_header_preferred(header, handled.iter().cloned()),
211 Some(1)
212 );
213 }
214
215 #[test]
216 fn preferred_empty() {
217 let header = "*/*";
218 let handled = [];
219 assert_eq!(
220 priority_header_preferred(header, handled.iter().cloned()),
221 None
222 );
223 }
224}