sip_header/
accept_encoding.rs1use std::fmt;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7#[non_exhaustive]
8pub struct SipAcceptEncodingEntry {
9 encoding: String,
10 params: Vec<(String, String)>,
11}
12
13impl SipAcceptEncodingEntry {
14 pub fn encoding(&self) -> &str {
16 &self.encoding
17 }
18
19 pub fn params(&self) -> &[(String, String)] {
21 &self.params
22 }
23
24 pub fn param(&self, key: &str) -> Option<&str> {
26 self.params
27 .iter()
28 .find(|(k, _)| k.eq_ignore_ascii_case(key))
29 .map(|(_, v)| v.as_str())
30 }
31
32 pub fn q(&self) -> Option<&str> {
34 self.param("q")
35 }
36}
37
38impl fmt::Display for SipAcceptEncodingEntry {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 write!(f, "{}", self.encoding)?;
41 for (key, value) in &self.params {
42 write!(f, ";{key}={value}")?;
43 }
44 Ok(())
45 }
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
50#[non_exhaustive]
51pub enum SipAcceptEncodingError {
52 Empty,
54 InvalidFormat(String),
56}
57
58impl fmt::Display for SipAcceptEncodingError {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 match self {
61 Self::Empty => write!(f, "empty Accept-Encoding header value"),
62 Self::InvalidFormat(raw) => write!(f, "invalid Accept-Encoding entry: {raw}"),
63 }
64 }
65}
66
67impl std::error::Error for SipAcceptEncodingError {}
68
69fn parse_entry(raw: &str) -> Result<SipAcceptEncodingEntry, SipAcceptEncodingError> {
70 let raw = raw.trim();
71 if raw.is_empty() {
72 return Err(SipAcceptEncodingError::InvalidFormat(raw.to_string()));
73 }
74
75 let (encoding_part, params_part) = match raw.split_once(';') {
76 Some((e, p)) => (e.trim(), Some(p)),
77 None => (raw, None),
78 };
79
80 if encoding_part.is_empty() {
81 return Err(SipAcceptEncodingError::InvalidFormat(raw.to_string()));
82 }
83
84 let encoding = encoding_part.to_ascii_lowercase();
85 let mut params = Vec::new();
86
87 if let Some(params_str) = params_part {
88 for segment in params_str.split(';') {
89 let segment = segment.trim();
90 if segment.is_empty() {
91 continue;
92 }
93 if let Some((key, value)) = segment.split_once('=') {
94 params.push((
95 key.trim()
96 .to_ascii_lowercase(),
97 value
98 .trim()
99 .to_string(),
100 ));
101 } else {
102 params.push((segment.to_ascii_lowercase(), String::new()));
103 }
104 }
105 }
106
107 Ok(SipAcceptEncodingEntry { encoding, params })
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
112#[non_exhaustive]
113pub struct SipAcceptEncoding(Vec<SipAcceptEncodingEntry>);
114
115impl SipAcceptEncoding {
116 pub fn parse(raw: &str) -> Result<Self, SipAcceptEncodingError> {
118 let raw = raw.trim();
119 if raw.is_empty() {
120 return Err(SipAcceptEncodingError::Empty);
121 }
122 let entries: Vec<_> = crate::split_comma_entries(raw)
123 .into_iter()
124 .map(parse_entry)
125 .collect::<Result<_, _>>()?;
126 if entries.is_empty() {
127 return Err(SipAcceptEncodingError::Empty);
128 }
129 Ok(Self(entries))
130 }
131
132 pub fn entries(&self) -> &[SipAcceptEncodingEntry] {
134 &self.0
135 }
136
137 pub fn into_entries(self) -> Vec<SipAcceptEncodingEntry> {
139 self.0
140 }
141
142 pub fn len(&self) -> usize {
144 self.0
145 .len()
146 }
147
148 pub fn is_empty(&self) -> bool {
150 self.0
151 .is_empty()
152 }
153}
154
155impl fmt::Display for SipAcceptEncoding {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 crate::fmt_joined(f, &self.0, ", ")
158 }
159}
160
161impl_from_str_via_parse!(SipAcceptEncoding, SipAcceptEncodingError);
162
163impl<'a> IntoIterator for &'a SipAcceptEncoding {
164 type Item = &'a SipAcceptEncodingEntry;
165 type IntoIter = std::slice::Iter<'a, SipAcceptEncodingEntry>;
166
167 fn into_iter(self) -> Self::IntoIter {
168 self.0
169 .iter()
170 }
171}
172
173impl IntoIterator for SipAcceptEncoding {
174 type Item = SipAcceptEncodingEntry;
175 type IntoIter = std::vec::IntoIter<SipAcceptEncodingEntry>;
176
177 fn into_iter(self) -> Self::IntoIter {
178 self.0
179 .into_iter()
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186
187 #[test]
188 fn single_encoding() {
189 let ae = SipAcceptEncoding::parse("gzip").unwrap();
190 assert_eq!(ae.len(), 1);
191 assert_eq!(ae.entries()[0].encoding(), "gzip");
192 }
193
194 #[test]
195 fn multiple_encodings_with_q() {
196 let ae = SipAcceptEncoding::parse("gzip;q=1.0, identity;q=0.5").unwrap();
197 assert_eq!(ae.len(), 2);
198 assert_eq!(ae.entries()[0].q(), Some("1.0"));
199 assert_eq!(ae.entries()[1].encoding(), "identity");
200 }
201
202 #[test]
203 fn wildcard() {
204 let ae = SipAcceptEncoding::parse("*").unwrap();
205 assert_eq!(ae.entries()[0].encoding(), "*");
206 }
207
208 #[test]
209 fn empty_input() {
210 assert!(matches!(
211 SipAcceptEncoding::parse(""),
212 Err(SipAcceptEncodingError::Empty)
213 ));
214 }
215
216 #[test]
217 fn from_str() {
218 let ae: SipAcceptEncoding = "gzip"
219 .parse()
220 .unwrap();
221 assert_eq!(ae.len(), 1);
222 }
223
224 #[test]
225 fn display_roundtrip() {
226 let raw = "gzip;q=0.8";
227 let ae = SipAcceptEncoding::parse(raw).unwrap();
228 assert_eq!(ae.to_string(), raw);
229 }
230}