{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE TypeOperators #-}

-- | Functions for retrieving context information from within tests.

module Test.Sandwich.Contexts where

import Control.Monad.Reader
import GHC.Stack
import Test.Sandwich.Types.ArgParsing
import Test.Sandwich.Types.RunTree
import Test.Sandwich.Types.Spec


-- | Get a context by its label.
getContext :: (Monad m, HasLabel context l a, HasCallStack, MonadReader context m) => Label l a -> m a
getContext :: forall (m :: * -> *) context (l :: Symbol) a.
(Monad m, HasLabel context l a, HasCallStack,
 MonadReader context m) =>
Label l a -> m a
getContext = forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall context (l :: Symbol) a.
HasLabel context l a =>
Label l a -> context -> a
getLabelValue

-- | Get the root folder of the on-disk test tree for the current run.
-- Will be 'Nothing' if the run isn't configured to use the disk.
getRunRoot :: (Monad m, HasBaseContext context, MonadReader context m) => m (Maybe FilePath)
getRunRoot :: forall (m :: * -> *) context.
(Monad m, HasBaseContext context, MonadReader context m) =>
m (Maybe FilePath)
getRunRoot = forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks (BaseContext -> Maybe FilePath
baseContextRunRoot forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. HasBaseContext a => a -> BaseContext
getBaseContext)

-- | Get the on-disk folder corresponding to the current node.
-- Will be 'Nothing' if the run isn't configured to use the disk, or if the current node is configured
-- not to create a folder.
getCurrentFolder :: (HasBaseContext context, MonadReader context m, MonadIO m) => m (Maybe FilePath)
getCurrentFolder :: forall context (m :: * -> *).
(HasBaseContext context, MonadReader context m, MonadIO m) =>
m (Maybe FilePath)
getCurrentFolder = forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks (BaseContext -> Maybe FilePath
baseContextPath forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. HasBaseContext a => a -> BaseContext
getBaseContext)

-- | Get the command line options, if configured.
-- Using the 'runSandwichWithCommandLineArgs' family of main functions will introduce these, or you can
-- introduce them manually
getCommandLineOptions :: forall a context m. (HasCommandLineOptions context a, MonadReader context m, MonadIO m) => m (CommandLineOptions a)
getCommandLineOptions :: forall a context (m :: * -> *).
(HasCommandLineOptions context a, MonadReader context m,
 MonadIO m) =>
m (CommandLineOptions a)
getCommandLineOptions = forall (m :: * -> *) context (l :: Symbol) a.
(Monad m, HasLabel context l a, HasCallStack,
 MonadReader context m) =>
Label l a -> m a
getContext forall {a}. Label "commandLineOptions" (CommandLineOptions a)
commandLineOptions

-- | Get the user command line options, if configured.
-- This just calls 'getCommandLineOptions' and pulls out the user options.
getUserCommandLineOptions :: (HasCommandLineOptions context a, MonadReader context m, MonadIO m) => m a
getUserCommandLineOptions :: forall context a (m :: * -> *).
(HasCommandLineOptions context a, MonadReader context m,
 MonadIO m) =>
m a
getUserCommandLineOptions = forall a. CommandLineOptions a -> a
optUserOptions forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) context (l :: Symbol) a.
(Monad m, HasLabel context l a, HasCallStack,
 MonadReader context m) =>
Label l a -> m a
getContext forall {a}. Label "commandLineOptions" (CommandLineOptions a)
commandLineOptions

-- * Low-level context management helpers

-- | Push a label to the context.
pushContext :: forall m l a intro context. (Monad m) => Label l intro -> intro -> ExampleT (LabelValue l intro :> context) m a -> ExampleT context m a
pushContext :: forall (m :: * -> *) (l :: Symbol) a intro context.
Monad m =>
Label l intro
-> intro
-> ExampleT (LabelValue l intro :> context) m a
-> ExampleT context m a
pushContext Label l intro
_label intro
value (ExampleT ReaderT (LabelValue l intro :> context) (LoggingT m) a
action) = do
  forall context (m :: * -> *) a.
ReaderT context (LoggingT m) a -> ExampleT context m a
ExampleT forall a b. (a -> b) -> a -> b
$ forall r' r (m :: * -> *) a.
(r' -> r) -> ReaderT r m a -> ReaderT r' m a
withReaderT (\context
context -> forall (l :: Symbol) a. a -> LabelValue l a
LabelValue intro
value forall a b. a -> b -> a :> b
:> context
context) forall a b. (a -> b) -> a -> b
$ ReaderT (LabelValue l intro :> context) (LoggingT m) a
action

-- | Remove a label from the context.
popContext :: forall m l a intro context. (Monad m) => Label l intro -> ExampleT context m a -> ExampleT (LabelValue l intro :> context) m a
popContext :: forall (m :: * -> *) (l :: Symbol) a intro context.
Monad m =>
Label l intro
-> ExampleT context m a
-> ExampleT (LabelValue l intro :> context) m a
popContext Label l intro
_label (ExampleT ReaderT context (LoggingT m) a
action) = do
  forall context (m :: * -> *) a.
ReaderT context (LoggingT m) a -> ExampleT context m a
ExampleT forall a b. (a -> b) -> a -> b
$ forall r' r (m :: * -> *) a.
(r' -> r) -> ReaderT r m a -> ReaderT r' m a
withReaderT (\(LabelValue l intro
_ :> context
context) -> context
context) forall a b. (a -> b) -> a -> b
$ ReaderT context (LoggingT m) a
action