1 //-
2 // Copyright 2017, 2018 The proptest developers
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9 
10 //! Provides higher order `Arbitrary` traits.
11 //! This is mainly for use by `proptest_derive`.
12 //!
13 //! ## Stability note
14 //!
15 //! This trait is mainly defined for `proptest_derive` to simplify the
16 //! mechanics of deriving recursive types. If you have custom containers
17 //! and want to support recursive for those, it is a good idea to implement
18 //! this trait.
19 //!
20 //! There are clearer and terser ways that work better with
21 //! inference such as using `proptest::collection::vec(..)`
22 //! to achieve the same result.
23 //!
24 //! For these reasons, the traits here are deliberately
25 //! not exported in a convenient way.
26 
27 use crate::std_facade::fmt;
28 
29 use crate::strategy::{BoxedStrategy, Strategy};
30 
31 /// `ArbitraryF1` lets you lift a [`Strategy`] to unary
32 /// type constructors such as `Box`, `Vec`, and `Option`.
33 ///
34 /// The trait corresponds to
35 /// [Haskell QuickCheck's `Arbitrary1` type class][HaskellQC].
36 ///
37 /// [HaskellQC]:
38 /// https://hackage.haskell.org/package/QuickCheck-2.10.1/docs/Test-QuickCheck-Arbitrary.html#t:Arbitrary1
39 ///
40 /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
41 pub trait ArbitraryF1<A: fmt::Debug>: fmt::Debug + Sized {
42     //==========================================================================
43     // Implementation note #1
44     //==========================================================================
45     // It might be better to do this with generic associated types by
46     // having an associated type:
47     //
48     // `type Strategy<A>: Strategy<Value = Self>;`
49     //
50     // But with this setup we will likely loose the ability to add bounds
51     // such as `Hash + Eq` on `A` which is needed for `HashSet`. We might
52     // be able to regain this ability with a ConstraintKinds feature.
53     //
54     // This alternate formulation will likely work better with type inference.
55     //
56     //==========================================================================
57     // Implementation note #2
58     //==========================================================================
59     //
60     // Until `-> impl Trait` has been stabilized, `BoxedStrategy` must be
61     // used. This incurs an unfortunate performance penalty - but since
62     // we are dealing with testing, it is better to provide slowed down and
63     // somewhat less general functionality than no functionality at all.
64     // Implementations should just use `.boxed()` in the end.
65     //==========================================================================
66 
67     /// The type of parameters that [`lift1_with`] accepts for
68     /// configuration of the lifted and generated [`Strategy`]. Parameters
69     /// must implement [`Default`].
70     ///
71     /// [`lift1_with`]:
72     ///     trait.ArbitraryF1.html#tymethod.lift1_with
73     ///
74     /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
75     /// [`Default`]:
76     ///     https://doc.rust-lang.org/nightly/std/default/trait.Default.html
77     type Parameters: Default;
78 
79     /// Lifts a given [`Strategy`] to a new [`Strategy`] for the (presumably)
80     /// bigger type. This is useful for lifting a `Strategy` for `SomeType`
81     /// to a container such as `Vec<SomeType>`.
82     ///
83     /// Calling this for the type `X` is the equivalent of using
84     /// [`X::lift1_with(base, Default::default())`].
85     ///
86     /// This method is defined in the trait for optimization for the
87     /// default if you want to do that. It is a logic error to not
88     /// preserve the semantics when overriding.
89     ///
90     /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
91     ///
92     /// [`X::lift1_with(base, Default::default())`]:
93     ///     trait.ArbitraryF1.html#tymethod.lift1_with
lift1<AS>(base: AS) -> BoxedStrategy<Self> where AS: Strategy<Value = A> + 'static,94     fn lift1<AS>(base: AS) -> BoxedStrategy<Self>
95     where
96         AS: Strategy<Value = A> + 'static,
97     {
98         Self::lift1_with(base, Self::Parameters::default())
99     }
100 
101     /// Lifts a given [`Strategy`] to a new [`Strategy`] for the (presumably)
102     /// bigger type. This is useful for lifting a `Strategy` for `SomeType`
103     /// to a container such as `Vec` of `SomeType`. The composite strategy is
104     /// passed the arguments given in `args`.
105     ///
106     /// If you wish to use the [`default()`] arguments,
107     /// use [`lift1`] instead.
108     ///
109     /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
110     ///
111     /// [`lift1`]: trait.ArbitraryF1.html#method.lift1
112     ///
113     /// [`default()`]:
114     ///     https://doc.rust-lang.org/nightly/std/default/trait.Default.html
lift1_with<AS>(base: AS, args: Self::Parameters) -> BoxedStrategy<Self> where AS: Strategy<Value = A> + 'static115     fn lift1_with<AS>(base: AS, args: Self::Parameters) -> BoxedStrategy<Self>
116     where
117         AS: Strategy<Value = A> + 'static;
118 }
119 
120 /// `ArbitraryF2` lets you lift [`Strategy`] to binary
121 /// type constructors such as `Result`, `HashMap`.
122 ///
123 /// The trait corresponds to
124 /// [Haskell QuickCheck's `Arbitrary2` type class][HaskellQC].
125 ///
126 /// [HaskellQC]:
127 /// https://hackage.haskell.org/package/QuickCheck-2.10.1/docs/Test-QuickCheck-Arbitrary.html#t:Arbitrary2
128 ///
129 /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
130 pub trait ArbitraryF2<A: fmt::Debug, B: fmt::Debug>:
131     fmt::Debug + Sized
132 {
133     /// The type of parameters that [`lift2_with`] accepts for
134     /// configuration of the lifted and generated [`Strategy`]. Parameters
135     /// must implement [`Default`].
136     ///
137     /// [`lift2_with`]: trait.ArbitraryF2.html#tymethod.lift2_with
138     ///
139     /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
140     ///
141     /// [`Default`]:
142     ///     https://doc.rust-lang.org/nightly/std/default/trait.Default.html
143     type Parameters: Default;
144 
145     /// Lifts two given strategies to a new [`Strategy`] for the (presumably)
146     /// bigger type. This is useful for lifting a `Strategy` for `Type1`
147     /// and one for `Type2` to a container such as `HashMap<Type1, Type2>`.
148     ///
149     /// Calling this for the type `X` is the equivalent of using
150     /// [`X::lift2_with(base, Default::default())`].
151     ///
152     /// This method is defined in the trait for optimization for the
153     /// default if you want to do that. It is a logic error to not
154     /// preserve the semantics when overriding.
155     ///
156     /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
157     ///
158     /// [`X::lift2_with(base, Default::default())`]:
159     ///     trait.Arbitrary.html#tymethod.lift2_with
lift2<AS, BS>(fst: AS, snd: BS) -> BoxedStrategy<Self> where AS: Strategy<Value = A> + 'static, BS: Strategy<Value = B> + 'static,160     fn lift2<AS, BS>(fst: AS, snd: BS) -> BoxedStrategy<Self>
161     where
162         AS: Strategy<Value = A> + 'static,
163         BS: Strategy<Value = B> + 'static,
164     {
165         Self::lift2_with(fst, snd, Self::Parameters::default())
166     }
167 
168     /// Lifts two given strategies to a new [`Strategy`] for the (presumably)
169     /// bigger type. This is useful for lifting a `Strategy` for `Type1`
170     /// and one for `Type2` to a container such as `HashMap<Type1, Type2>`.
171     /// The composite strategy is passed the arguments given in `args`.
172     ///
173     /// If you wish to use the [`default()`] arguments,
174     /// use [`lift2`] instead.
175     ///
176     /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
177     ///
178     /// [`lift2`]: trait.ArbitraryF2.html#method.lift2
179     ///
180     /// [`default()`]:
181     ///     https://doc.rust-lang.org/nightly/std/default/trait.Default.html
lift2_with<AS, BS>( fst: AS, snd: BS, args: Self::Parameters, ) -> BoxedStrategy<Self> where AS: Strategy<Value = A> + 'static, BS: Strategy<Value = B> + 'static182     fn lift2_with<AS, BS>(
183         fst: AS,
184         snd: BS,
185         args: Self::Parameters,
186     ) -> BoxedStrategy<Self>
187     where
188         AS: Strategy<Value = A> + 'static,
189         BS: Strategy<Value = B> + 'static;
190 }
191 
192 macro_rules! lift1 {
193     ([$($bounds : tt)*] $typ: ty, $params: ty;
194      $base: ident, $args: ident => $logic: expr) => {
195         impl<A: ::core::fmt::Debug + $($bounds)*>
196         $crate::arbitrary::functor::ArbitraryF1<A>
197         for $typ {
198             type Parameters = $params;
199 
200             fn lift1_with<S>($base: S, $args: Self::Parameters)
201                 -> $crate::strategy::BoxedStrategy<Self>
202             where
203                 S: $crate::strategy::Strategy<Value = A> + 'static
204             {
205                 $crate::strategy::Strategy::boxed($logic)
206             }
207         }
208     };
209     ([$($bounds : tt)*] $typ: ty; $base: ident => $logic: expr) => {
210         lift1!([$($bounds)*] $typ, (); $base, _args => $logic);
211     };
212     ([$($bounds : tt)*] $typ: ty; $mapper: expr) => {
213         lift1!(['static + $($bounds)*] $typ; base =>
214             $crate::strategy::Strategy::prop_map(base, $mapper));
215     };
216     ([$($bounds : tt)*] $typ: ty) => {
217         lift1!(['static + $($bounds)*] $typ; base =>
218             $crate::strategy::Strategy::prop_map_into(base));
219     };
220 }
221