use crate::wiremock::{Match, Request};
use http_types::headers::{HeaderName, HeaderValue, HeaderValues};
use http_types::{Method, Url};
use regex::Regex;
use std::convert::TryInto;
use std::str;
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: TryInto<Method>,
<T as TryInto<Method>>::Error: std::fmt::Debug,
{
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.contains('?') {
panic!("Wiremock can't match the path `{}` because it contains a `?`. You must use `wiremock::matchers::query_param` to match on query parameters (the part of the path after the `?`).", path);
}
if let Ok(url) = Url::parse(&path) {
if let Some(host) = url.host_str() {
panic!("Wiremock can't match the path `{}` because it contains the host `{}`. You don't have to specify the host - wiremock knows it. Try replacing your path with `path(\"{}\")`", path, host, url.path());
}
}
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 PathRegexMatcher(Regex);
pub fn path_regex<T>(path: T) -> PathRegexMatcher
where
T: Into<String>,
{
PathRegexMatcher::new(path)
}
impl PathRegexMatcher {
pub fn new<T: Into<String>>(path: T) -> Self {
let path = path.into();
Self(Regex::new(&path).expect("Failed to create regex for path matcher"))
}
}
impl Match for PathRegexMatcher {
fn matches(&self, request: &Request) -> bool {
self.0.is_match(request.url.path())
}
}
#[derive(Debug)]
pub struct HeaderExactMatcher(HeaderName, HeaderValues);
pub fn header<K, V>(key: K, value: V) -> HeaderExactMatcher
where
K: TryInto<HeaderName>,
<K as TryInto<HeaderName>>::Error: std::fmt::Debug,
V: TryInto<HeaderValue>,
<V as TryInto<HeaderValue>>::Error: std::fmt::Debug,
{
HeaderExactMatcher::new(key, value.try_into().map(HeaderValues::from).unwrap())
}
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<HeaderValues>,
<V as TryInto<HeaderValues>>::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) => {
let headers: Vec<&str> = self.1.iter().map(HeaderValue::as_str).collect();
values.eq(headers.as_slice())
},
}
}
}
#[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())
}
}