use std::fmt;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Headers {
entries: Vec<(String, String)>,
}
impl Headers {
pub fn new() -> Self {
Self::default()
}
pub fn from_pairs<I, N, V>(pairs: I) -> Self
where
I: IntoIterator<Item = (N, V)>,
N: Into<String>,
V: Into<String>,
{
let mut headers = Self::new();
for (name, value) in pairs {
headers.insert(name, value);
}
headers
}
pub fn get(&self, name: &str) -> Option<&str> {
self.entries
.iter()
.find(|(existing, _)| existing.eq_ignore_ascii_case(name))
.map(|(_, value)| value.as_str())
}
pub fn contains(&self, name: &str) -> bool {
self.get(name).is_some()
}
pub fn insert<N: Into<String>, V: Into<String>>(&mut self, name: N, value: V) {
let name = name.into();
let value = value.into();
match self
.entries
.iter_mut()
.find(|(existing, _)| existing.eq_ignore_ascii_case(&name))
{
Some(entry) => entry.1 = value,
None => self.entries.push((name, value)),
}
}
pub fn insert_missing<N: Into<String>, V: Into<String>>(&mut self, name: N, value: V) {
let name = name.into();
if !self.contains(&name) {
self.insert(name, value);
}
}
pub fn remove(&mut self, name: &str) -> Option<String> {
let index = self
.entries
.iter()
.position(|(existing, _)| existing.eq_ignore_ascii_case(name))?;
Some(self.entries.remove(index).1)
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
self.entries
.iter()
.map(|(name, value)| (name.as_str(), value.as_str()))
}
pub fn extend_missing(&mut self, other: &Headers) {
for (name, value) in other.iter() {
self.insert_missing(name.to_string(), value.to_string());
}
}
}
impl<'a> IntoIterator for &'a Headers {
type Item = (&'a str, &'a str);
type IntoIter = Box<dyn Iterator<Item = (&'a str, &'a str)> + 'a>;
fn into_iter(self) -> Self::IntoIter {
Box::new(self.iter())
}
}
impl fmt::Display for Headers {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
for (name, value) in self.iter() {
writeln!(formatter, "{name}: {value}")?;
}
Ok(())
}
}
pub fn append_list_value(headers: &mut Headers, name: &str, value: &str) {
match headers.get(name) {
None => headers.insert(name.to_string(), value.to_string()),
Some(existing) => {
let already = existing
.split(',')
.any(|part| part.trim().eq_ignore_ascii_case(value));
if !already {
let merged = format!("{existing}, {value}");
headers.insert(name.to_string(), merged);
}
}
}
}