httptest/
matchers.rs

1//! Matcher implementations.
2//!
3//! This module contains matchers for composing a set of operations. The result
4//! of the composition usually results in a boolean.
5
6use std::borrow::Borrow;
7use std::fmt;
8use std::marker::PhantomData;
9
10// import the any_of and all_of macros from crate root so they are accessible if
11// people glob import this module.
12#[doc(inline)]
13pub use crate::all_of;
14#[doc(inline)]
15pub use crate::any_of;
16
17pub mod request;
18
19/// An ExecutionContext tracks how Matchers are chained together. There is a
20/// single public method called chain that when used to chain input from one
21/// matcher to another will allow tracking the flow of data across composable
22/// matchers.
23pub struct ExecutionContext {
24    // Users outside this crate should not need to construct an ExecutionContext.
25    stack_depth: usize,
26}
27
28impl ExecutionContext {
29    /// Evaluate the given matcher with the provided input.
30    pub fn evaluate<M, I>(matcher: &mut M, input: &I) -> bool
31    where
32        M: Matcher<I> + ?Sized,
33        I: fmt::Debug + ?Sized,
34    {
35        let mut ctx = ExecutionContext { stack_depth: 0 };
36        log::debug!(
37            "Matching {:?} with input: {:?}",
38            matcher_name(matcher),
39            input
40        );
41        let x = matcher.matches(input, &mut ctx);
42        log::debug!(
43            "┗━ {}",
44            if x {
45                "✅ matches"
46            } else {
47                "❌ does not match"
48            }
49        );
50        x
51    }
52
53    /// Invoke the provided matcher with the provided input. This is equivalent
54    /// to invoking `matcher.matches(input)`, but allows tracking the execution
55    /// flow to provide better diagnostics about why a request did or did not
56    /// match a composed set of matchers.
57    pub fn chain<M, I>(&mut self, matcher: &mut M, input: &I) -> bool
58    where
59        M: Matcher<I> + ?Sized,
60        I: fmt::Debug + ?Sized,
61    {
62        self.stack_depth += 1;
63        log::debug!(
64            "{}Matching {:?} with input: {:?}",
65            VerticalLines {
66                num_lines: self.stack_depth
67            },
68            matcher_name(matcher),
69            input
70        );
71        let x = matcher.matches(input, self);
72        log::debug!(
73            "{}┗━ {}",
74            VerticalLines {
75                num_lines: self.stack_depth
76            },
77            x
78        );
79        self.stack_depth -= 1;
80        x
81    }
82}
83
84struct VerticalLines {
85    num_lines: usize,
86}
87
88impl fmt::Display for VerticalLines {
89    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90        for _ in 0..self.num_lines {
91            write!(f, "┃ ")?;
92        }
93        Ok(())
94    }
95}
96
97/// The core trait. Defines how an input value should be turned into an output
98/// value. This allows for a flexible pattern of composition where two or more
99/// matchers are chained together to form a readable and flexible manipulation.
100pub trait Matcher<IN>: Send
101where
102    IN: ?Sized,
103{
104    /// Map an input to output.
105    fn matches(&mut self, input: &IN, ctx: &mut ExecutionContext) -> bool;
106
107    /// formatted name of the mapper. This is used for debugging purposes and
108    /// should typically look like a fmt::Debug representation.
109    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
110}
111
112/// convenience function to print the Matcher::fmt representation of a mapper.
113/// Returns an object with a fmt::Debug matching the Matcher::fmt.
114pub(crate) fn matcher_name<M, IN>(mapper: &M) -> MatcherName<'_, M, IN>
115where
116    M: ?Sized,
117    IN: ?Sized,
118{
119    MatcherName(mapper, PhantomData)
120}
121pub(crate) struct MatcherName<'a, M, IN>(&'a M, PhantomData<&'a IN>)
122where
123    M: ?Sized,
124    IN: ?Sized;
125impl<'a, M, IN> fmt::Debug for MatcherName<'a, M, IN>
126where
127    M: Matcher<IN> + ?Sized,
128    IN: ?Sized,
129{
130    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131        self.0.fmt(f)
132    }
133}
134
135/// Always true.
136///
137/// # Example
138///
139/// ```
140/// use httptest::matchers::*;
141///
142/// // A request matcher that matches a query parameter `foobar` with any value.
143/// request::query(url_decoded(contains(("foobar", any()))));
144/// ```
145pub fn any() -> Any {
146    Any
147}
148/// The `Any` mapper returned by [any()](fn.any.html)
149#[derive(Debug)]
150pub struct Any;
151impl<IN> Matcher<IN> for Any
152where
153    IN: ?Sized,
154{
155    fn matches(&mut self, _input: &IN, _ctx: &mut ExecutionContext) -> bool {
156        true
157    }
158
159    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
160        <Self as fmt::Debug>::fmt(self, f)
161    }
162}
163
164/// true if the input is equal to value.
165///
166/// # Example
167///
168/// ```
169/// use httptest::matchers::*;
170///
171/// request::body(json_decoded(eq(serde_json::json!({
172///     "foo": 1,
173/// }))));
174/// ```
175pub fn eq<T>(value: T) -> Eq<T> {
176    Eq(value)
177}
178/// The `Eq` mapper returned by [eq()](fn.eq.html)
179pub struct Eq<T>(T)
180where
181    T: ?Sized;
182impl<IN, T> Matcher<IN> for Eq<T>
183where
184    T: Borrow<IN> + fmt::Debug + Send + ?Sized,
185    IN: PartialEq + ?Sized,
186{
187    fn matches(&mut self, input: &IN, _ctx: &mut ExecutionContext) -> bool {
188        self.0.borrow() == input
189    }
190
191    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192        <Self as fmt::Debug>::fmt(self, f)
193    }
194}
195impl<T> fmt::Debug for Eq<T>
196where
197    T: fmt::Debug + ?Sized,
198{
199    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200        write!(f, "Eq({:?})", &self.0)
201    }
202}
203
204/// A &str is an implicit Eq mapper.
205impl<IN> Matcher<IN> for &str
206where
207    IN: AsRef<[u8]> + ?Sized,
208{
209    fn matches(&mut self, input: &IN, _ctx: &mut ExecutionContext) -> bool {
210        self.as_bytes() == input.as_ref()
211    }
212
213    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214        <Self as fmt::Debug>::fmt(self, f)
215    }
216}
217
218/// A String is an implicit Eq mapper.
219impl<IN> Matcher<IN> for String
220where
221    IN: AsRef<[u8]> + ?Sized,
222{
223    fn matches(&mut self, input: &IN, _ctx: &mut ExecutionContext) -> bool {
224        self.as_bytes() == input.as_ref()
225    }
226
227    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
228        <Self as fmt::Debug>::fmt(self, f)
229    }
230}
231
232/// A &[u8] is an implicit Eq mapper.
233impl<IN> Matcher<IN> for &[u8]
234where
235    IN: AsRef<[u8]> + ?Sized,
236{
237    fn matches(&mut self, input: &IN, _ctx: &mut ExecutionContext) -> bool {
238        *self == input.as_ref()
239    }
240
241    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
242        <Self as fmt::Debug>::fmt(self, f)
243    }
244}
245
246/// A Vec<u8> is an implicit Eq mapper.
247impl<IN> Matcher<IN> for Vec<u8>
248where
249    IN: AsRef<[u8]> + ?Sized,
250{
251    fn matches(&mut self, input: &IN, _ctx: &mut ExecutionContext) -> bool {
252        self.as_slice() == input.as_ref()
253    }
254
255    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
256        <Self as fmt::Debug>::fmt(self, f)
257    }
258}
259
260/// Create a regex.
261///
262/// This trait may panic if the regex failed to build.
263pub trait IntoRegex {
264    /// turn self into a regex.
265    fn into_regex(self) -> regex::bytes::Regex;
266}
267impl IntoRegex for &str {
268    fn into_regex(self) -> regex::bytes::Regex {
269        regex::bytes::Regex::new(self).expect("failed to create regex")
270    }
271}
272impl IntoRegex for String {
273    fn into_regex(self) -> regex::bytes::Regex {
274        regex::bytes::Regex::new(&self).expect("failed to create regex")
275    }
276}
277impl IntoRegex for &mut regex::bytes::RegexBuilder {
278    fn into_regex(self) -> regex::bytes::Regex {
279        self.build().expect("failed to create regex")
280    }
281}
282impl IntoRegex for regex::bytes::Regex {
283    fn into_regex(self) -> regex::bytes::Regex {
284        self
285    }
286}
287
288/// true if the input matches the regex provided.
289///
290/// # Example
291///
292/// ```
293/// use httptest::matchers::*;
294///
295/// // A request matcher that matches a request to path "/test/foo" or "/test/bar".
296/// request::path(matches("^/test/(foo|bar)$"));
297/// ```
298pub fn matches(value: impl IntoRegex) -> Matches {
299    //let regex = regex::bytes::Regex::new(value).expect("failed to create regex");
300    Matches(value.into_regex())
301}
302/// The `Matches` mapper returned by [matches()](fn.matches.html)
303#[derive(Debug)]
304pub struct Matches(regex::bytes::Regex);
305impl<IN> Matcher<IN> for Matches
306where
307    IN: AsRef<[u8]> + ?Sized,
308{
309    fn matches(&mut self, input: &IN, _ctx: &mut ExecutionContext) -> bool {
310        self.0.is_match(input.as_ref())
311    }
312
313    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
314        <Self as fmt::Debug>::fmt(self, f)
315    }
316}
317
318/// invert the result of the inner mapper.
319///
320/// # Example
321///
322/// ```
323/// use httptest::matchers::*;
324///
325/// // A request matcher that matches if there is no `foobar` query parameter.
326/// request::query(url_decoded(not(contains(key("foobar")))));
327/// ```
328pub fn not<M>(inner: M) -> Not<M> {
329    Not(inner)
330}
331/// The `Not` mapper returned by [not()](fn.not.html)
332pub struct Not<M>(M);
333impl<M, IN> Matcher<IN> for Not<M>
334where
335    M: Matcher<IN>,
336    IN: fmt::Debug + ?Sized,
337{
338    fn matches(&mut self, input: &IN, ctx: &mut ExecutionContext) -> bool {
339        !ctx.chain(&mut self.0, input)
340    }
341
342    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
343        f.debug_tuple("Not").field(&matcher_name(&self.0)).finish()
344    }
345}
346
347/// true if all the provided matchers return true. See the `all_of!` macro for
348/// convenient usage.
349///
350/// ```
351/// use httptest::matchers::*;
352///
353/// // A request matcher that matches a POST with a path that matches the regex 'foo.*'.
354/// let mut m = all_of![
355///     request::method("POST"),
356///     request::path(matches("foo.*")),
357/// ];
358///
359/// # // Allow type inference to determine the request type.
360/// # ExecutionContext::evaluate(&mut m, &http::Request::get("/").body("").unwrap());
361/// ```
362pub fn all_of<IN>(inner: Vec<Box<dyn Matcher<IN>>>) -> AllOf<IN>
363where
364    IN: ?Sized,
365{
366    AllOf(inner)
367}
368
369/// The `AllOf` mapper returned by [all_of()](fn.all_of.html)
370pub struct AllOf<IN>(Vec<Box<dyn Matcher<IN>>>)
371where
372    IN: ?Sized;
373impl<IN> Matcher<IN> for AllOf<IN>
374where
375    IN: fmt::Debug + ?Sized,
376{
377    fn matches(&mut self, input: &IN, ctx: &mut ExecutionContext) -> bool {
378        self.0
379            .iter_mut()
380            .all(|mapper| ctx.chain(mapper.as_mut(), input))
381    }
382
383    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
384        <Self as fmt::Debug>::fmt(self, f)
385    }
386}
387
388impl<IN> fmt::Debug for AllOf<IN>
389where
390    IN: ?Sized,
391{
392    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
393        f.write_str("AllOf")?;
394        f.debug_list()
395            .entries(self.0.iter().map(|x| matcher_name(&**x)))
396            .finish()
397    }
398}
399
400/// true if any of the provided matchers returns true. See the `any_of!` macro
401/// for convenient usage.
402///
403/// # Example
404///
405/// ```
406/// use httptest::matchers::*;
407///
408/// // A request matcher that matches a request to path "/foo"
409/// // or matches the reqex '^/test/(foo|bar)$'.
410/// let mut m = any_of![
411///     request::path("/foo"),
412///     request::path(matches("^/test/(foo|bar)$")),
413/// ];
414///
415/// # // Allow type inference to determine the request type.
416/// # ExecutionContext::evaluate(&mut m, &http::Request::get("/").body("").unwrap());
417/// ```
418pub fn any_of<IN>(inner: Vec<Box<dyn Matcher<IN>>>) -> AnyOf<IN>
419where
420    IN: ?Sized,
421{
422    AnyOf(inner)
423}
424/// The `AnyOf` mapper returned by [any_of()](fn.any_of.html)
425pub struct AnyOf<IN>(Vec<Box<dyn Matcher<IN>>>)
426where
427    IN: ?Sized;
428impl<IN> Matcher<IN> for AnyOf<IN>
429where
430    IN: fmt::Debug + ?Sized,
431{
432    fn matches(&mut self, input: &IN, ctx: &mut ExecutionContext) -> bool {
433        self.0
434            .iter_mut()
435            .any(|mapper| ctx.chain(mapper.as_mut(), input))
436    }
437
438    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
439        <Self as fmt::Debug>::fmt(self, f)
440    }
441}
442
443impl<IN> fmt::Debug for AnyOf<IN>
444where
445    IN: ?Sized,
446{
447    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
448        f.write_str("AnyOf")?;
449        f.debug_list()
450            .entries(self.0.iter().map(|x| matcher_name(&**x)))
451            .finish()
452    }
453}
454
455/// A key-value pair.
456#[derive(Debug, PartialEq, PartialOrd)]
457pub struct KV<K, V>
458where
459    Self: Sized,
460    K: ToOwned + ?Sized,
461    V: ToOwned + ?Sized,
462{
463    /// The key
464    pub k: K::Owned,
465    /// The value
466    pub v: V::Owned,
467}
468
469impl<K, V> KV<K, V>
470where
471    K: ToOwned + ?Sized,
472    V: ToOwned + ?Sized,
473{
474    /// Create a new KV. This will clone the provided k and v.
475    pub fn new(k: &K, v: &V) -> Self {
476        KV {
477            k: k.to_owned(),
478            v: v.to_owned(),
479        }
480    }
481}
482
483/// url decode the input and pass the resulting slice of key-value pairs to the next mapper.
484///
485/// # Example
486///
487/// ```rust
488/// use httptest::matchers::*;
489///
490/// // A request matcher that matches a request with a query parameter `foobar=value`.
491/// request::query(url_decoded(contains(("foobar", "value"))));
492///
493/// // A request matcher that matches a request with a form-urlencoded parameter `foobar=value`.
494/// request::body(url_decoded(contains(("foobar", "value"))));
495/// ```
496pub fn url_decoded<M>(inner: M) -> UrlDecoded<M> {
497    UrlDecoded(inner)
498}
499/// The `UrlDecoded` mapper returned by [url_decoded()](fn.url_decoded.html)
500#[derive(Debug)]
501pub struct UrlDecoded<M>(M);
502impl<IN, M> Matcher<IN> for UrlDecoded<M>
503where
504    IN: AsRef<[u8]> + ?Sized,
505    M: Matcher<[KV<str, str>]>,
506{
507    fn matches(&mut self, input: &IN, ctx: &mut ExecutionContext) -> bool {
508        let decoded: Vec<KV<str, str>> = form_urlencoded::parse(input.as_ref())
509            .into_owned()
510            .map(|(k, v)| KV { k, v })
511            .collect();
512        ctx.chain(&mut self.0, &decoded)
513    }
514
515    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
516        f.debug_tuple("UrlDecoded")
517            .field(&matcher_name(&self.0))
518            .finish()
519    }
520}
521
522/// json decode the input and pass the resulting value to the inner mapper. If
523/// the input cannot be decoded a false value is returned.
524///
525/// This can be used with Fn matchers to allow for flexible matching of json content.
526/// The following example matches whenever the body of the request contains a
527/// json list of strings of length 3.
528///
529/// # Example
530///
531/// ```rust
532/// use httptest::matchers::*;
533///
534/// request::body(json_decoded(|b: &Vec<String>| b.len() == 3));
535/// ```
536pub fn json_decoded<T, M>(inner: M) -> JsonDecoded<T, M>
537where
538    M: Matcher<T>,
539{
540    JsonDecoded(PhantomData, inner)
541}
542/// The `JsonDecoded` mapper returned by [json_decoded()](fn.json_decoded.html)
543#[derive(Debug)]
544pub struct JsonDecoded<T, M>(PhantomData<T>, M);
545impl<IN, T, M> Matcher<IN> for JsonDecoded<T, M>
546where
547    IN: AsRef<[u8]> + ?Sized,
548    M: Matcher<T>,
549    T: serde::de::DeserializeOwned + fmt::Debug + Send,
550{
551    fn matches(&mut self, input: &IN, ctx: &mut ExecutionContext) -> bool {
552        let value: T = match serde_json::from_slice(input.as_ref()) {
553            Ok(value) => value,
554            Err(_) => return false,
555        };
556        ctx.chain(&mut self.1, &value)
557    }
558
559    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
560        f.debug_tuple("JsonDecoded")
561            .field(&matcher_name(&self.1))
562            .finish()
563    }
564}
565
566/// lowercase the input and pass it to the next mapper.
567///
568/// # Example
569///
570/// ```
571/// use httptest::matchers::*;
572///
573/// // A request matcher that matches a request with a query parameter `foo` in any case.
574/// request::query(url_decoded(contains(key(lowercase("foo")))));
575/// ```
576pub fn lowercase<M>(inner: M) -> Lowercase<M> {
577    Lowercase(inner)
578}
579/// The `Lowercase` mapper returned by [lowercase()](fn.lowercase.html)
580#[derive(Debug)]
581pub struct Lowercase<M>(M);
582impl<IN, M> Matcher<IN> for Lowercase<M>
583where
584    IN: AsRef<[u8]> + ?Sized,
585    M: Matcher<[u8]>,
586{
587    fn matches(&mut self, input: &IN, ctx: &mut ExecutionContext) -> bool {
588        use bstr::ByteSlice;
589        ctx.chain(&mut self.0, &input.as_ref().to_lowercase())
590    }
591
592    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
593        f.debug_tuple("Lowercase")
594            .field(&matcher_name(&self.0))
595            .finish()
596    }
597}
598
599// Fn(T) -> bool implements Matcher<T>
600impl<IN, F> Matcher<IN> for F
601where
602    F: Fn(&IN) -> bool + Send,
603{
604    fn matches(&mut self, input: &IN, _ctx: &mut ExecutionContext) -> bool {
605        self(input)
606    }
607
608    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
609        write!(f, "fn(&{}) -> bool", std::any::type_name::<IN>())
610    }
611}
612
613/// true if any input element matches the provided mapper.
614///
615/// This works on slices of elements. Each element is handed to the provided
616/// mapper until the mapper returns true for one, false if no elements evaluate
617/// to true.
618///
619/// Look at [matches()](fn.matches.html) if substring matching is what want.
620///
621/// # Example
622///
623/// ```
624/// use httptest::matchers::*;
625///
626/// // A request matcher that matches a request with a header `x-foobar=value`.
627/// request::headers(contains(("x-foobar", "value")));
628///
629/// // A request matcher that matches a request with a query parameter `foo=bar`.
630/// request::query(url_decoded(contains(("foo", "bar"))));
631///
632/// // A request matcher that matches a request with a query parameter `foo` and any value.
633/// // Same as `contains(key("foo"))`.
634/// request::query(url_decoded(contains(("foo", any()))));
635/// ```
636pub fn contains<M>(inner: M) -> Contains<M> {
637    Contains(inner)
638}
639/// The `Contains` mapper returned by [contains()](fn.contains.html)
640#[derive(Debug)]
641pub struct Contains<M>(M);
642impl<M, E> Matcher<[E]> for Contains<M>
643where
644    M: Matcher<E>,
645    E: fmt::Debug,
646{
647    fn matches(&mut self, input: &[E], ctx: &mut ExecutionContext) -> bool {
648        input.iter().any(|x| ctx.chain(&mut self.0, x))
649    }
650
651    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
652        f.debug_tuple("Contains")
653            .field(&matcher_name(&self.0))
654            .finish()
655    }
656}
657
658/// extract the key from a key-value pair.
659///
660/// # Example
661///
662/// ```
663/// use httptest::matchers::*;
664///
665/// // A request matcher that matches a request with a header `x-foobar` with any value.
666/// request::headers(contains(key("x-foobar")));
667///
668/// // A request matcher that matches a request with a query parameter `foobar` with any value.
669/// request::query(url_decoded(contains(key("foobar"))));
670/// ```
671pub fn key<M>(inner: M) -> Key<M> {
672    Key(inner)
673}
674/// The `Key` mapper returned by [key()](fn.key.html)
675#[derive(Debug)]
676pub struct Key<M>(M);
677impl<M, K, V> Matcher<KV<K, V>> for Key<M>
678where
679    K: ToOwned + fmt::Debug + ?Sized,
680    V: ToOwned + ?Sized,
681    M: Matcher<K>,
682{
683    fn matches(&mut self, input: &KV<K, V>, ctx: &mut ExecutionContext) -> bool {
684        ctx.chain(&mut self.0, input.k.borrow())
685    }
686
687    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
688        f.debug_tuple("Key").field(&matcher_name(&self.0)).finish()
689    }
690}
691
692/// extract the value from a key-value pair.
693///
694/// # Example
695///
696/// ```
697/// use httptest::matchers::*;
698///
699/// // A request matcher that matches any query parameter with the value `foobar`.
700/// request::query(url_decoded(contains(value("foobar"))));
701/// ```
702pub fn value<M>(inner: M) -> Value<M> {
703    Value(inner)
704}
705/// The `Value` mapper returned by [value()](fn.value.html)
706#[derive(Debug)]
707pub struct Value<M>(M);
708impl<M, K, V> Matcher<KV<K, V>> for Value<M>
709where
710    K: ToOwned + ?Sized,
711    V: ToOwned + fmt::Debug + ?Sized,
712    M: Matcher<V>,
713{
714    fn matches(&mut self, input: &KV<K, V>, ctx: &mut ExecutionContext) -> bool {
715        ctx.chain(&mut self.0, input.v.borrow())
716    }
717
718    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
719        f.debug_tuple("Value")
720            .field(&matcher_name(&self.0))
721            .finish()
722    }
723}
724
725impl<K, V, KMatcher, VMatcher> Matcher<KV<K, V>> for (KMatcher, VMatcher)
726where
727    K: ToOwned + fmt::Debug + ?Sized,
728    V: ToOwned + fmt::Debug + ?Sized,
729    KMatcher: Matcher<K>,
730    VMatcher: Matcher<V>,
731{
732    fn matches(&mut self, input: &KV<K, V>, ctx: &mut ExecutionContext) -> bool {
733        ctx.chain(&mut self.0, input.k.borrow()) && ctx.chain(&mut self.1, input.v.borrow())
734    }
735
736    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
737        f.debug_tuple("")
738            .field(&matcher_name(&self.0))
739            .field(&matcher_name(&self.1))
740            .finish()
741    }
742}
743
744/// extract the length of the input.
745///
746/// # Example
747///
748/// ```
749/// use httptest::matchers::*;
750///
751/// // A request matcher that matches a header `x-foobar` and the value has the length of 3.
752/// request::headers(contains(("x-foobar", len(eq(3)))));
753///
754/// // A request matcher that matches a request with two query parameters.
755/// request::query(url_decoded(len(eq(2))));
756///
757/// // A request matcher that matches a body with the length of 42.
758/// request::body(len(eq(42)));
759/// ```
760pub fn len<M>(inner: M) -> Len<M> {
761    Len(inner)
762}
763/// The `Len` mapper returned by [len()](fn.len.html)
764#[derive(Debug)]
765pub struct Len<M>(M);
766impl<M, T> Matcher<[T]> for Len<M>
767where
768    M: Matcher<usize>,
769{
770    fn matches(&mut self, input: &[T], ctx: &mut ExecutionContext) -> bool {
771        ctx.chain(&mut self.0, &input.len())
772    }
773
774    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
775        f.debug_tuple("Len").field(&matcher_name(&self.0)).finish()
776    }
777}
778
779impl<M> Matcher<str> for Len<M>
780where
781    M: Matcher<usize>,
782{
783    fn matches(&mut self, input: &str, ctx: &mut ExecutionContext) -> bool {
784        ctx.chain(&mut self.0, &input.len())
785    }
786
787    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
788        f.debug_tuple("Len").field(&matcher_name(&self.0)).finish()
789    }
790}
791
792impl<M> Matcher<bstr::BStr> for Len<M>
793where
794    M: Matcher<usize>,
795{
796    fn matches(&mut self, input: &bstr::BStr, ctx: &mut ExecutionContext) -> bool {
797        ctx.chain(&mut self.0, &input.len())
798    }
799
800    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
801        f.debug_tuple("Len").field(&matcher_name(&self.0)).finish()
802    }
803}
804
805#[cfg(test)]
806mod tests {
807    use super::*;
808
809    fn eval<M, I>(matcher: &mut M, input: &I) -> bool
810    where
811        M: Matcher<I> + ?Sized,
812        I: fmt::Debug + ?Sized,
813    {
814        ExecutionContext::evaluate(matcher, input)
815    }
816
817    #[test]
818    fn test_eq() {
819        let mut c = eq("foo");
820        assert_eq!(false, eval(&mut c, "foobar"));
821        assert_eq!(false, eval(&mut c, "bazfoobar"));
822        assert_eq!(false, eval(&mut c, "bar"));
823        assert_eq!(true, eval(&mut c, "foo"));
824    }
825
826    #[test]
827    fn test_matches() {
828        // regex from str
829        let mut c = matches(r#"^foo\d*bar$"#);
830        assert_eq!(true, eval(&mut c, "foobar"));
831        assert_eq!(true, eval(&mut c, "foo99bar"));
832        assert_eq!(false, eval(&mut c, "foo99barz"));
833        assert_eq!(false, eval(&mut c, "bat"));
834
835        // regex from String
836        let mut c = matches(r#"^foo\d*bar$"#.to_owned());
837        assert_eq!(true, eval(&mut c, "foobar"));
838        assert_eq!(true, eval(&mut c, "foo99bar"));
839        assert_eq!(false, eval(&mut c, "foo99barz"));
840        assert_eq!(false, eval(&mut c, "bat"));
841
842        // regex from RegexBuilder
843        let mut c = matches(regex::bytes::RegexBuilder::new("foobar").case_insensitive(true));
844        assert_eq!(true, eval(&mut c, "foobar"));
845        assert_eq!(true, eval(&mut c, "FOOBAR"));
846        assert_eq!(false, eval(&mut c, "FOO99BAR"));
847
848        // regex from Regex
849        let mut c = matches(
850            regex::bytes::RegexBuilder::new("foobar")
851                .case_insensitive(true)
852                .build()
853                .unwrap(),
854        );
855        assert_eq!(true, eval(&mut c, "foobar"));
856        assert_eq!(true, eval(&mut c, "FOOBAR"));
857        assert_eq!(false, eval(&mut c, "FOO99BAR"));
858    }
859
860    #[test]
861    fn test_not() {
862        let mut c = not(matches(r#"^foo\d*bar$"#));
863        assert_eq!(false, eval(&mut c, "foobar"));
864        assert_eq!(false, eval(&mut c, "foo99bar"));
865        assert_eq!(true, eval(&mut c, "foo99barz"));
866        assert_eq!(true, eval(&mut c, "bat"));
867    }
868
869    #[test]
870    fn test_all_of() {
871        let mut c = all_of![matches("foo"), matches("bar")];
872        assert_eq!(true, eval(&mut c, "foobar"));
873        assert_eq!(true, eval(&mut c, "barfoo"));
874        assert_eq!(false, eval(&mut c, "foo"));
875        assert_eq!(false, eval(&mut c, "bar"));
876    }
877
878    #[test]
879    fn test_any_of() {
880        let mut c = any_of![matches("foo"), matches("bar")];
881        assert_eq!(true, eval(&mut c, "foobar"));
882        assert_eq!(true, eval(&mut c, "barfoo"));
883        assert_eq!(true, eval(&mut c, "foo"));
884        assert_eq!(true, eval(&mut c, "bar"));
885        assert_eq!(false, eval(&mut c, "baz"));
886    }
887
888    #[test]
889    fn test_url_decoded() {
890        let expected = vec![KV::new("key 1", "value 1"), KV::new("key2", "")];
891        let mut c = request::query(url_decoded(eq(expected)));
892        let req = http::Request::get("https://example.com/path?key%201=value%201&key2")
893            .body("")
894            .unwrap();
895
896        assert_eq!(true, eval(&mut c, &req));
897    }
898
899    #[test]
900    fn test_json_decoded() {
901        let mut c = json_decoded(eq(serde_json::json!({
902            "foo": 1,
903            "bar": 99,
904        })));
905        assert_eq!(true, eval(&mut c, r#"{"foo": 1, "bar": 99}"#));
906        assert_eq!(true, eval(&mut c, r#"{"bar": 99, "foo": 1}"#));
907        assert_eq!(false, eval(&mut c, r#"{"foo": 1, "bar": 100}"#));
908    }
909
910    #[test]
911    fn test_lowercase() {
912        let mut c = lowercase(matches("foo"));
913        assert_eq!(true, eval(&mut c, "FOO"));
914        assert_eq!(true, eval(&mut c, "FoOBar"));
915        assert_eq!(true, eval(&mut c, "foobar"));
916        assert_eq!(false, eval(&mut c, "bar"));
917    }
918
919    #[test]
920    fn test_fn_mapper() {
921        let mut c = |input: &u64| input % 2 == 0;
922        assert_eq!(true, eval(&mut c, &6));
923        assert_eq!(true, eval(&mut c, &20));
924        assert_eq!(true, eval(&mut c, &0));
925        assert_eq!(false, eval(&mut c, &11));
926    }
927
928    #[test]
929    fn test_contains() {
930        let mut c = contains(eq(100));
931        assert_eq!(true, eval(&mut c, vec![100, 200, 300].as_slice()));
932        assert_eq!(false, eval(&mut c, vec![99, 200, 300].as_slice()));
933    }
934
935    #[test]
936    fn test_key() {
937        let kv = KV::new("key1", "value1");
938        assert_eq!(true, eval(&mut key("key1"), &kv));
939        assert_eq!(false, eval(&mut key("key2"), &kv));
940    }
941
942    #[test]
943    fn test_value() {
944        let kv = KV::new("key1", "value1");
945        assert_eq!(true, eval(&mut value("value1"), &kv));
946        assert_eq!(false, eval(&mut value("value2"), &kv));
947    }
948
949    #[test]
950    fn test_tuple() {
951        let kv = KV::new("key1", "value1");
952        assert_eq!(true, eval(&mut ("key1", any()), &kv));
953        assert_eq!(true, eval(&mut ("key1", "value1"), &kv));
954        assert_eq!(false, eval(&mut ("key1", "value2"), &kv));
955        assert_eq!(false, eval(&mut ("key2", "value1"), &kv));
956    }
957
958    #[test]
959    fn test_len() {
960        let mut c = len(eq(3));
961        assert_eq!(true, eval(&mut c, "foo"));
962        assert_eq!(false, eval(&mut c, "foobar"));
963        assert_eq!(true, eval(&mut c, &b"foo"[..]));
964        assert_eq!(false, eval(&mut c, &b"foobar"[..]));
965
966        let req = http::Request::get("/test?foo=bar").body("foobar").unwrap();
967        assert!(eval(&mut request::body(len(eq(6))), &req));
968    }
969
970    #[test]
971    fn test_fn() {
972        let mut c = len(|&len: &usize| len <= 3);
973        assert_eq!(true, eval(&mut c, "f"));
974        assert_eq!(true, eval(&mut c, "fo"));
975        assert_eq!(true, eval(&mut c, "foo"));
976        assert_eq!(false, eval(&mut c, "foob"));
977        assert_eq!(false, eval(&mut c, "fooba"));
978        assert_eq!(false, eval(&mut c, "foobar"));
979    }
980}