use crate::{Match, Request};
use http_types::headers::{HeaderName, HeaderValue};
use http_types::Method;
use serde::Serialize;
use std::convert::TryInto;
use std::str::FromStr;
impl<F> Match for F
where
F: Fn(&Request) -> bool,
F: Send + Sync,
{
fn matches(&self, request: &Request) -> bool {
self(request)
}
}
#[derive(Debug)]
pub struct MethodExactMatcher(Method);
pub fn method<T>(method: T) -> MethodExactMatcher
where
T: AsRef<str>,
{
let method = Method::from_str(method.as_ref()).expect("Failed to convert into method");
MethodExactMatcher::new(method)
}
impl MethodExactMatcher {
pub fn new<T>(method: T) -> Self
where
T: TryInto<Method>,
<T as TryInto<Method>>::Error: std::fmt::Debug,
{
let method = method
.try_into()
.expect("Failed to convert to HTTP method.");
Self(method)
}
}
impl Match for MethodExactMatcher {
fn matches(&self, request: &Request) -> bool {
request.method == self.0
}
}
#[derive(Debug)]
pub struct PathExactMatcher(String);
pub fn path<T>(path: T) -> PathExactMatcher
where
T: Into<String>,
{
PathExactMatcher::new(path)
}
impl PathExactMatcher {
pub fn new<T: Into<String>>(path: T) -> Self {
let path = path.into();
if path.starts_with('/') {
Self(path)
} else {
Self(format!("/{}", path))
}
}
}
impl Match for PathExactMatcher {
fn matches(&self, request: &Request) -> bool {
request.url.path() == self.0
}
}
#[derive(Debug)]
pub struct HeaderExactMatcher(HeaderName, HeaderValue);
pub fn header<K, V>(key: K, value: V) -> HeaderExactMatcher
where
K: AsRef<str>,
V: AsRef<str>,
{
let key = HeaderName::from_str(key.as_ref()).expect("Failed to convert to header name.");
let value = HeaderValue::from_str(value.as_ref()).expect("Failed to convert to header value.");
HeaderExactMatcher::new(key, value)
}
impl HeaderExactMatcher {
pub fn new<K, V>(key: K, value: V) -> Self
where
K: TryInto<HeaderName>,
<K as TryInto<HeaderName>>::Error: std::fmt::Debug,
V: TryInto<HeaderValue>,
<V as TryInto<HeaderValue>>::Error: std::fmt::Debug,
{
let key = key.try_into().expect("Failed to convert to header name.");
let value = value
.try_into()
.expect("Failed to convert to header value.");
Self(key, value)
}
}
impl Match for HeaderExactMatcher {
fn matches(&self, request: &Request) -> bool {
match request.headers.get(&self.0) {
None => false,
Some(values) => values.contains(&self.1),
}
}
}
#[derive(Debug)]
pub struct BodyExactMatcher(Vec<u8>);
impl BodyExactMatcher {
pub fn string<T: Into<String>>(body: T) -> Self {
let body = body.into();
Self(body.as_bytes().into())
}
pub fn bytes<T: Into<Vec<u8>>>(body: T) -> Self {
let body = body.into();
Self(body)
}
pub fn json<T: Serialize>(body: T) -> Self {
let body = serde_json::to_vec(&body).expect("Failed to serialise body");
Self(body)
}
}
pub fn body_json<T>(body: T) -> BodyExactMatcher
where
T: Serialize,
{
BodyExactMatcher::json(body)
}
pub fn body_string<T>(body: T) -> BodyExactMatcher
where
T: Into<String>,
{
BodyExactMatcher::string(body)
}
pub fn body_bytes<T>(body: T) -> BodyExactMatcher
where
T: Into<Vec<u8>>,
{
BodyExactMatcher::bytes(body)
}
impl Match for BodyExactMatcher {
fn matches(&self, request: &Request) -> bool {
request.body == self.0
}
}
#[derive(Debug)]
pub struct QueryParamExactMatcher(String, String);
impl QueryParamExactMatcher {
pub fn new<K: Into<String>, V: Into<String>>(key: K, value: V) -> Self {
let key = key.into();
let value = value.into();
Self(key, value)
}
}
pub fn query_param<K, V>(key: K, value: V) -> QueryParamExactMatcher
where
K: Into<String>,
V: Into<String>,
{
QueryParamExactMatcher::new(key, value)
}
impl Match for QueryParamExactMatcher {
fn matches(&self, request: &Request) -> bool {
request
.url
.query_pairs()
.any(|q| q.0 == self.0.as_str() && q.1 == self.1.as_str())
}
}