//- // Copyright 2017, 2018 The proptest developers // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Provides higher order `Arbitrary` traits. //! This is mainly for use by `proptest_derive`. //! //! ## Stability note //! //! This trait is mainly defined for `proptest_derive` to simplify the //! mechanics of deriving recursive types. If you have custom containers //! and want to support recursive for those, it is a good idea to implement //! this trait. //! //! There are clearer and terser ways that work better with //! inference such as using `proptest::collection::vec(..)` //! to achieve the same result. //! //! For these reasons, the traits here are deliberately //! not exported in a convenient way. use crate::std_facade::fmt; use crate::strategy::{BoxedStrategy, Strategy}; /// `ArbitraryF1` lets you lift a [`Strategy`] to unary /// type constructors such as `Box`, `Vec`, and `Option`. /// /// The trait corresponds to /// [Haskell QuickCheck's `Arbitrary1` type class][HaskellQC]. /// /// [HaskellQC]: /// https://hackage.haskell.org/package/QuickCheck-2.10.1/docs/Test-QuickCheck-Arbitrary.html#t:Arbitrary1 /// /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html pub trait ArbitraryF1: fmt::Debug + Sized { //========================================================================== // Implementation note #1 //========================================================================== // It might be better to do this with generic associated types by // having an associated type: // // `type Strategy: Strategy;` // // But with this setup we will likely loose the ability to add bounds // such as `Hash + Eq` on `A` which is needed for `HashSet`. We might // be able to regain this ability with a ConstraintKinds feature. // // This alternate formulation will likely work better with type inference. // //========================================================================== // Implementation note #2 //========================================================================== // // Until `-> impl Trait` has been stabilized, `BoxedStrategy` must be // used. This incurs an unfortunate performance penalty - but since // we are dealing with testing, it is better to provide slowed down and // somewhat less general functionality than no functionality at all. // Implementations should just use `.boxed()` in the end. //========================================================================== /// The type of parameters that [`lift1_with`] accepts for /// configuration of the lifted and generated [`Strategy`]. Parameters /// must implement [`Default`]. /// /// [`lift1_with`]: /// trait.ArbitraryF1.html#tymethod.lift1_with /// /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html /// [`Default`]: /// https://doc.rust-lang.org/nightly/std/default/trait.Default.html type Parameters: Default; /// Lifts a given [`Strategy`] to a new [`Strategy`] for the (presumably) /// bigger type. This is useful for lifting a `Strategy` for `SomeType` /// to a container such as `Vec`. /// /// Calling this for the type `X` is the equivalent of using /// [`X::lift1_with(base, Default::default())`]. /// /// This method is defined in the trait for optimization for the /// default if you want to do that. It is a logic error to not /// preserve the semantics when overriding. /// /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html /// /// [`X::lift1_with(base, Default::default())`]: /// trait.ArbitraryF1.html#tymethod.lift1_with fn lift1(base: AS) -> BoxedStrategy where AS: Strategy + 'static, { Self::lift1_with(base, Self::Parameters::default()) } /// Lifts a given [`Strategy`] to a new [`Strategy`] for the (presumably) /// bigger type. This is useful for lifting a `Strategy` for `SomeType` /// to a container such as `Vec` of `SomeType`. The composite strategy is /// passed the arguments given in `args`. /// /// If you wish to use the [`default()`] arguments, /// use [`lift1`] instead. /// /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html /// /// [`lift1`]: trait.ArbitraryF1.html#method.lift1 /// /// [`default()`]: /// https://doc.rust-lang.org/nightly/std/default/trait.Default.html fn lift1_with(base: AS, args: Self::Parameters) -> BoxedStrategy where AS: Strategy + 'static; } /// `ArbitraryF2` lets you lift [`Strategy`] to binary /// type constructors such as `Result`, `HashMap`. /// /// The trait corresponds to /// [Haskell QuickCheck's `Arbitrary2` type class][HaskellQC]. /// /// [HaskellQC]: /// https://hackage.haskell.org/package/QuickCheck-2.10.1/docs/Test-QuickCheck-Arbitrary.html#t:Arbitrary2 /// /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html pub trait ArbitraryF2: fmt::Debug + Sized { /// The type of parameters that [`lift2_with`] accepts for /// configuration of the lifted and generated [`Strategy`]. Parameters /// must implement [`Default`]. /// /// [`lift2_with`]: trait.ArbitraryF2.html#tymethod.lift2_with /// /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html /// /// [`Default`]: /// https://doc.rust-lang.org/nightly/std/default/trait.Default.html type Parameters: Default; /// Lifts two given strategies to a new [`Strategy`] for the (presumably) /// bigger type. This is useful for lifting a `Strategy` for `Type1` /// and one for `Type2` to a container such as `HashMap`. /// /// Calling this for the type `X` is the equivalent of using /// [`X::lift2_with(base, Default::default())`]. /// /// This method is defined in the trait for optimization for the /// default if you want to do that. It is a logic error to not /// preserve the semantics when overriding. /// /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html /// /// [`X::lift2_with(base, Default::default())`]: /// trait.Arbitrary.html#tymethod.lift2_with fn lift2(fst: AS, snd: BS) -> BoxedStrategy where AS: Strategy + 'static, BS: Strategy + 'static, { Self::lift2_with(fst, snd, Self::Parameters::default()) } /// Lifts two given strategies to a new [`Strategy`] for the (presumably) /// bigger type. This is useful for lifting a `Strategy` for `Type1` /// and one for `Type2` to a container such as `HashMap`. /// The composite strategy is passed the arguments given in `args`. /// /// If you wish to use the [`default()`] arguments, /// use [`lift2`] instead. /// /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html /// /// [`lift2`]: trait.ArbitraryF2.html#method.lift2 /// /// [`default()`]: /// https://doc.rust-lang.org/nightly/std/default/trait.Default.html fn lift2_with( fst: AS, snd: BS, args: Self::Parameters, ) -> BoxedStrategy where AS: Strategy + 'static, BS: Strategy + 'static; } macro_rules! lift1 { ([$($bounds : tt)*] $typ: ty, $params: ty; $base: ident, $args: ident => $logic: expr) => { impl $crate::arbitrary::functor::ArbitraryF1 for $typ { type Parameters = $params; fn lift1_with($base: S, $args: Self::Parameters) -> $crate::strategy::BoxedStrategy where S: $crate::strategy::Strategy + 'static { $crate::strategy::Strategy::boxed($logic) } } }; ([$($bounds : tt)*] $typ: ty; $base: ident => $logic: expr) => { lift1!([$($bounds)*] $typ, (); $base, _args => $logic); }; ([$($bounds : tt)*] $typ: ty; $mapper: expr) => { lift1!(['static + $($bounds)*] $typ; base => $crate::strategy::Strategy::prop_map(base, $mapper)); }; ([$($bounds : tt)*] $typ: ty) => { lift1!(['static + $($bounds)*] $typ; base => $crate::strategy::Strategy::prop_map_into(base)); }; }