1use bytes::Bytes;
8
9use crate::escape;
10
11#[derive(Debug, Clone)]
13pub struct Param {
14 name: Bytes,
15 value: Option<Bytes>,
16}
17
18impl Param {
19 #[must_use]
21 pub fn flag(name: impl Into<Bytes>) -> Self {
22 Self {
23 name: name.into(),
24 value: None,
25 }
26 }
27
28 #[must_use]
30 pub fn new(name: impl Into<Bytes>, value: impl Into<Bytes>) -> Self {
31 Self {
32 name: name.into(),
33 value: Some(value.into()),
34 }
35 }
36
37 #[must_use]
39 pub fn name(&self) -> &[u8] {
40 &self.name
41 }
42
43 #[must_use]
45 pub fn value(&self) -> Option<&[u8]> {
46 self.value.as_deref()
47 }
48
49 #[must_use]
55 pub fn has_name(&self, name: &str) -> bool {
56 names_equivalent(&self.name, name.as_bytes())
57 }
58}
59
60#[must_use]
63pub(crate) fn names_equivalent(a: &[u8], b: &[u8]) -> bool {
64 escape::eq_ignore_ascii_case(
65 &escape::normalize_for_comparison(a),
66 &escape::normalize_for_comparison(b),
67 )
68}
69
70#[derive(Debug, Clone, Default)]
72pub struct Params {
73 entries: Vec<Param>,
74}
75
76impl Params {
77 #[must_use]
79 pub fn new() -> Self {
80 Self::default()
81 }
82
83 #[must_use]
85 pub fn len(&self) -> usize {
86 self.entries.len()
87 }
88
89 #[must_use]
91 pub fn is_empty(&self) -> bool {
92 self.entries.is_empty()
93 }
94
95 pub fn push(&mut self, param: Param) {
97 self.entries.push(param);
98 }
99
100 pub fn remove(&mut self, name: &str) -> bool {
106 let before = self.entries.len();
107 self.entries.retain(|p| !p.has_name(name));
108 self.entries.len() != before
109 }
110
111 pub fn iter(&self) -> impl Iterator<Item = &Param> {
113 self.entries.iter()
114 }
115
116 #[must_use]
118 pub fn get(&self, name: &str) -> Option<&Param> {
119 self.entries.iter().find(|p| p.has_name(name))
120 }
121
122 #[must_use]
128 pub fn value(&self, name: &str) -> Option<&[u8]> {
129 self.get(name).and_then(Param::value)
130 }
131
132 #[must_use]
134 pub fn contains(&self, name: &str) -> bool {
135 self.get(name).is_some()
136 }
137
138 #[must_use]
142 fn values_equivalent(a: Option<&[u8]>, b: Option<&[u8]>) -> bool {
143 match (a, b) {
144 (None, None) => true,
145 (Some(x), Some(y)) => escape::eq_ignore_ascii_case(
146 &escape::normalize_for_comparison(x),
147 &escape::normalize_for_comparison(y),
148 ),
149 _ => false,
150 }
151 }
152
153 #[must_use]
155 pub(crate) fn param_equivalent(&self, other: &Self, name: &str) -> bool {
156 match (self.get(name), other.get(name)) {
157 (None, None) => true,
158 (Some(x), Some(y)) => Self::values_equivalent(x.value(), y.value()),
159 _ => false,
160 }
161 }
162
163 #[must_use]
169 pub(crate) fn common_params_agree(&self, other: &Self) -> bool {
170 self.entries.iter().all(|p| {
171 other
172 .entries
173 .iter()
174 .filter(|q| names_equivalent(q.name(), p.name()))
175 .all(|q| Self::values_equivalent(p.value(), q.value()))
176 })
177 }
178
179 pub fn write_to(&self, out: &mut Vec<u8>, separator: u8) {
181 for (i, p) in self.entries.iter().enumerate() {
182 out.push(if i == 0 && separator == b'?' {
183 b'?'
184 } else if separator == b'?' {
185 b'&'
186 } else {
187 separator
188 });
189 out.extend_from_slice(&p.name);
190 if let Some(v) = &p.value {
191 out.push(b'=');
192 out.extend_from_slice(v);
193 }
194 }
195 }
196}
197
198impl<'a> IntoIterator for &'a Params {
199 type Item = &'a Param;
200 type IntoIter = std::slice::Iter<'a, Param>;
201
202 fn into_iter(self) -> Self::IntoIter {
203 self.entries.iter()
204 }
205}
206
207impl FromIterator<Param> for Params {
208 fn from_iter<T: IntoIterator<Item = Param>>(iter: T) -> Self {
209 Self {
210 entries: iter.into_iter().collect(),
211 }
212 }
213}
214
215#[cfg(test)]
216#[allow(
217 clippy::unwrap_used,
218 clippy::expect_used,
219 clippy::panic,
220 clippy::indexing_slicing
221)]
222mod tests {
223 use super::*;
224
225 fn params(pairs: &[(&str, Option<&str>)]) -> Params {
226 pairs
227 .iter()
228 .map(|(n, v)| match v {
229 Some(v) => Param::new(Bytes::from((*n).to_owned()), Bytes::from((*v).to_owned())),
230 None => Param::flag(Bytes::from((*n).to_owned())),
231 })
232 .collect()
233 }
234
235 #[test]
236 fn preserves_order_and_duplicates() {
237 let p = params(&[("b", Some("2")), ("a", Some("1")), ("b", Some("3"))]);
238 assert_eq!(p.len(), 3);
239 let names: Vec<_> = p.iter().map(|x| x.name().to_vec()).collect();
240 assert_eq!(names, vec![b"b".to_vec(), b"a".to_vec(), b"b".to_vec()]);
241 assert_eq!(p.value("b"), Some(&b"2"[..]));
243 }
244
245 #[test]
249 fn remove_takes_every_copy_and_says_whether_there_was_one() {
250 let mut p = params(&[("b", Some("2")), ("a", Some("1")), ("b", Some("3"))]);
251 assert!(p.remove("b"));
252 assert_eq!(p.len(), 1);
253 assert_eq!(p.value("b"), None);
254 assert_eq!(p.value("a"), Some(&b"1"[..]));
255 assert!(!p.remove("b"));
257 assert_eq!(p.len(), 1);
258 let mut flags = params(&[("lr", None), ("Transport", Some("tcp"))]);
261 assert!(flags.remove("LR"));
262 assert!(flags.remove("transport"));
263 assert!(flags.is_empty());
264 }
265
266 #[test]
267 fn flag_is_not_the_same_as_absent() {
268 let p = params(&[("lr", None)]);
269 assert!(p.contains("lr"));
270 assert_eq!(p.value("lr"), None);
271 assert!(!p.contains("nope"));
272 }
273
274 #[test]
275 fn names_and_values_compare_case_insensitively() {
276 let a = params(&[("Transport", Some("TCP"))]);
277 let b = params(&[("transport", Some("tcp"))]);
278 assert!(a.param_equivalent(&b, "transport"));
279 }
280
281 #[test]
284 fn escaped_names_fold_when_looked_up() {
285 let p = params(&[("%74ransport", Some("udp"))]);
286 assert!(p.get("transport").is_some());
287 assert_eq!(p.value("transport"), Some(&b"udp"[..]));
288
289 let plain = params(&[("transport", Some("udp"))]);
290 assert!(p.param_equivalent(&plain, "transport"));
291 assert!(p.common_params_agree(&plain) && plain.common_params_agree(&p));
292
293 let other = params(&[("transport", Some("tcp"))]);
294 assert!(!p.common_params_agree(&other));
295 }
296
297 #[test]
298 fn writes_uri_and_header_separators() {
299 let p = params(&[("a", Some("1")), ("b", None)]);
300 let mut out = Vec::new();
301 p.write_to(&mut out, b';');
302 assert_eq!(out, b";a=1;b");
303
304 let mut out = Vec::new();
305 p.write_to(&mut out, b'?');
306 assert_eq!(out, b"?a=1&b");
307 }
308}