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