{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Test.Hspec.Core.Spec.Monad (
  Spec
, SpecWith
, SpecM (..)
, runSpecM
, fromSpecList
, runIO

, mapSpecItem
, mapSpecItem_
, modifyParams
) where

import           Prelude ()
import           Test.Hspec.Core.Compat

import           Control.Arrow
import           Control.Monad.Trans.Writer
import           Control.Monad.IO.Class (liftIO)

import           Test.Hspec.Core.Example
import           Test.Hspec.Core.Tree

type Spec = SpecWith ()

type SpecWith a = SpecM a ()

-- | A writer monad for `SpecTree` forests
newtype SpecM a r = SpecM (WriterT [SpecTree a] IO r)
  deriving (a -> SpecM a b -> SpecM a a
(a -> b) -> SpecM a a -> SpecM a b
(forall a b. (a -> b) -> SpecM a a -> SpecM a b)
-> (forall a b. a -> SpecM a b -> SpecM a a) -> Functor (SpecM a)
forall a b. a -> SpecM a b -> SpecM a a
forall a b. (a -> b) -> SpecM a a -> SpecM a b
forall a a b. a -> SpecM a b -> SpecM a a
forall a a b. (a -> b) -> SpecM a a -> SpecM a b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: a -> SpecM a b -> SpecM a a
$c<$ :: forall a a b. a -> SpecM a b -> SpecM a a
fmap :: (a -> b) -> SpecM a a -> SpecM a b
$cfmap :: forall a a b. (a -> b) -> SpecM a a -> SpecM a b
Functor, Functor (SpecM a)
a -> SpecM a a
Functor (SpecM a)
-> (forall a. a -> SpecM a a)
-> (forall a b. SpecM a (a -> b) -> SpecM a a -> SpecM a b)
-> (forall a b c.
    (a -> b -> c) -> SpecM a a -> SpecM a b -> SpecM a c)
-> (forall a b. SpecM a a -> SpecM a b -> SpecM a b)
-> (forall a b. SpecM a a -> SpecM a b -> SpecM a a)
-> Applicative (SpecM a)
SpecM a a -> SpecM a b -> SpecM a b
SpecM a a -> SpecM a b -> SpecM a a
SpecM a (a -> b) -> SpecM a a -> SpecM a b
(a -> b -> c) -> SpecM a a -> SpecM a b -> SpecM a c
forall a. Functor (SpecM a)
forall a. a -> SpecM a a
forall a a. a -> SpecM a a
forall a b. SpecM a a -> SpecM a b -> SpecM a a
forall a b. SpecM a a -> SpecM a b -> SpecM a b
forall a b. SpecM a (a -> b) -> SpecM a a -> SpecM a b
forall a a b. SpecM a a -> SpecM a b -> SpecM a a
forall a a b. SpecM a a -> SpecM a b -> SpecM a b
forall a a b. SpecM a (a -> b) -> SpecM a a -> SpecM a b
forall a b c. (a -> b -> c) -> SpecM a a -> SpecM a b -> SpecM a c
forall a a b c.
(a -> b -> c) -> SpecM a a -> SpecM a b -> SpecM a c
forall (f :: * -> *).
Functor f
-> (forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
<* :: SpecM a a -> SpecM a b -> SpecM a a
$c<* :: forall a a b. SpecM a a -> SpecM a b -> SpecM a a
*> :: SpecM a a -> SpecM a b -> SpecM a b
$c*> :: forall a a b. SpecM a a -> SpecM a b -> SpecM a b
liftA2 :: (a -> b -> c) -> SpecM a a -> SpecM a b -> SpecM a c
$cliftA2 :: forall a a b c.
(a -> b -> c) -> SpecM a a -> SpecM a b -> SpecM a c
<*> :: SpecM a (a -> b) -> SpecM a a -> SpecM a b
$c<*> :: forall a a b. SpecM a (a -> b) -> SpecM a a -> SpecM a b
pure :: a -> SpecM a a
$cpure :: forall a a. a -> SpecM a a
$cp1Applicative :: forall a. Functor (SpecM a)
Applicative, Applicative (SpecM a)
a -> SpecM a a
Applicative (SpecM a)
-> (forall a b. SpecM a a -> (a -> SpecM a b) -> SpecM a b)
-> (forall a b. SpecM a a -> SpecM a b -> SpecM a b)
-> (forall a. a -> SpecM a a)
-> Monad (SpecM a)
SpecM a a -> (a -> SpecM a b) -> SpecM a b
SpecM a a -> SpecM a b -> SpecM a b
forall a. Applicative (SpecM a)
forall a. a -> SpecM a a
forall a a. a -> SpecM a a
forall a b. SpecM a a -> SpecM a b -> SpecM a b
forall a b. SpecM a a -> (a -> SpecM a b) -> SpecM a b
forall a a b. SpecM a a -> SpecM a b -> SpecM a b
forall a a b. SpecM a a -> (a -> SpecM a b) -> SpecM a b
forall (m :: * -> *).
Applicative m
-> (forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
return :: a -> SpecM a a
$creturn :: forall a a. a -> SpecM a a
>> :: SpecM a a -> SpecM a b -> SpecM a b
$c>> :: forall a a b. SpecM a a -> SpecM a b -> SpecM a b
>>= :: SpecM a a -> (a -> SpecM a b) -> SpecM a b
$c>>= :: forall a a b. SpecM a a -> (a -> SpecM a b) -> SpecM a b
$cp1Monad :: forall a. Applicative (SpecM a)
Monad)

-- | Convert a `Spec` to a forest of `SpecTree`s.
runSpecM :: SpecWith a -> IO [SpecTree a]
runSpecM :: SpecWith a -> IO [SpecTree a]
runSpecM (SpecM WriterT [SpecTree a] IO ()
specs) = WriterT [SpecTree a] IO () -> IO [SpecTree a]
forall (m :: * -> *) w a. Monad m => WriterT w m a -> m w
execWriterT WriterT [SpecTree a] IO ()
specs

-- | Create a `Spec` from a forest of `SpecTree`s.
fromSpecList :: [SpecTree a] -> SpecWith a
fromSpecList :: [SpecTree a] -> SpecWith a
fromSpecList = WriterT [SpecTree a] IO () -> SpecWith a
forall a r. WriterT [SpecTree a] IO r -> SpecM a r
SpecM (WriterT [SpecTree a] IO () -> SpecWith a)
-> ([SpecTree a] -> WriterT [SpecTree a] IO ())
-> [SpecTree a]
-> SpecWith a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [SpecTree a] -> WriterT [SpecTree a] IO ()
forall (m :: * -> *) w. Monad m => w -> WriterT w m ()
tell

-- | Run an IO action while constructing the spec tree.
--
-- `SpecM` is a monad to construct a spec tree, without executing any spec
-- items.  @runIO@ allows you to run IO actions during this construction phase.
-- The IO action is always run when the spec tree is constructed (e.g. even
-- when @--dry-run@ is specified).
-- If you do not need the result of the IO action to construct the spec tree,
-- `Test.Hspec.Core.Hooks.beforeAll` may be more suitable for your use case.
runIO :: IO r -> SpecM a r
runIO :: IO r -> SpecM a r
runIO = WriterT [SpecTree a] IO r -> SpecM a r
forall a r. WriterT [SpecTree a] IO r -> SpecM a r
SpecM (WriterT [SpecTree a] IO r -> SpecM a r)
-> (IO r -> WriterT [SpecTree a] IO r) -> IO r -> SpecM a r
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO r -> WriterT [SpecTree a] IO r
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO

mapSpecTree :: (SpecTree a -> SpecTree b) -> SpecM a r -> SpecM b r
mapSpecTree :: (SpecTree a -> SpecTree b) -> SpecM a r -> SpecM b r
mapSpecTree SpecTree a -> SpecTree b
f (SpecM WriterT [SpecTree a] IO r
specs) = WriterT [SpecTree b] IO r -> SpecM b r
forall a r. WriterT [SpecTree a] IO r -> SpecM a r
SpecM ((IO (r, [SpecTree a]) -> IO (r, [SpecTree b]))
-> WriterT [SpecTree a] IO r -> WriterT [SpecTree b] IO r
forall (m :: * -> *) a w (n :: * -> *) b w'.
(m (a, w) -> n (b, w')) -> WriterT w m a -> WriterT w' n b
mapWriterT (((r, [SpecTree a]) -> (r, [SpecTree b]))
-> IO (r, [SpecTree a]) -> IO (r, [SpecTree b])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (([SpecTree a] -> [SpecTree b])
-> (r, [SpecTree a]) -> (r, [SpecTree b])
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second ((SpecTree a -> SpecTree b) -> [SpecTree a] -> [SpecTree b]
forall a b. (a -> b) -> [a] -> [b]
map SpecTree a -> SpecTree b
f))) WriterT [SpecTree a] IO r
specs)

mapSpecItem :: (ActionWith a -> ActionWith b) -> (Item a -> Item b) -> SpecWith a -> SpecWith b
mapSpecItem :: (ActionWith a -> ActionWith b)
-> (Item a -> Item b) -> SpecWith a -> SpecWith b
mapSpecItem ActionWith a -> ActionWith b
g Item a -> Item b
f = (SpecTree a -> SpecTree b) -> SpecWith a -> SpecWith b
forall a b r. (SpecTree a -> SpecTree b) -> SpecM a r -> SpecM b r
mapSpecTree ((ActionWith a -> ActionWith b)
-> (Item a -> Item b) -> SpecTree a -> SpecTree b
forall a b c d. (a -> b) -> (c -> d) -> Tree a c -> Tree b d
bimapTree ActionWith a -> ActionWith b
g Item a -> Item b
f)

mapSpecItem_ :: (Item a -> Item a) -> SpecWith a -> SpecWith a
mapSpecItem_ :: (Item a -> Item a) -> SpecWith a -> SpecWith a
mapSpecItem_ = (ActionWith a -> ActionWith a)
-> (Item a -> Item a) -> SpecWith a -> SpecWith a
forall a b.
(ActionWith a -> ActionWith b)
-> (Item a -> Item b) -> SpecWith a -> SpecWith b
mapSpecItem ActionWith a -> ActionWith a
forall a. a -> a
id

modifyParams :: (Params -> Params) -> SpecWith a -> SpecWith a
modifyParams :: (Params -> Params) -> SpecWith a -> SpecWith a
modifyParams Params -> Params
f = (Item a -> Item a) -> SpecWith a -> SpecWith a
forall a. (Item a -> Item a) -> SpecWith a -> SpecWith a
mapSpecItem_ ((Item a -> Item a) -> SpecWith a -> SpecWith a)
-> (Item a -> Item a) -> SpecWith a -> SpecWith a
forall a b. (a -> b) -> a -> b
$ \Item a
item -> Item a
item {itemExample :: Params -> (ActionWith a -> IO ()) -> ProgressCallback -> IO Result
itemExample = \Params
p -> (Item a
-> Params
-> (ActionWith a -> IO ())
-> ProgressCallback
-> IO Result
forall a.
Item a
-> Params
-> (ActionWith a -> IO ())
-> ProgressCallback
-> IO Result
itemExample Item a
item) (Params -> Params
f Params
p)}