use std::borrow::Borrow;
use std::fmt;
use std::marker::PhantomData;
#[doc(inline)]
pub use crate::all_of;
#[doc(inline)]
pub use crate::any_of;
pub mod request;
pub trait Mapper<IN>: Send
where
IN: ?Sized,
{
type Out;
fn map(&mut self, input: &IN) -> Self::Out;
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
pub(crate) fn mapper_name<M, IN>(mapper: &M) -> MapperName<'_, M, IN>
where
M: ?Sized,
IN: ?Sized,
{
MapperName(mapper, PhantomData)
}
pub(crate) struct MapperName<'a, M, IN>(&'a M, PhantomData<&'a IN>)
where
M: ?Sized,
IN: ?Sized;
impl<'a, M, IN> fmt::Debug for MapperName<'a, M, IN>
where
M: Mapper<IN> + ?Sized,
IN: ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
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
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
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
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
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)
}
}
impl<IN> Mapper<IN> for &str
where
IN: AsRef<[u8]> + ?Sized,
{
type Out = bool;
fn map(&mut self, input: &IN) -> bool {
self.as_bytes() == input.as_ref()
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
impl<IN> Mapper<IN> for String
where
IN: AsRef<[u8]> + ?Sized,
{
type Out = bool;
fn map(&mut self, input: &IN) -> bool {
self.as_bytes() == input.as_ref()
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
impl<IN> Mapper<IN> for &[u8]
where
IN: AsRef<[u8]> + ?Sized,
{
type Out = bool;
fn map(&mut self, input: &IN) -> bool {
*self == input.as_ref()
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
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())
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
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)
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Not").field(&mapper_name(&self.0)).finish()
}
}
pub fn all_of<IN>(inner: Vec<Box<dyn Mapper<IN, Out = bool>>>) -> AllOf<IN>
where
IN: ?Sized,
{
AllOf(inner)
}
pub struct AllOf<IN>(Vec<Box<dyn Mapper<IN, Out = bool>>>)
where
IN: ?Sized;
impl<IN> Mapper<IN> for AllOf<IN>
where
IN: ?Sized,
{
type Out = bool;
fn map(&mut self, input: &IN) -> bool {
self.0.iter_mut().all(|maper| maper.map(input))
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
impl<IN> fmt::Debug for AllOf<IN>
where
IN: ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("AllOf")?;
f.debug_list()
.entries(self.0.iter().map(|x| mapper_name(&**x)))
.finish()
}
}
pub fn any_of<IN>(inner: Vec<Box<dyn Mapper<IN, Out = bool>>>) -> AnyOf<IN>
where
IN: ?Sized,
{
AnyOf(inner)
}
pub struct AnyOf<IN>(Vec<Box<dyn Mapper<IN, Out = bool>>>)
where
IN: ?Sized;
impl<IN> Mapper<IN> for AnyOf<IN>
where
IN: ?Sized,
{
type Out = bool;
fn map(&mut self, input: &IN) -> bool {
self.0.iter_mut().any(|maper| maper.map(input))
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
impl<IN> fmt::Debug for AnyOf<IN>
where
IN: ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("AnyOf")?;
f.debug_list()
.entries(self.0.iter().map(|x| mapper_name(&**x)))
.finish()
}
}
#[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)
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("UrlDecoded")
.field(&mapper_name(&self.0))
.finish()
}
}
pub fn json_decoded<T, M>(inner: M) -> JsonDecoded<T, M>
where
M: Mapper<T, Out = bool>,
{
JsonDecoded(PhantomData, inner)
}
#[derive(Debug)]
pub struct JsonDecoded<T, M>(PhantomData<T>, M);
impl<IN, T, M> Mapper<IN> for JsonDecoded<T, M>
where
IN: AsRef<[u8]> + ?Sized,
M: Mapper<T, Out = bool>,
T: serde::de::DeserializeOwned + Send,
{
type Out = bool;
fn map(&mut self, input: &IN) -> bool {
let value: T = match serde_json::from_slice(input.as_ref()) {
Ok(value) => value,
Err(_) => return false,
};
self.1.map(&value)
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("JsonDecoded")
.field(&mapper_name(&self.1))
.finish()
}
}
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())
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Lowercase")
.field(&mapper_name(&self.0))
.finish()
}
}
impl<IN, F> Mapper<IN> for F
where
F: Fn(&IN) -> bool + Send,
{
type Out = bool;
fn map(&mut self, input: &IN) -> bool {
self(input)
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "fn(&{}) -> bool", std::any::type_name::<IN>())
}
}
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
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("ContainsEntry")
.field(&mapper_name(&self.0))
.finish()
}
}
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())
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Key").field(&mapper_name(&self.0)).finish()
}
}
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())
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Value").field(&mapper_name(&self.0)).finish()
}
}
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())
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("")
.field(&mapper_name(&self.0))
.field(&mapper_name(&self.1))
.finish()
}
}
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({:?}) == {:?}",
mapper_name(&self.0),
input,
output
);
output
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Inspect")
.field(&mapper_name(&self.0))
.finish()
}
}
#[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("key 1", "value 1"), kv("key2", "")];
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 = |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("key1", "value1");
assert_eq!(true, key("key1").map(&kv));
assert_eq!(false, key("key2").map(&kv));
}
#[test]
fn test_value() {
let kv = kv("key1", "value1");
assert_eq!(true, value("value1").map(&kv));
assert_eq!(false, value("value2").map(&kv));
}
#[test]
fn test_tuple() {
let kv = kv("key1", "value1");
assert_eq!(true, ("key1", any()).map(&kv));
assert_eq!(true, ("key1", "value1").map(&kv));
assert_eq!(false, ("key1", "value2").map(&kv));
assert_eq!(false, ("key2", "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"));
}
}