1use std::collections::HashMap;
7
8#[derive(Debug, Clone, Default)]
10pub struct FetchHeaders {
11 headers: HashMap<String, String>,
12}
13
14impl FetchHeaders {
15 pub fn new() -> Self {
16 Self::default()
17 }
18
19 pub fn from_iter<I: Iterator<Item = (String, String)>>(iter: I) -> Self {
20 let mut headers = HashMap::new();
21 for (key, value) in iter {
22 headers.insert(key.to_ascii_lowercase(), value);
23 }
24 Self { headers }
25 }
26
27 pub fn get(&self, name: &str) -> Option<&str> {
28 self.headers
29 .get(&name.to_ascii_lowercase())
30 .map(|s| s.as_str())
31 }
32
33 pub fn set(&mut self, name: &str, value: String) {
34 self.headers.insert(name.to_ascii_lowercase(), value);
35 }
36
37 pub fn has(&self, name: &str) -> bool {
38 self.headers.contains_key(&name.to_ascii_lowercase())
39 }
40
41 pub fn delete(&mut self, name: &str) -> bool {
42 self.headers.remove(&name.to_ascii_lowercase()).is_some()
43 }
44
45 pub fn len(&self) -> usize {
46 self.headers.len()
47 }
48
49 pub fn is_empty(&self) -> bool {
50 self.headers.is_empty()
51 }
52
53 pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
54 self.headers.iter().map(|(k, v)| (k.as_str(), v.as_str()))
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn fetch_headers_case_insensitive() {
64 let mut h = FetchHeaders::new();
65 h.set("Content-Type", "text/html".into());
66 assert_eq!(h.get("content-type"), Some("text/html"));
67 assert_eq!(h.get("CONTENT-TYPE"), Some("text/html"));
68 }
69
70 #[test]
71 fn fetch_headers_delete() {
72 let mut h = FetchHeaders::new();
73 h.set("X-Custom", "value".into());
74 assert!(h.delete("x-custom"));
75 assert!(!h.has("X-Custom"));
76 }
77
78 #[test]
79 fn fetch_headers_from_iter() {
80 let h = FetchHeaders::from_iter(vec![("Name".into(), "val".into())].into_iter());
81 assert_eq!(h.get("name"), Some("val"));
82 }
83}