net-cat 0.1.0

Minimal hand-rolled HTTP/1.1 client over std::net::TcpStream. Plain HTTP only in v0 (no TLS); used to give web-api-cat's fetch a concrete backend. No external HTTP crate; all parsing and framing are local. No mut beyond the FFI carve-out for TcpStream::read_to_end. Sixth sub-crate of a Servo-replacement webview runtime targeting Tauri.
//! HTTP headers.

/// An ordered list of name/value pairs.  Name comparisons are
/// ASCII-case-insensitive per RFC 9110.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Headers {
    items: Vec<(String, String)>,
}

impl Headers {
    /// An empty header set.
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    /// Return a copy with `name: value` appended.
    #[must_use]
    pub fn with(self, name: impl Into<String>, value: impl Into<String>) -> Self {
        let extended: Vec<(String, String)> = self
            .items
            .into_iter()
            .chain(std::iter::once((name.into(), value.into())))
            .collect();
        Self { items: extended }
    }

    /// Look up the first value for `name` (case-insensitive).
    #[must_use]
    pub fn get(&self, name: &str) -> Option<&str> {
        self.items
            .iter()
            .find(|(k, _)| k.eq_ignore_ascii_case(name))
            .map(|(_, v)| v.as_str())
    }

    /// All header pairs in insertion order.
    pub fn iter(&self) -> std::slice::Iter<'_, (String, String)> {
        self.items.iter()
    }

    /// Number of headers.
    #[must_use]
    pub fn len(&self) -> usize {
        self.items.len()
    }

    /// Whether the set is empty.
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.items.is_empty()
    }
}

impl<'a> IntoIterator for &'a Headers {
    type Item = &'a (String, String);
    type IntoIter = std::slice::Iter<'a, (String, String)>;
    fn into_iter(self) -> Self::IntoIter {
        self.items.iter()
    }
}