macro_rules! prop_compose { ($(#[$meta:meta])* $vis:vis $([$($modi:tt)*])? fn $name:ident $params:tt ($($var:pat in $strategy:expr),+ $(,)?) -> $return_type:ty $body:block) => { ... }; ($(#[$meta:meta])* $vis:vis $([$($modi:tt)*])? fn $name:ident $params:tt ($($var:pat in $strategy:expr),+ $(,)?) ($($var2:pat in $strategy2:expr),+ $(,)?) -> $return_type:ty $body:block) => { ... }; ($(#[$meta:meta])* $vis:vis $([$($modi:tt)*])? fn $name:ident $params:tt ($($arg:tt)+) -> $return_type:ty $body:block) => { ... }; ($(#[$meta:meta])* $vis:vis $([$($modi:tt)*])? fn $name:ident $params:tt ($($arg:tt)+ $(,)?) ($($arg2:tt)+ $(,)?) -> $return_type:ty $body:block) => { ... }; }
Expand description
Convenience to define functions which produce new strategies.
The macro has two general forms. In the first, you define a function with
two argument lists. The first argument list uses the usual syntax and
becomes exactly the argument list of the defined function. The second
argument list uses the in strategy
syntax as with proptest!
, and is
used to generate the other inputs for the function. The second argument
list has access to all arguments in the first. The return type indicates
the type of value being generated; the final return type of the function is
impl Strategy<Value = $type>
.
use proptest::prelude::*;
#[derive(Clone, Debug)]
struct MyStruct {
integer: u32,
string: String,
}
prop_compose! {
fn my_struct_strategy(max_integer: u32)
(integer in 0..max_integer, string in ".*")
-> MyStruct {
MyStruct { integer, string }
}
}
This form is simply sugar around making a tuple and then calling prop_map
on it. You can also use arg: type
as in proptest! { .. }
:
prop_compose! {
fn my_struct_strategy(max_integer: u32)
(integer in 0..max_integer, string: String)
-> MyStruct {
MyStruct { integer, string }
}
}
The second form is mostly the same, except that it takes three argument lists. The third argument list can see all values in both prior, which permits producing strategies based on other strategies.
use proptest::prelude::*;
prop_compose! {
fn nearby_numbers()(centre in -1000..1000)
(a in centre-10..centre+10,
b in centre-10..centre+10)
-> (i32, i32) {
(a, b)
}
}
However, the body of the function does not have access to the second argument list. If the body needs access to those values, they must be passed through explicitly.
use proptest::prelude::*;
prop_compose! {
fn vec_and_index
(max_length: usize)
(vec in prop::collection::vec(1..10, 1..max_length))
(index in 0..vec.len(), vec in Just(vec))
-> (Vec<i32>, usize)
{
(vec, index)
}
}
The second form is sugar around making a strategy tuple, calling
prop_flat_map()
, then prop_map()
.
To give the function any modifier which isn’t a visibility modifier, put it
in brackets before the fn
token but after any visibility modifier.
use proptest::prelude::*;
prop_compose! {
pub(crate) [unsafe] fn pointer()(v in prop::num::usize::ANY)
-> *const () {
v as *const ()
}
}
§Comparison with Hypothesis’ @composite
prop_compose!
makes it easy to do a lot of things you can do with
Hypothesis’ @composite
,
but not everything.
-
You can’t filter via this macro. For filtering, you need to make the strategy the “normal” way and use
prop_filter()
. -
More than two layers of strategies or arbitrary logic between the two layers. If you need either of these, you can achieve them by calling
prop_flat_map()
by hand.