use super::{matcher_name, ExecutionContext, Matcher, KV};
use std::fmt;
pub fn method<M>(inner: M) -> Method<M> {
Method(inner)
}
#[derive(Debug)]
pub struct Method<M>(M);
impl<M, B> Matcher<http::Request<B>> for Method<M>
where
M: Matcher<str>,
{
fn matches(&mut self, input: &http::Request<B>, ctx: &mut ExecutionContext) -> bool {
ctx.chain(&mut self.0, input.method().as_str())
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Method")
.field(&matcher_name(&self.0))
.finish()
}
}
pub fn path<M>(inner: M) -> Path<M> {
Path(inner)
}
#[derive(Debug)]
pub struct Path<M>(M);
impl<M, B> Matcher<http::Request<B>> for Path<M>
where
M: Matcher<str>,
{
fn matches(&mut self, input: &http::Request<B>, ctx: &mut ExecutionContext) -> bool {
ctx.chain(&mut self.0, input.uri().path())
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Path").field(&matcher_name(&self.0)).finish()
}
}
pub fn query<M>(inner: M) -> Query<M> {
Query(inner)
}
#[derive(Debug)]
pub struct Query<M>(M);
impl<M, B> Matcher<http::Request<B>> for Query<M>
where
M: Matcher<str>,
{
fn matches(&mut self, input: &http::Request<B>, ctx: &mut ExecutionContext) -> bool {
ctx.chain(&mut self.0, input.uri().query().unwrap_or(""))
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Query")
.field(&matcher_name(&self.0))
.finish()
}
}
pub fn headers<M>(inner: M) -> Headers<M> {
Headers(inner)
}
#[derive(Debug)]
pub struct Headers<M>(M);
impl<M, B> Matcher<http::Request<B>> for Headers<M>
where
M: Matcher<[KV<str, bstr::BStr>]>,
{
fn matches(&mut self, input: &http::Request<B>, ctx: &mut ExecutionContext) -> bool {
use bstr::{BStr, ByteSlice};
let headers: Vec<KV<str, BStr>> = input
.headers()
.iter()
.map(|(k, v)| KV {
k: k.as_str().to_owned(),
v: v.as_bytes().as_bstr().to_owned(),
})
.collect();
ctx.chain(&mut self.0, &headers)
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Headers")
.field(&matcher_name(&self.0))
.finish()
}
}
pub fn body<M>(inner: M) -> Body<M> {
Body(inner)
}
#[derive(Debug)]
pub struct Body<M>(M);
impl<M, B> Matcher<http::Request<B>> for Body<M>
where
B: AsRef<[u8]>,
M: Matcher<bstr::BStr>,
{
fn matches(&mut self, input: &http::Request<B>, ctx: &mut ExecutionContext) -> bool {
use bstr::ByteSlice;
ctx.chain(&mut self.0, input.body().as_ref().as_bstr())
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Body").field(&matcher_name(&self.0)).finish()
}
}
pub fn method_path<M, P>(method: M, path: P) -> MethodPath<M, P> {
MethodPath { method, path }
}
#[derive(Debug)]
pub struct MethodPath<M, P> {
method: M,
path: P,
}
impl<M, P, B> Matcher<http::Request<B>> for MethodPath<M, P>
where
M: Matcher<str>,
P: Matcher<str>,
{
fn matches(&mut self, input: &http::Request<B>, ctx: &mut ExecutionContext) -> bool {
ctx.chain(&mut self.method, input.method().as_str())
&& ctx.chain(&mut self.path, input.uri().path())
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("MethodPath")
.field("method", &matcher_name(&self.method))
.field("path", &matcher_name(&self.path))
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::matchers::*;
fn eval<M, I>(matcher: &mut M, input: &I) -> bool
where
M: Matcher<I> + ?Sized,
I: fmt::Debug + ?Sized,
{
ExecutionContext::evaluate(matcher, input)
}
#[test]
fn test_path() {
let req = http::Request::get("https://example.com/foo")
.body("")
.unwrap();
assert!(eval(&mut path("/foo"), &req));
let req = http::Request::get("https://example.com/foobar")
.body("")
.unwrap();
assert!(eval(&mut path("/foobar"), &req));
}
#[test]
fn test_query() {
let req = http::Request::get("https://example.com/path?foo=bar&baz=bat")
.body("")
.unwrap();
assert!(eval(&mut query("foo=bar&baz=bat"), &req));
let req = http::Request::get("https://example.com/path?search=1")
.body("")
.unwrap();
assert!(eval(&mut query("search=1"), &req));
}
#[test]
fn test_method() {
let req = http::Request::get("https://example.com/foo")
.body("")
.unwrap();
assert!(eval(&mut method("GET"), &req));
let req = http::Request::post("https://example.com/foobar")
.body("")
.unwrap();
assert!(eval(&mut method("POST"), &req));
}
#[test]
fn test_headers() {
use bstr::{ByteSlice, B};
let expected = vec![
KV::new("host", B("example.com").as_bstr()),
KV::new("content-length", B("101").as_bstr()),
];
let mut req = http::Request::get("https://example.com/path?key%201=value%201&key2")
.body("")
.unwrap();
req.headers_mut().extend(vec![
(
hyper::header::HOST,
hyper::header::HeaderValue::from_static("example.com"),
),
(
hyper::header::CONTENT_LENGTH,
hyper::header::HeaderValue::from_static("101"),
),
]);
assert!(eval(&mut headers(eq(expected)), &req));
}
#[test]
fn test_body() {
let req = http::Request::get("https://example.com/foo")
.body("my request body")
.unwrap();
assert!(eval(&mut body("my request body"), &req));
let bytes: &[u8] = &b"my request body"[..];
assert!(eval(&mut body(bytes), &req));
let v: Vec<u8> = "my request body".to_string().into_bytes();
assert!(eval(&mut body(v), &req));
}
#[test]
fn test_method_path() {
let req = http::Request::get("https://example.com/foo")
.body("")
.unwrap();
assert!(eval(&mut method_path("GET", "/foo"), &req));
assert!(!eval(&mut method_path("POST", "/foo"), &req));
assert!(!eval(&mut method_path("GET", "/"), &req));
let req = http::Request::post("https://example.com/foobar")
.body("")
.unwrap();
assert!(eval(&mut method_path("POST", "/foobar"), &req));
assert!(!eval(&mut method_path("GET", "/foobar"), &req));
assert!(!eval(&mut method_path("POST", "/"), &req));
}
}