typed_headers/impls/
quality.rs1use std::fmt;
2use std::slice;
3use std::str::{self, FromStr};
4
5#[derive(Debug, Clone, PartialEq)]
11pub struct QualityItem<T> {
12 pub item: T,
13 pub quality: Quality,
14}
15
16impl<T> QualityItem<T> {
17 pub fn new(item: T, quality: Quality) -> QualityItem<T> {
19 QualityItem { item, quality }
20 }
21}
22
23impl<T> fmt::Display for QualityItem<T>
24where
25 T: fmt::Display,
26{
27 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
28 fmt::Display::fmt(&self.item, fmt)?;
29 match self.quality.0 {
30 1000 => Ok(()),
31 0 => fmt.write_str("; q=0"),
32 mut x => {
33 fmt.write_str("; q=0.")?;
34 let mut digits = *b"000";
35 digits[2] = (x % 10) as u8 + b'0';
36 x /= 10;
37 digits[1] = (x % 10) as u8 + b'0';
38 x /= 10;
39 digits[0] = (x % 10) as u8 + b'0';
40
41 let s = unsafe { str::from_utf8_unchecked(&digits[..]) };
42 fmt.write_str(s.trim_end_matches('0'))
43 }
44 }
45 }
46}
47
48impl<T> FromStr for QualityItem<T>
49where
50 T: FromStr,
51{
52 type Err = T::Err;
53
54 fn from_str(mut s: &str) -> Result<QualityItem<T>, T::Err> {
55 let quality = match WeightParser::parse(s) {
56 Some((remaining, quality)) => {
57 s = &s[..remaining];
58 quality
59 }
60 None => Quality(1000),
61 };
62
63 let item = s.parse()?;
64
65 Ok(QualityItem { item, quality })
66 }
67}
68
69struct WeightParser<'a>(slice::Iter<'a, u8>);
70
71impl<'a> WeightParser<'a> {
72 fn parse(s: &'a str) -> Option<(usize, Quality)> {
73 let mut parser = WeightParser(s.as_bytes().iter());
74 let qvalue = parser.qvalue()?;
75 parser.eat(b'=')?;
76 parser.eat(b'q').or_else(|| parser.eat(b'Q'))?;
77 parser.ows();
78 parser.eat(b';')?;
79 parser.ows();
80 let remaining = parser.0.as_slice().len();
81 Some((remaining, Quality(qvalue)))
82 }
83
84 fn qvalue(&mut self) -> Option<u16> {
85 let mut qvalue = match self.digit() {
86 Some(v @ 0) | Some(v @ 1) if self.peek() == Some(b'=') => return Some(v * 1000),
87 Some(v) => v,
88 None if self.peek() == Some(b'.') => 0,
89 None => return None,
90 };
91
92 match self.digit() {
93 Some(digit1) => match self.digit() {
94 Some(digit2) => qvalue += digit1 * 10 + digit2 * 100,
95 None => {
96 qvalue *= 10;
97 qvalue += digit1 * 100;
98 }
99 },
100 None => qvalue *= 100,
101 }
102
103 self.eat(b'.')?;
104
105 match self.peek()? {
106 b'0' => {
107 self.next();
108 Some(qvalue)
109 }
110 b'1' if qvalue == 0 => {
111 self.next();
112 Some(1000)
113 }
114 _ => None,
115 }
116 }
117
118 fn digit(&mut self) -> Option<u16> {
119 match self.peek()? {
120 v @ b'0'..=b'9' => {
121 self.next();
122 Some((v - b'0') as u16)
123 }
124 _ => None,
125 }
126 }
127
128 fn ows(&mut self) {
129 loop {
130 match self.peek() {
131 Some(b' ') | Some(b'\t') => {
132 self.next();
133 }
134 _ => break,
135 }
136 }
137 }
138
139 fn peek(&self) -> Option<u8> {
140 self.0.clone().next_back().cloned()
141 }
142
143 fn next(&mut self) -> Option<u8> {
144 self.0.next_back().cloned()
145 }
146
147 fn eat(&mut self, value: u8) -> Option<()> {
148 if self.peek() == Some(value) {
149 self.next();
150 Some(())
151 } else {
152 None
153 }
154 }
155}
156
157#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
163pub struct Quality(u16);
164
165impl Quality {
166 pub fn from_u16(quality: u16) -> Quality {
174 assert!(quality <= 1000);
175 Quality(quality)
176 }
177
178 pub fn as_u16(&self) -> u16 {
180 self.0
181 }
182}
183
184#[cfg(test)]
185mod test {
186 use super::*;
187 use crate::Error;
188
189 #[derive(Debug, Clone, PartialEq)]
190 struct Item;
191
192 impl fmt::Display for Item {
193 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
194 fmt.write_str("item")
195 }
196 }
197
198 impl FromStr for Item {
199 type Err = Error;
200
201 fn from_str(s: &str) -> Result<Item, Error> {
202 if s == "item" {
203 Ok(Item)
204 } else {
205 Err(Error::invalid_value())
206 }
207 }
208 }
209
210 fn qitem(quality: u16) -> QualityItem<Item> {
211 QualityItem {
212 item: Item,
213 quality: Quality(quality),
214 }
215 }
216
217 #[test]
218 fn parse_ok() {
219 assert_eq!(qitem(1000), "item".parse().unwrap());
220 assert_eq!(qitem(1000), "item; q=1".parse().unwrap());
221 assert_eq!(qitem(1000), "item; Q=1".parse().unwrap());
222 assert_eq!(qitem(1000), "item ;q=1".parse().unwrap());
223 assert_eq!(qitem(1000), "item; q=1.".parse().unwrap());
224 assert_eq!(qitem(1000), "item; q=1.0".parse().unwrap());
225 assert_eq!(qitem(1000), "item; q=1.00".parse().unwrap());
226 assert_eq!(qitem(1000), "item; q=1.000".parse().unwrap());
227
228 assert_eq!(qitem(0), "item; q=0".parse().unwrap());
229 assert_eq!(qitem(0), "item; q=0.".parse().unwrap());
230 assert_eq!(qitem(0), "item; q=0.0".parse().unwrap());
231 assert_eq!(qitem(0), "item; q=0.00".parse().unwrap());
232 assert_eq!(qitem(0), "item; q=0.000".parse().unwrap());
233
234 assert_eq!(qitem(100), "item; q=0.1".parse().unwrap());
235 assert_eq!(qitem(100), "item; q=0.10".parse().unwrap());
236 assert_eq!(qitem(100), "item; q=0.100".parse().unwrap());
237 assert_eq!(qitem(120), "item; q=0.12".parse().unwrap());
238 assert_eq!(qitem(120), "item; q=0.120".parse().unwrap());
239 assert_eq!(qitem(123), "item; q=0.123".parse().unwrap());
240 }
241
242 #[test]
243 fn parse_err() {
244 assert!("item; q=".parse::<QualityItem<Item>>().is_err());
245 assert!("item; q=.1".parse::<QualityItem<Item>>().is_err());
246 assert!("item; q=1.1".parse::<QualityItem<Item>>().is_err());
247 assert!("item; q=1.01".parse::<QualityItem<Item>>().is_err());
248 assert!("item; q=1.001".parse::<QualityItem<Item>>().is_err());
249 assert!("item; q=0.0001".parse::<QualityItem<Item>>().is_err());
250 }
251
252 #[test]
253 fn display() {
254 assert_eq!(qitem(1000).to_string(), "item");
255 assert_eq!(qitem(0).to_string(), "item; q=0");
256 assert_eq!(qitem(1).to_string(), "item; q=0.001");
257 assert_eq!(qitem(10).to_string(), "item; q=0.01");
258 assert_eq!(qitem(100).to_string(), "item; q=0.1");
259 }
260}