1use std::borrow::Cow;
2use std::fmt::{self, Debug};
3
4#[derive(Clone)]
6pub struct Map<'a> {
7 inner: Vec<(Cow<'a, str>, Cow<'a, str>)>,
8}
9
10impl<'a> Map<'a> {
11 #[inline]
13 #[must_use]
14 pub const fn new() -> Self {
15 Self { inner: Vec::new() }
16 }
17
18 #[inline]
20 #[must_use]
21 pub fn len(&self) -> usize {
22 self.inner.len()
23 }
24
25 #[inline]
27 #[must_use]
28 pub fn is_empty(&self) -> bool {
29 self.inner.is_empty()
30 }
31
32 #[must_use]
34 pub fn get(&self, key: &str) -> Option<&str> {
35 self.inner
36 .binary_search_by(|a| a.0.as_ref().cmp(key))
37 .map_or(None, |i| self.inner.get(i).map(|kv| kv.1.as_ref()))
38 }
39
40 pub fn insert<K, V>(&mut self, key: K, value: V)
56 where
57 K: Into<Cow<'a, str>>,
58 V: Into<Cow<'a, str>>,
59 {
60 let key = key.into();
61 let value = value.into();
62
63 let i = self.inner.binary_search_by(|a| a.0.cmp(&key));
64 match i {
65 Ok(i) => {
66 let old_value = self.inner.get_mut(i).expect("i can't be out of bounds");
67 *old_value = (key, value);
68 }
69 Err(i) => self.inner.insert(i, (key, value)),
70 }
71 }
72
73 pub fn append<K, V>(&mut self, key: K, value: V)
89 where
90 K: Into<Cow<'a, str>>,
91 V: Into<Cow<'a, str>>,
92 {
93 let key = key.into();
94 let value = value.into();
95
96 let i = self.inner.binary_search_by(|a| a.0.cmp(&key));
97 match i {
98 Ok(i) => {
99 let old_value = self.inner.get_mut(i).expect("i can't be out of bounds");
100 let new_value = Cow::Owned(format!("{}, {}", old_value.1, value));
101 *old_value = (key, new_value);
102 }
103 Err(i) => self.inner.insert(i, (key, value)),
104 }
105 }
106
107 pub fn remove(&mut self, key: &str) -> Option<(Cow<'a, str>, Cow<'a, str>)> {
109 match self.inner.binary_search_by(|a| a.0.as_ref().cmp(key)) {
110 Ok(i) => Some(self.inner.remove(i)),
111 Err(_) => None,
112 }
113 }
114
115 pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> + Clone {
119 self.inner.iter().map(|t| (t.0.as_ref(), t.1.as_ref()))
120 }
121}
122
123impl Debug for Map<'_> {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 f.debug_map().entries(self.iter()).finish()
126 }
127}
128
129impl Default for Map<'_> {
130 #[inline]
131 fn default() -> Self {
132 Self::new()
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use pretty_assertions::assert_eq;
139
140 use super::*;
141
142 #[test]
143 fn map() {
144 let mut map = Map::new();
145 {
146 assert_eq!(map.len(), 0);
147 assert!(map.is_empty());
148 assert!(map.get("nothing").is_none());
149
150 let mut iter = map.iter();
151 assert!(iter.next().is_none());
152 }
153
154 {
155 map.insert("content-type", "text/plain");
156 assert_eq!(map.len(), 1);
157 assert!(!map.is_empty());
158 assert!(map.get("nothing").is_none());
159 assert_eq!(map.get("content-type"), Some("text/plain"));
160
161 let iter = map.iter();
162 iter.eq(vec![("content-type", "text/plain")]);
163 }
164
165 {
166 map.insert("cache-control", "public, max-age=86400");
167 assert_eq!(map.len(), 2);
168 assert!(!map.is_empty());
169 assert!(map.get("nothing").is_none());
170 assert_eq!(map.get("content-type"), Some("text/plain"));
171 assert_eq!(map.get("cache-control"), Some("public, max-age=86400"));
172
173 let iter = map.iter();
174 iter.eq(vec![
175 ("cache-control", "public, max-age=86400"),
176 ("content-type", "text/plain"),
177 ]);
178 }
179
180 {
181 map.insert("x-amz-storage-class", "standard");
182 assert_eq!(map.len(), 3);
183 assert!(!map.is_empty());
184 assert!(map.get("nothing").is_none());
185 assert_eq!(map.get("content-type"), Some("text/plain"));
186 assert_eq!(map.get("cache-control"), Some("public, max-age=86400"));
187 assert_eq!(map.get("x-amz-storage-class"), Some("standard"));
188
189 let iter = map.iter();
190 iter.eq(vec![
191 ("cache-control", "public, max-age=86400"),
192 ("content-type", "text/plain"),
193 ("x-amz-storage-class", "standard"),
194 ]);
195 }
196
197 {
198 map.remove("content-type");
199 assert_eq!(map.len(), 2);
200 assert!(!map.is_empty());
201 assert!(map.get("nothing").is_none());
202 assert_eq!(map.get("cache-control"), Some("public, max-age=86400"));
203 assert_eq!(map.get("x-amz-storage-class"), Some("standard"));
204
205 let iter = map.iter();
206 iter.eq(vec![
207 ("cache-control", "public, max-age=86400"),
208 ("x-amz-storage-class", "standard"),
209 ]);
210 }
211
212 {
213 map.remove("x-amz-look-at-how-many-headers-you-have");
214 assert_eq!(map.len(), 2);
215 assert!(!map.is_empty());
216 assert!(map.get("nothing").is_none());
217 assert_eq!(map.get("cache-control"), Some("public, max-age=86400"));
218 assert_eq!(map.get("x-amz-storage-class"), Some("standard"));
219
220 let iter = map.iter();
221 iter.eq(vec![
222 ("cache-control", "public, max-age=86400"),
223 ("x-amz-storage-class", "standard"),
224 ]);
225 }
226
227 {
228 map.append("cache-control", "immutable");
229 assert_eq!(map.len(), 2);
230 assert!(!map.is_empty());
231 assert!(map.get("nothing").is_none());
232 assert_eq!(
233 map.get("cache-control"),
234 Some("public, max-age=86400, immutable")
235 );
236 assert_eq!(map.get("x-amz-storage-class"), Some("standard"));
237
238 let iter = map.iter();
239 iter.eq(vec![
240 ("cache-control", "public, max-age=86400, immutable"),
241 ("x-amz-storage-class", "standard"),
242 ]);
243 }
244 }
245}