-- | A compatibility module to use "Faker" data in your "Hedgehog" tests.
module Hedgehog.Gen.Faker where

import           System.IO.Unsafe (unsafePerformIO)
import qualified Faker
import Faker (Fake)
import System.Random (mkStdGen)

import Hedgehog (Gen)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import qualified Hedgehog.Internal.Gen  as InternalGen
import qualified Hedgehog.Internal.Seed as InternalSeed

-- | Select a value 'Fake' program in 'Gen'.
--
-- @since 0.0.1.0
fake :: Fake a -> Gen a
fake :: Fake a -> Gen a
fake Fake a
f = do
    StdGen
randomGen <- Int -> StdGen
mkStdGen (Int -> StdGen) -> GenT Identity Int -> GenT Identity StdGen
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Range Int -> GenT Identity Int
forall (m :: * -> *) a. (MonadGen m, Integral a) => Range a -> m a
Gen.integral_ Range Int
forall a. (Bounded a, Integral a) => Range a
Range.linearBounded
    a -> Gen a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (a -> Gen a) -> a -> Gen a
forall a b. (a -> b) -> a -> b
$!
        IO a -> a
forall a. IO a -> a
unsafePerformIO (IO a -> a) -> IO a -> a
forall a b. (a -> b) -> a -> b
$
        -- (parsonsmatt): OK so `unsafePerformIO` is bad, unless you know exactly
        -- what you're doing, so do I know exactly what I am doing? Perhaps I can
        -- convince you.
        --
        -- The Faker library doesn't keep the data as Haskell values, but stores it
        -- in `data-files`. The code that generates this fake data loads the values
        -- from the `data-files` for the library. That's what happens in IO. It is
        -- possible that the data-file is missing, and an exception will be thrown.
        -- However, no mutating actions are performed. I believe this is a safe use
        -- of 'unsafePerformIO'.
        --
        -- The alternative would be to lift it into `GenT IO a`, which is
        -- undesirable, as it would harm composition with basically any other
        -- generator.
        FakerSettings -> Fake a -> IO a
forall (m :: * -> *) a.
MonadIO m =>
FakerSettings -> FakeT m a -> m a
Faker.generateWithSettings
            (StdGen -> FakerSettings -> FakerSettings
Faker.setRandomGen
              StdGen
randomGen
              FakerSettings
Faker.defaultFakerSettings
            )
            Fake a
f