{-# LANGUAGE FlexibleContexts #-}

module Cardano.Chain.ValidationMode (
  ValidationMode (..),
  fromBlockValidationMode,
  orThrowErrorInBlockValidationMode,
  askBlockValidationMode,
  askTxValidationMode,
  whenBlockValidation,
  whenTxValidation,
  unlessNoTxValidation,
  wrapErrorWithValidationMode,
) where

import Cardano.Chain.Block.ValidationMode (
  BlockValidationMode (..),
  toTxValidationMode,
 )
import Cardano.Chain.UTxO.ValidationMode (TxValidationMode (..))
import Cardano.Prelude

--------------------------------------------------------------------------------
-- ValidationMode
--------------------------------------------------------------------------------

data ValidationMode = ValidationMode
  { ValidationMode -> BlockValidationMode
blockValidationMode :: !BlockValidationMode
  , ValidationMode -> TxValidationMode
txValidationMode :: !TxValidationMode
  }
  deriving (Int -> ValidationMode -> ShowS
[ValidationMode] -> ShowS
ValidationMode -> String
(Int -> ValidationMode -> ShowS)
-> (ValidationMode -> String)
-> ([ValidationMode] -> ShowS)
-> Show ValidationMode
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ValidationMode -> ShowS
showsPrec :: Int -> ValidationMode -> ShowS
$cshow :: ValidationMode -> String
show :: ValidationMode -> String
$cshowList :: [ValidationMode] -> ShowS
showList :: [ValidationMode] -> ShowS
Show)

-- | Helper function which accepts a 'BlockValidationMode', constructs a
-- sensible 'TxValidationMode' based on that, and constructs a
-- 'ValidationMode'
fromBlockValidationMode :: BlockValidationMode -> ValidationMode
fromBlockValidationMode :: BlockValidationMode -> ValidationMode
fromBlockValidationMode BlockValidationMode
bvm =
  ValidationMode
    { blockValidationMode :: BlockValidationMode
blockValidationMode = BlockValidationMode
bvm
    , txValidationMode :: TxValidationMode
txValidationMode = BlockValidationMode -> TxValidationMode
toTxValidationMode BlockValidationMode
bvm
    }

orThrowErrorInBlockValidationMode ::
  (MonadError e m, MonadReader ValidationMode m) =>
  Bool ->
  e ->
  m ()
orThrowErrorInBlockValidationMode :: forall e (m :: * -> *).
(MonadError e m, MonadReader ValidationMode m) =>
Bool -> e -> m ()
orThrowErrorInBlockValidationMode Bool
condition e
err = do
  bvm <- m BlockValidationMode
forall (m :: * -> *).
MonadReader ValidationMode m =>
m BlockValidationMode
askBlockValidationMode
  unless (bvm == NoBlockValidation || condition) (throwError err)

infix 1 `orThrowErrorInBlockValidationMode`

--------------------------------------------------------------------------------
-- ValidationMode Helpers
--------------------------------------------------------------------------------

askBlockValidationMode ::
  MonadReader ValidationMode m =>
  m BlockValidationMode
askBlockValidationMode :: forall (m :: * -> *).
MonadReader ValidationMode m =>
m BlockValidationMode
askBlockValidationMode = ValidationMode -> BlockValidationMode
blockValidationMode (ValidationMode -> BlockValidationMode)
-> m ValidationMode -> m BlockValidationMode
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m ValidationMode
forall r (m :: * -> *). MonadReader r m => m r
ask

askTxValidationMode ::
  MonadReader ValidationMode m =>
  m TxValidationMode
askTxValidationMode :: forall (m :: * -> *).
MonadReader ValidationMode m =>
m TxValidationMode
askTxValidationMode = ValidationMode -> TxValidationMode
txValidationMode (ValidationMode -> TxValidationMode)
-> m ValidationMode -> m TxValidationMode
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m ValidationMode
forall r (m :: * -> *). MonadReader r m => m r
ask

-- | Perform an action only when in the 'BlockValidation' mode. Otherwise, do
-- nothing.
whenBlockValidation ::
  (MonadError err m, MonadReader ValidationMode m) =>
  m () ->
  m ()
whenBlockValidation :: forall err (m :: * -> *).
(MonadError err m, MonadReader ValidationMode m) =>
m () -> m ()
whenBlockValidation m ()
action = do
  bvmode <- m BlockValidationMode
forall (m :: * -> *).
MonadReader ValidationMode m =>
m BlockValidationMode
askBlockValidationMode
  when (bvmode == BlockValidation) action

-- | Perform an action only when in the 'TxValidation' mode. Otherwise, do
-- nothing.
whenTxValidation ::
  (MonadError err m, MonadReader ValidationMode m) =>
  m () ->
  m ()
whenTxValidation :: forall err (m :: * -> *).
(MonadError err m, MonadReader ValidationMode m) =>
m () -> m ()
whenTxValidation m ()
action = do
  tvmode <- m TxValidationMode
forall (m :: * -> *).
MonadReader ValidationMode m =>
m TxValidationMode
askTxValidationMode
  when (tvmode == TxValidation) action

-- | Perform an action unless in the 'NoTxValidation' mode.
unlessNoTxValidation ::
  (MonadError err m, MonadReader ValidationMode m) =>
  m () ->
  m ()
unlessNoTxValidation :: forall err (m :: * -> *).
(MonadError err m, MonadReader ValidationMode m) =>
m () -> m ()
unlessNoTxValidation m ()
action = do
  tvmode <- m TxValidationMode
forall (m :: * -> *).
MonadReader ValidationMode m =>
m TxValidationMode
askTxValidationMode
  unless (tvmode == NoTxValidation) action

wrapErrorWithValidationMode ::
  (MonadError e' m, MonadReader ValidationMode m) =>
  ReaderT ValidationMode (Either e) a ->
  (e -> e') ->
  m a
wrapErrorWithValidationMode :: forall e' (m :: * -> *) e a.
(MonadError e' m, MonadReader ValidationMode m) =>
ReaderT ValidationMode (Either e) a -> (e -> e') -> m a
wrapErrorWithValidationMode ReaderT ValidationMode (Either e) a
rt e -> e'
wrapper = do
  vMode <- m ValidationMode
forall r (m :: * -> *). MonadReader r m => m r
ask
  case runReaderT rt vMode of
    Left e
err -> e' -> m a
forall a. e' -> m a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (e' -> m a) -> e' -> m a
forall a b. (a -> b) -> a -> b
$ e -> e'
wrapper e
err
    Right a
x -> a -> m a
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x

infix 1 `wrapErrorWithValidationMode`