use std::borrow::Borrow;
use std::fmt;
#[doc(inline)]
pub use crate::all_of;
#[doc(inline)]
pub use crate::any_of;
pub mod request;
pub mod response;
pub trait Mapper<IN>: Send + fmt::Debug
where
IN: ?Sized,
{
type Out;
fn map(&mut self, input: &IN) -> Self::Out;
}
pub trait Matcher<IN>: Send + fmt::Debug
where
IN: ?Sized,
{
fn matches(&mut self, input: &IN) -> bool;
}
impl<T, IN> Matcher<IN> for T
where
T: Mapper<IN, Out = bool>,
{
fn matches(&mut self, input: &IN) -> bool {
self.map(input)
}
}
pub fn any() -> Any {
Any
}
#[derive(Debug)]
pub struct Any;
impl<IN> Mapper<IN> for Any
where
IN: ?Sized,
{
type Out = bool;
fn map(&mut self, _input: &IN) -> bool {
true
}
}
pub fn eq<T>(value: T) -> Eq<T> {
Eq(value)
}
pub struct Eq<T>(T)
where
T: ?Sized;
impl<IN, T> Mapper<IN> for Eq<T>
where
T: Borrow<IN> + fmt::Debug + Send + ?Sized,
IN: PartialEq + ?Sized,
{
type Out = bool;
fn map(&mut self, input: &IN) -> bool {
self.0.borrow() == input
}
}
impl<T> fmt::Debug for Eq<T>
where
T: fmt::Debug + ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Eq({:?})", &self.0)
}
}
pub trait IntoRegex {
fn into_regex(self) -> regex::bytes::Regex;
}
impl IntoRegex for &str {
fn into_regex(self) -> regex::bytes::Regex {
regex::bytes::Regex::new(self).expect("failed to create regex")
}
}
impl IntoRegex for String {
fn into_regex(self) -> regex::bytes::Regex {
regex::bytes::Regex::new(&self).expect("failed to create regex")
}
}
impl IntoRegex for &mut regex::bytes::RegexBuilder {
fn into_regex(self) -> regex::bytes::Regex {
self.build().expect("failed to create regex")
}
}
impl IntoRegex for regex::bytes::Regex {
fn into_regex(self) -> regex::bytes::Regex {
self
}
}
pub fn matches(value: impl IntoRegex) -> Matches {
Matches(value.into_regex())
}
#[derive(Debug)]
pub struct Matches(regex::bytes::Regex);
impl<IN> Mapper<IN> for Matches
where
IN: AsRef<[u8]> + ?Sized,
{
type Out = bool;
fn map(&mut self, input: &IN) -> bool {
self.0.is_match(input.as_ref())
}
}
pub fn not<M>(inner: M) -> Not<M> {
Not(inner)
}
pub struct Not<M>(M);
impl<M, IN> Mapper<IN> for Not<M>
where
M: Mapper<IN, Out = bool>,
IN: ?Sized,
{
type Out = bool;
fn map(&mut self, input: &IN) -> bool {
!self.0.map(input)
}
}
impl<M> fmt::Debug for Not<M>
where
M: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Not({:?})", &self.0)
}
}
pub fn all_of<IN>(inner: Vec<Box<dyn Mapper<IN, Out = bool>>>) -> AllOf<IN>
where
IN: fmt::Debug + ?Sized,
{
AllOf(inner)
}
#[derive(Debug)]
pub struct AllOf<IN>(Vec<Box<dyn Mapper<IN, Out = bool>>>)
where
IN: ?Sized;
impl<IN> Mapper<IN> for AllOf<IN>
where
IN: fmt::Debug + ?Sized,
{
type Out = bool;
fn map(&mut self, input: &IN) -> bool {
self.0.iter_mut().all(|maper| maper.map(input))
}
}
pub fn any_of<IN>(inner: Vec<Box<dyn Mapper<IN, Out = bool>>>) -> AnyOf<IN>
where
IN: fmt::Debug + ?Sized,
{
AnyOf(inner)
}
#[derive(Debug)]
pub struct AnyOf<IN>(Vec<Box<dyn Mapper<IN, Out = bool>>>)
where
IN: ?Sized;
impl<IN> Mapper<IN> for AnyOf<IN>
where
IN: fmt::Debug + ?Sized,
{
type Out = bool;
fn map(&mut self, input: &IN) -> bool {
self.0.iter_mut().any(|maper| maper.map(input))
}
}
#[derive(Debug, PartialEq, PartialOrd)]
pub struct KV<K, V>
where
Self: Sized,
K: ToOwned + ?Sized,
V: ToOwned + ?Sized,
{
pub k: K::Owned,
pub v: V::Owned,
}
pub fn kv<K, V>(k: &K, v: &V) -> KV<K, V>
where
K: ToOwned + ?Sized,
V: ToOwned + ?Sized,
{
KV {
k: k.to_owned(),
v: v.to_owned(),
}
}
pub fn url_decoded<M>(inner: M) -> UrlDecoded<M> {
UrlDecoded(inner)
}
#[derive(Debug)]
pub struct UrlDecoded<M>(M);
impl<IN, M> Mapper<IN> for UrlDecoded<M>
where
IN: AsRef<[u8]> + ?Sized,
M: Mapper<[KV<str, str>]>,
{
type Out = M::Out;
fn map(&mut self, input: &IN) -> M::Out {
let decoded: Vec<KV<str, str>> = url::form_urlencoded::parse(input.as_ref())
.into_owned()
.map(|(k, v)| KV { k, v })
.collect();
self.0.map(&decoded)
}
}
pub fn json_decoded<M>(inner: M) -> JsonDecoded<M> {
JsonDecoded(inner)
}
#[derive(Debug)]
pub struct JsonDecoded<M>(M);
impl<IN, M> Mapper<IN> for JsonDecoded<M>
where
IN: AsRef<[u8]> + ?Sized,
M: Mapper<serde_json::Value>,
{
type Out = M::Out;
fn map(&mut self, input: &IN) -> M::Out {
let json_value: serde_json::Value =
serde_json::from_slice(input.as_ref()).unwrap_or(serde_json::Value::Null);
self.0.map(&json_value)
}
}
pub fn lowercase<M>(inner: M) -> Lowercase<M> {
Lowercase(inner)
}
#[derive(Debug)]
pub struct Lowercase<M>(M);
impl<IN, M> Mapper<IN> for Lowercase<M>
where
IN: AsRef<[u8]> + ?Sized,
M: Mapper<[u8]>,
{
type Out = M::Out;
fn map(&mut self, input: &IN) -> M::Out {
use bstr::ByteSlice;
self.0.map(&input.as_ref().to_lowercase())
}
}
pub fn map_fn<F>(f: F) -> MapFn<F> {
MapFn(f)
}
pub struct MapFn<F>(F);
impl<IN, F> Mapper<IN> for MapFn<F>
where
F: Fn(&IN) -> bool + Send,
{
type Out = bool;
fn map(&mut self, input: &IN) -> bool {
self.0(input)
}
}
impl<F> fmt::Debug for MapFn<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MapFn")
}
}
pub fn contains_entry<M>(inner: M) -> ContainsEntry<M> {
ContainsEntry(inner)
}
#[derive(Debug)]
pub struct ContainsEntry<M>(M);
impl<M, E> Mapper<[E]> for ContainsEntry<M>
where
M: Mapper<E, Out = bool>,
{
type Out = bool;
fn map(&mut self, input: &[E]) -> bool {
for elem in input {
if self.0.map(elem) {
return true;
}
}
false
}
}
pub fn key<M>(inner: M) -> Key<M> {
Key(inner)
}
#[derive(Debug)]
pub struct Key<M>(M);
impl<M, K, V> Mapper<KV<K, V>> for Key<M>
where
K: ToOwned + ?Sized,
V: ToOwned + ?Sized,
M: Mapper<K>,
{
type Out = M::Out;
fn map(&mut self, input: &KV<K, V>) -> M::Out {
self.0.map(input.k.borrow())
}
}
pub fn value<M>(inner: M) -> Value<M> {
Value(inner)
}
#[derive(Debug)]
pub struct Value<M>(M);
impl<M, K, V> Mapper<KV<K, V>> for Value<M>
where
K: ToOwned + ?Sized,
V: ToOwned + ?Sized,
M: Mapper<V>,
{
type Out = M::Out;
fn map(&mut self, input: &KV<K, V>) -> M::Out {
self.0.map(input.v.borrow())
}
}
impl<K, V, KMapper, VMapper> Mapper<KV<K, V>> for (KMapper, VMapper)
where
K: ToOwned + ?Sized,
V: ToOwned + ?Sized,
KMapper: Mapper<K, Out = bool>,
VMapper: Mapper<V, Out = bool>,
{
type Out = bool;
fn map(&mut self, input: &KV<K, V>) -> bool {
self.0.map(input.k.borrow()) && self.1.map(input.v.borrow())
}
}
pub fn inspect<M>(inner: M) -> Inspect<M> {
Inspect(inner)
}
#[derive(Debug)]
pub struct Inspect<M>(M);
impl<IN, M> Mapper<IN> for Inspect<M>
where
IN: fmt::Debug + ?Sized,
M: Mapper<IN>,
M::Out: fmt::Debug,
{
type Out = M::Out;
fn map(&mut self, input: &IN) -> M::Out {
let output = self.0.map(input);
log::debug!("{:?}.map({:?}) == {:?}", self.0, input, output);
output
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_eq() {
let mut c = eq("foo");
assert_eq!(false, c.map("foobar"));
assert_eq!(false, c.map("bazfoobar"));
assert_eq!(false, c.map("bar"));
assert_eq!(true, c.map("foo"));
}
#[test]
fn test_matches() {
let mut c = matches(r#"^foo\d*bar$"#);
assert_eq!(true, c.map("foobar"));
assert_eq!(true, c.map("foo99bar"));
assert_eq!(false, c.map("foo99barz"));
assert_eq!(false, c.map("bat"));
let mut c = matches(r#"^foo\d*bar$"#.to_owned());
assert_eq!(true, c.map("foobar"));
assert_eq!(true, c.map("foo99bar"));
assert_eq!(false, c.map("foo99barz"));
assert_eq!(false, c.map("bat"));
let mut c = matches(regex::bytes::RegexBuilder::new("foobar").case_insensitive(true));
assert_eq!(true, c.map("foobar"));
assert_eq!(true, c.map("FOOBAR"));
assert_eq!(false, c.map("FOO99BAR"));
let mut c = matches(
regex::bytes::RegexBuilder::new("foobar")
.case_insensitive(true)
.build()
.unwrap(),
);
assert_eq!(true, c.map("foobar"));
assert_eq!(true, c.map("FOOBAR"));
assert_eq!(false, c.map("FOO99BAR"));
}
#[test]
fn test_not() {
let mut c = not(matches(r#"^foo\d*bar$"#));
assert_eq!(false, c.map("foobar"));
assert_eq!(false, c.map("foo99bar"));
assert_eq!(true, c.map("foo99barz"));
assert_eq!(true, c.map("bat"));
}
#[test]
fn test_all_of() {
let mut c = all_of![matches("foo"), matches("bar")];
assert_eq!(true, c.map("foobar"));
assert_eq!(true, c.map("barfoo"));
assert_eq!(false, c.map("foo"));
assert_eq!(false, c.map("bar"));
}
#[test]
fn test_any_of() {
let mut c = any_of![matches("foo"), matches("bar")];
assert_eq!(true, c.map("foobar"));
assert_eq!(true, c.map("barfoo"));
assert_eq!(true, c.map("foo"));
assert_eq!(true, c.map("bar"));
assert_eq!(false, c.map("baz"));
}
#[test]
fn test_url_decoded() {
let expected = vec![
KV {
k: "key 1".to_owned(),
v: "value 1".to_owned(),
},
KV {
k: "key2".to_owned(),
v: "".to_owned(),
},
];
let mut c = request::query(url_decoded(eq(expected)));
let req = http::Request::get("https://example.com/path?key%201=value%201&key2")
.body("")
.unwrap();
assert_eq!(true, c.map(&req));
}
#[test]
fn test_json_decoded() {
let mut c = json_decoded(eq(serde_json::json!({
"foo": 1,
"bar": 99,
})));
assert_eq!(true, c.map(r#"{"foo": 1, "bar": 99}"#));
assert_eq!(true, c.map(r#"{"bar": 99, "foo": 1}"#));
assert_eq!(false, c.map(r#"{"foo": 1, "bar": 100}"#));
}
#[test]
fn test_lowercase() {
let mut c = lowercase(matches("foo"));
assert_eq!(true, c.map("FOO"));
assert_eq!(true, c.map("FoOBar"));
assert_eq!(true, c.map("foobar"));
assert_eq!(false, c.map("bar"));
}
#[test]
fn test_fn_mapper() {
let mut c = map_fn(|input: &u64| input % 2 == 0);
assert_eq!(true, c.map(&6));
assert_eq!(true, c.map(&20));
assert_eq!(true, c.map(&0));
assert_eq!(false, c.map(&11));
}
#[test]
fn test_contains_entry() {
let mut c = contains_entry(eq(100));
assert_eq!(true, c.map(vec![100, 200, 300].as_slice()));
assert_eq!(false, c.map(vec![99, 200, 300].as_slice()));
}
#[test]
fn test_key() {
let kv: KV<str, str> = KV {
k: "key1".to_owned(),
v: "value1".to_owned(),
};
assert_eq!(true, key(eq("key1")).map(&kv));
assert_eq!(false, key(eq("key2")).map(&kv));
}
#[test]
fn test_value() {
let kv = kv("key1", "value1");
assert_eq!(true, value(eq("value1")).map(&kv));
assert_eq!(false, value(eq("value2")).map(&kv));
}
#[test]
fn test_tuple() {
let kv: KV<str, str> = KV {
k: "key1".to_owned(),
v: "value1".to_owned(),
};
assert_eq!(true, (matches("key1"), any()).map(&kv));
assert_eq!(true, (matches("key1"), matches("value1")).map(&kv));
assert_eq!(false, (matches("key1"), matches("value2")).map(&kv));
assert_eq!(false, (matches("key2"), matches("value1")).map(&kv));
}
#[test]
fn test_inspect() {
let _ = pretty_env_logger::try_init();
let mut c = inspect(lowercase(matches("^foobar$")));
assert_eq!(true, c.map("Foobar"));
assert_eq!(false, c.map("Foobar1"));
}
}