packages feed

calamity-0.1.9.3: src/Calamity/Commands/Check.hs

-- | Command invokation preconditions
module Calamity.Commands.Check
    ( Check(..)
    , buildCheck
    , buildCheckPure
    , runCheck ) where

import {-# SOURCE #-} Calamity.Commands.Context
import           Calamity.Commands.Error
import           Calamity.Internal.RunIntoIO
import           Calamity.Internal.Utils

import           Control.Lens                hiding ( (<.>), Context )

import           Data.Generics.Labels        ()
import           Data.Maybe
import qualified Data.Text                   as S
import qualified Data.Text.Lazy              as L

import           GHC.Generics

import qualified Polysemy                    as P

-- | A check for a command.
--
-- Every check for a command must return Nothing for the command to be run.
data Check = MkCheck
  { name     :: S.Text
    -- ^ The name of the check.
  , callback :: Context -> IO (Maybe L.Text)
    -- ^ The callback for the check, returns Nothing if it passes, otherwise
    -- returns the reason for it not passing.
  }
  deriving ( Generic )

-- | Given the name of a check and a callback in the 'P.Sem' monad, build a
-- check by transforming the Polysemy action into an IO action.
buildCheck :: P.Member (P.Final IO) r => S.Text -> (Context -> P.Sem r (Maybe L.Text)) -> P.Sem r Check
buildCheck name cb = do
  cb' <- bindSemToIO cb
  let cb'' = fromMaybe (Just "failed internally") <.> cb'
  pure $ MkCheck name cb''

-- | Given the name of a check and a pure callback function, build a check.
buildCheckPure :: S.Text -> (Context -> Maybe L.Text) -> Check
buildCheckPure name cb = MkCheck name (pure . cb)

-- | Given an invokation 'Context', run a check and transform the result into an
-- @'Either' 'CommandError' ()@.
runCheck :: P.Member (P.Embed IO) r => Context -> Check -> P.Sem r (Either CommandError ())
runCheck ctx chk = P.embed (callback chk ctx) <&> justToEither . (CheckError (chk ^. #name) <$>)