rama_http_headers/specifier/
quality_value.rs1#[allow(unused, deprecated)]
2use std::ascii::AsciiExt;
3use std::cmp;
4use std::default::Default;
5use std::fmt;
6use std::str;
7
8use crate::Error;
9
10use self::internal::IntoQuality;
11
12#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
27pub struct Quality(u16);
28
29impl Quality {
30 #[inline]
31 pub fn one() -> Self {
32 Self(1000)
33 }
34
35 #[inline]
36 pub fn as_u16(&self) -> u16 {
37 self.0
38 }
39}
40
41impl str::FromStr for Quality {
42 type Err = Error;
43
44 fn from_str(s: &str) -> Result<Self, Self::Err> {
46 let mut c = s.chars();
47 match c.next() {
49 Some('q' | 'Q') => (),
50 _ => return Err(Error::invalid()),
51 };
52 match c.next() {
53 Some('=') => (),
54 _ => return Err(Error::invalid()),
55 };
56
57 let mut value = match c.next() {
60 Some('0') => 0,
61 Some('1') => 1000,
62 _ => return Err(Error::invalid()),
63 };
64
65 match c.next() {
67 Some('.') => (),
68 None => return Ok(Self(value)),
69 _ => return Err(Error::invalid()),
70 };
71
72 let mut factor = 100;
76 loop {
77 match c.next() {
78 Some(n @ '0'..='9') => {
79 if factor < 1 {
82 return Err(Error::invalid());
83 }
84 value += factor * (n as u16 - '0' as u16);
86 }
87 None => {
88 return if value <= 1000 {
91 Ok(Self(value))
92 } else {
93 Err(Error::invalid())
94 };
95 }
96 _ => return Err(Error::invalid()),
97 };
98 factor /= 10;
99 }
100 }
101}
102
103impl Default for Quality {
104 fn default() -> Quality {
105 Quality(1000)
106 }
107}
108
109#[derive(Clone, PartialEq, Eq, Debug)]
112pub struct QualityValue<T> {
113 pub value: T,
115 pub quality: Quality,
117}
118
119impl<T: Copy> Copy for QualityValue<T> {}
120
121impl<T> QualityValue<T> {
122 pub const fn new(value: T, quality: Quality) -> QualityValue<T> {
124 QualityValue { value, quality }
125 }
126
127 }
141
142impl<T> From<T> for QualityValue<T> {
143 fn from(value: T) -> QualityValue<T> {
144 QualityValue {
145 value,
146 quality: Quality::default(),
147 }
148 }
149}
150
151impl<T: PartialEq> cmp::PartialOrd for QualityValue<T> {
152 fn partial_cmp(&self, other: &QualityValue<T>) -> Option<cmp::Ordering> {
153 self.quality.partial_cmp(&other.quality)
154 }
155}
156
157impl<T: fmt::Display> fmt::Display for QualityValue<T> {
158 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159 fmt::Display::fmt(&self.value, f)?;
160 match self.quality.0 {
161 1000 => Ok(()),
162 0 => f.write_str("; q=0"),
163 x => write!(f, "; q=0.{}", format!("{:03}", x).trim_end_matches('0')),
164 }
165 }
166}
167
168impl<T: str::FromStr> str::FromStr for QualityValue<T> {
169 type Err = Error;
170 fn from_str(s: &str) -> Result<QualityValue<T>, Error> {
171 let mut raw_item = s;
173 let mut quality = Quality::one();
174
175 let mut parts = s.rsplitn(2, ';').map(|x| x.trim());
176 if let (Some(first), Some(second), None) = (parts.next(), parts.next(), parts.next()) {
177 if first.len() < 2 {
178 return Err(Error::invalid());
179 }
180 if first.starts_with("q=") || first.starts_with("Q=") {
181 quality = Quality::from_str(first)?;
182 raw_item = second;
183 }
184 }
185 match raw_item.parse::<T>() {
186 Ok(item) => Ok(QualityValue::new(item, quality)),
188 Err(_) => Err(Error::invalid()),
189 }
190 }
191}
192
193#[inline]
194fn from_f32(f: f32) -> Quality {
195 debug_assert!(
199 (0f32..=1f32).contains(&f),
200 "q value must be between 0.0 and 1.0"
201 );
202 Quality((f * 1000f32) as u16)
203}
204
205#[cfg(test)]
206fn q<T: IntoQuality>(val: T) -> Quality {
207 val.into_quality()
208}
209
210impl<T> From<T> for Quality
211where
212 T: IntoQuality,
213{
214 fn from(x: T) -> Self {
215 x.into_quality()
216 }
217}
218
219mod internal {
220 use super::Quality;
221
222 pub trait IntoQuality: Sealed + Sized {
230 fn into_quality(self) -> Quality;
231 }
232
233 impl IntoQuality for f32 {
234 fn into_quality(self) -> Quality {
235 assert!(
236 (0f32..=1f32).contains(&self),
237 "float must be between 0.0 and 1.0"
238 );
239 super::from_f32(self)
240 }
241 }
242
243 impl IntoQuality for u16 {
244 fn into_quality(self) -> Quality {
245 assert!(self <= 1000, "u16 must be between 0 and 1000");
246 Quality(self)
247 }
248 }
249
250 pub trait Sealed {}
251 impl Sealed for u16 {}
252 impl Sealed for f32 {}
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 #[test]
260 fn test_quality_item_fmt_q_1() {
261 let x = QualityValue::from("foo");
262 assert_eq!(format!("{}", x), "foo");
263 }
264 #[test]
265 fn test_quality_item_fmt_q_0001() {
266 let x = QualityValue::new("foo", Quality(1));
267 assert_eq!(format!("{}", x), "foo; q=0.001");
268 }
269 #[test]
270 fn test_quality_item_fmt_q_05() {
271 let x = QualityValue::new("foo", Quality(500));
272 assert_eq!(format!("{}", x), "foo; q=0.5");
273 }
274
275 #[test]
276 fn test_quality_item_fmt_q_0() {
277 let x = QualityValue::new("foo", Quality(0));
278 assert_eq!(x.to_string(), "foo; q=0");
279 }
280
281 #[test]
282 fn test_quality_item_from_str1() {
283 let x: QualityValue<String> = "chunked".parse().unwrap();
284 assert_eq!(
285 x,
286 QualityValue {
287 value: "chunked".to_owned(),
288 quality: Quality(1000),
289 }
290 );
291 }
292 #[test]
293 fn test_quality_item_from_str2() {
294 let x: QualityValue<String> = "chunked; q=1".parse().unwrap();
295 assert_eq!(
296 x,
297 QualityValue {
298 value: "chunked".to_owned(),
299 quality: Quality(1000),
300 }
301 );
302 }
303 #[test]
304 fn test_quality_item_from_str3() {
305 let x: QualityValue<String> = "gzip; q=0.5".parse().unwrap();
306 assert_eq!(
307 x,
308 QualityValue {
309 value: "gzip".to_owned(),
310 quality: Quality(500),
311 }
312 );
313 }
314 #[test]
315 fn test_quality_item_from_str4() {
316 let x: QualityValue<String> = "gzip; q=0.273".parse().unwrap();
317 assert_eq!(
318 x,
319 QualityValue {
320 value: "gzip".to_owned(),
321 quality: Quality(273),
322 }
323 );
324 }
325 #[test]
326 fn test_quality_item_from_str5() {
327 assert!("gzip; q=0.2739999".parse::<QualityValue<String>>().is_err());
328 }
329
330 #[test]
331 fn test_quality_item_from_str6() {
332 assert!("gzip; q=2".parse::<QualityValue<String>>().is_err());
333 }
334 #[test]
335 fn test_quality_item_ordering() {
336 let x: QualityValue<String> = "gzip; q=0.5".parse().unwrap();
337 let y: QualityValue<String> = "gzip; q=0.273".parse().unwrap();
338 assert!(x > y)
339 }
340
341 #[test]
342 fn test_quality() {
343 assert_eq!(q(0.5), Quality(500));
344 }
345
346 #[test]
347 #[should_panic]
348 fn test_quality_invalid() {
349 q(-1.0);
350 }
351
352 #[test]
353 #[should_panic]
354 fn test_quality_invalid2() {
355 q(2.0);
356 }
357
358 #[test]
359 fn test_fuzzing_bugs() {
360 assert!("99999;".parse::<QualityValue<String>>().is_err());
361 assert!("\x0d;;;=\u{d6aa}==".parse::<QualityValue<String>>().is_ok())
362 }
363}