1use http::header::{HeaderMap, ACCEPT_ENCODING};
2
3use super::error::FeatureError;
4
5#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
7pub enum ContentEncoding {
8 Br,
10 Deflate,
12 Gzip,
14 #[default]
16 NoOp,
17}
18
19impl ContentEncoding {
20 pub fn from_headers(headers: &HeaderMap) -> Self {
21 let mut prefer = ContentEncodingWithQValue::default();
22
23 for encoding in Self::_from_headers(headers) {
24 prefer.try_update(encoding);
25 }
26
27 prefer.enc
28 }
29
30 fn _from_headers(headers: &HeaderMap) -> impl Iterator<Item = ContentEncodingWithQValue> + '_ {
31 headers
32 .get_all(ACCEPT_ENCODING)
33 .iter()
34 .filter_map(|hval| hval.to_str().ok())
35 .flat_map(|s| s.split(','))
36 .filter_map(|v| {
37 let mut v = v.splitn(2, ';');
38 Self::try_parse(v.next().unwrap().trim()).ok().map(|enc| {
39 let val = v
40 .next()
41 .and_then(|v| QValue::parse(v.trim()))
42 .unwrap_or_else(QValue::one);
43 ContentEncodingWithQValue { enc, val }
44 })
45 })
46 }
47
48 pub(super) fn try_parse(s: &str) -> Result<Self, FeatureError> {
49 if s.eq_ignore_ascii_case("gzip") {
50 Ok(Self::Gzip)
51 } else if s.eq_ignore_ascii_case("deflate") {
52 Ok(Self::Deflate)
53 } else if s.eq_ignore_ascii_case("br") {
54 Ok(Self::Br)
55 } else if s.eq_ignore_ascii_case("identity") {
56 Ok(Self::NoOp)
57 } else {
58 Err(FeatureError::Unknown(s.to_string().into_boxed_str()))
59 }
60 }
61}
62
63struct ContentEncodingWithQValue {
64 enc: ContentEncoding,
65 val: QValue,
66}
67
68impl Default for ContentEncodingWithQValue {
69 fn default() -> Self {
70 Self {
71 enc: ContentEncoding::NoOp,
72 val: QValue::zero(),
73 }
74 }
75}
76
77impl ContentEncodingWithQValue {
78 fn try_update(&mut self, other: Self) {
79 if other.val > self.val {
80 match other.enc {
81 #[cfg(not(feature = "br"))]
82 ContentEncoding::Br => return,
83 #[cfg(not(feature = "de"))]
84 ContentEncoding::Deflate => return,
85 #[cfg(not(feature = "gz"))]
86 ContentEncoding::Gzip => return,
87 _ => {}
88 };
89 *self = other;
90 }
91 }
92}
93
94#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
95struct QValue(u16);
96
97impl QValue {
98 const fn zero() -> Self {
99 Self(0)
100 }
101
102 const fn one() -> Self {
103 Self(1000)
104 }
105
106 fn parse(s: &str) -> Option<Self> {
108 let mut c = s.chars();
109 match c.next() {
111 Some('q') | Some('Q') => (),
112 _ => return None,
113 };
114 match c.next() {
115 Some('=') => (),
116 _ => return None,
117 };
118
119 let mut value = match c.next() {
122 Some('0') => 0,
123 Some('1') => 1000,
124 _ => return None,
125 };
126
127 match c.next() {
129 Some('.') => (),
130 None => return Some(Self(value)),
131 _ => return None,
132 };
133
134 let mut factor = 100;
138 loop {
139 match c.next() {
140 Some(n @ '0'..='9') => {
141 if factor < 1 {
144 return None;
145 }
146 value += factor * (n as u16 - '0' as u16);
148 }
149 None => {
150 return if value <= 1000 { Some(Self(value)) } else { None };
153 }
154 _ => return None,
155 };
156 factor /= 10;
157 }
158 }
159}