module Development.Shake.Plus.Core ( MonadAction(..) , MonadRules(..) , UnliftAction(..) , MonadUnliftAction(..) , withUnliftAction , askUnliftAction , toAction , RAction , ShakePlus , runRAction , runShakePlus , runSimpleShakePlus , parallel , forP , par , Development.Shake.Action , Development.Shake.Rules , Development.Shake.FilePattern , Development.Shake.shake , Development.Shake.shakeArgs , Development.Shake.shakeOptions ) where import Control.Exception import Development.Shake (Action, FilePattern, Rules) import qualified Development.Shake import RIO -- | Monads in which `Action`s may be embedded. class MonadIO m => MonadAction m where liftAction :: Action a -> m a instance MonadAction Action where liftAction = id instance MonadAction m => MonadAction (ReaderT r m) where liftAction = lift . liftAction newtype UnliftAction m = UnliftAction { unliftAction :: forall a. m a -> Action a } -- | Monads which allow their actions to be run in 'Action'. -- -- For the same reasons as `MonadUnliftIO` this is limited to 'ReaderT' -- and `IdentityT` transformers on top of `Action'. class MonadAction m => MonadUnliftAction m where {-# INLINE withRunInAction #-} withRunInAction :: ((forall a. m a -> Action a) -> Action b) -> m b withRunInAction inner = askUnliftAction >>= \u -> liftAction (inner (unliftAction u)) instance MonadUnliftAction Action where {-# INLINE withRunInAction #-} withRunInAction inner = inner id instance MonadUnliftAction m => MonadUnliftAction (ReaderT r m) where {-# INLINE withRunInAction #-} withRunInAction inner = ReaderT $ \r -> withRunInAction $ \run -> inner (run . flip runReaderT r) class Monad m => MonadRules m where liftRules :: Rules a -> m a instance MonadRules Rules where liftRules = id instance MonadRules m => MonadRules (ReaderT r m) where liftRules = lift . liftRules withUnliftAction :: MonadUnliftAction m => (UnliftAction m -> Action a) -> m a withUnliftAction inner = askUnliftAction >>= liftAction . inner askUnliftAction :: MonadUnliftAction m => m (UnliftAction m) askUnliftAction = withRunInAction (\run -> return (UnliftAction run)) toAction :: MonadUnliftAction m => m a -> m (Action a) toAction m = withRunInAction $ \run -> return $ run m -- | Concrete `Action` runner, hardcoded to `ReaderT r Action a`. newtype RAction r a = RAction (ReaderT r Action a) deriving (Functor, Applicative, Monad, MonadReader r, MonadIO, MonadAction, MonadUnliftAction, MonadFail) -- | Concrete `Rules` collector, hardcoded to `ReaderT r Rules a`. newtype ShakePlus r a = ShakePlus (ReaderT r Rules a) deriving (Functor, Applicative, Monad, MonadReader r, MonadIO, MonadRules) -- | Run an `RAction` with an environment, consuming it for a result. runRAction :: MonadAction m => env -> RAction env a -> m a runRAction r (RAction (ReaderT f)) = liftAction (f r) -- | Run a `ShakePlus` with an environment, consuming it for some Shake `Rules`. runShakePlus :: MonadRules m => env -> ShakePlus env a -> m a runShakePlus r (ShakePlus (ReaderT f)) = liftRules (f r) instance MonadThrow (RAction r) where throwM = liftIO . Control.Exception.throwIO instance MonadThrow (ShakePlus r) where throwM = liftIO . Control.Exception.throwIO -- | Run a `ShakePlus` with just a `LogFunc` in the environment that logs to stderr. runSimpleShakePlus :: MonadIO m => ShakePlus LogFunc a -> m () runSimpleShakePlus m = do lo <- logOptionsHandle stderr True (lf, dlf) <- newLogFunc (setLogMinLevel LevelInfo lo) liftIO $ Development.Shake.shakeArgs Development.Shake.shakeOptions $ void $ runShakePlus lf m dlf -- | Unlifted `Development.Shake.parallel`. parallel :: MonadUnliftAction m => [m a] -> m [a] parallel xs = withRunInAction $ \run -> Development.Shake.parallel $ fmap run xs -- | Unlifted `Development.Shake.forP`. forP :: MonadUnliftAction m => [a] -> (a -> m b) -> m [b] forP x f = withRunInAction $ \run -> Development.Shake.forP x $ run . f -- | Unlifted `Development.Shake.par`. par :: MonadUnliftAction m => m a -> m b -> m (a, b) par a b = withRunInAction $ \run -> Development.Shake.par (run a) (run b)