use crate::{http::Request, MatchType::ToHave, Matcher, TypedMatcher};
use http::{HeaderMap, HeaderName};
pub trait AsHeaderName {
fn into_header_name(self) -> HeaderName;
}
impl AsHeaderName for HeaderName {
fn into_header_name(self) -> HeaderName {
self
}
}
impl AsHeaderName for String {
fn into_header_name(self) -> HeaderName {
let upper = self.to_uppercase();
match upper.parse() {
Ok(header_name) => header_name,
Err(_) => panic!("Invalid header name"),
}
}
}
impl AsHeaderName for &str {
fn into_header_name(self) -> HeaderName {
self.to_string()
.into_header_name()
}
}
pub fn header<H>(value: H) -> Header
where
H: AsHeaderName,
{
Header(value.into_header_name())
}
pub struct Header(http::header::HeaderName);
impl Matcher<Request> for Header {
fn matches(&self, value: &Request) -> bool {
value
.headers()
.contains_key(&self.0)
}
fn description(&self) -> String {
format!("header matching {}", self.0)
}
}
impl TypedMatcher<Request> for Header {
fn matcher_type(&self) -> crate::MatchType {
ToHave
}
}
impl Matcher<HeaderMap> for Header {
fn matches(&self, value: &HeaderMap) -> bool {
value.contains_key(&self.0)
}
fn description(&self) -> String {
format!("header matching {}", self.0)
}
}
impl TypedMatcher<HeaderMap> for Header {
fn matcher_type(&self) -> crate::MatchType {
ToHave
}
}
pub fn header_value<N>(name: N, value: &str) -> HeaderValue
where
N: AsHeaderName,
{
let regex = regex::Regex::new(value);
match regex {
Ok(regex) => HeaderValue { name: name.into_header_name(), regex },
Err(_) => panic!("Invalid regex pattern"),
}
}
pub struct HeaderValue {
name: HeaderName,
regex: regex::Regex,
}
impl Matcher<Request> for HeaderValue {
fn matches(&self, value: &Request) -> bool {
value
.headers()
.get(&self.name)
.is_some_and(|v| {
self.regex.is_match(
v.to_str()
.unwrap_or(""),
)
})
}
fn description(&self) -> String {
format!("header {} with value matching {:?}", self.name, self.regex)
}
}
impl TypedMatcher<Request> for HeaderValue {
fn matcher_type(&self) -> crate::MatchType {
ToHave
}
}
impl Matcher<HeaderMap> for HeaderValue {
fn matches(&self, value: &HeaderMap) -> bool {
self.regex.is_match(
value
.get(&self.name)
.and_then(|v| v.to_str().ok())
.expect("Header value is not valid UTF-8"),
)
}
fn description(&self) -> String {
format!("header {} with value matching {:?}", self.name, self.regex)
}
}
impl TypedMatcher<HeaderMap> for HeaderValue {
fn matcher_type(&self) -> crate::MatchType {
ToHave
}
}