use caramelo::{MatchType::ToHave, Matcher, TypedMatcher};
use http::HeaderName;
use crate::mock::Request;
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())
}
#[derive(Clone)]
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) -> caramelo::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"),
}
}
#[derive(Clone)]
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) -> caramelo::MatchType {
ToHave
}
}
pub fn jwt(token: &str) -> Jwt {
Jwt { token: token.to_string() }
}
#[derive(Clone)]
pub struct Jwt {
token: String,
}
impl Matcher<Request> for Jwt {
fn matches(&self, value: &Request) -> bool {
value
.headers()
.get("Authorization")
.is_some_and(|v| {
v.to_str()
.unwrap_or("")
.starts_with("Bearer ")
})
}
fn description(&self) -> String {
format!("JWT token matching {}", self.token)
}
}
impl TypedMatcher<Request> for Jwt {
fn matcher_type(&self) -> caramelo::MatchType {
ToHave
}
}
pub fn basic_auth(username: &str, password: &str) -> BasicAuth {
BasicAuth { username: username.to_string(), password: password.to_string() }
}
#[derive(Clone)]
pub struct BasicAuth {
username: String,
password: String,
}
impl Matcher<Request> for BasicAuth {
fn matches(&self, value: &Request) -> bool {
value
.headers()
.get("Authorization")
.is_some_and(|v| {
v.to_str()
.unwrap_or("")
.starts_with("Basic ")
})
}
fn description(&self) -> String {
format!("Basic auth with username {} and password {}", self.username, self.password)
}
}
impl TypedMatcher<Request> for BasicAuth {
fn matcher_type(&self) -> caramelo::MatchType {
ToHave
}
}