-- | This module provides tweaks operating on transaction labels
module Cooked.Tweak.Labels
  ( labelled,
    addLabelTweak,
    removeLabelTweak,
    hasLabelTweak,
    ensureLabelTweak,
    labelled',
  )
where

import Control.Monad
import Cooked.Skeleton
import Cooked.Tweak.Common
import Data.Functor
import Data.Set qualified as Set
import Data.Text (Text)

-- | Adds a label to a 'TxSkel'.
addLabelTweak :: (LabelConstrs lbl, MonadTweak m) => lbl -> m ()
addLabelTweak :: forall lbl (m :: * -> *).
(LabelConstrs lbl, MonadTweak m) =>
lbl -> m ()
addLabelTweak = Optic' A_Lens NoIx TxSkel (Set TxSkelLabel)
-> (Set TxSkelLabel -> Set TxSkelLabel) -> m ()
forall (m :: * -> *) k (is :: IxList) a.
(MonadTweak m, Is k A_Setter) =>
Optic' k is TxSkel a -> (a -> a) -> m ()
overTweak Optic' A_Lens NoIx TxSkel (Set TxSkelLabel)
txSkelLabelL ((Set TxSkelLabel -> Set TxSkelLabel) -> m ())
-> (lbl -> Set TxSkelLabel -> Set TxSkelLabel) -> lbl -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TxSkelLabel -> Set TxSkelLabel -> Set TxSkelLabel
forall a. Ord a => a -> Set a -> Set a
Set.insert (TxSkelLabel -> Set TxSkelLabel -> Set TxSkelLabel)
-> (lbl -> TxSkelLabel)
-> lbl
-> Set TxSkelLabel
-> Set TxSkelLabel
forall b c a. (b -> c) -> (a -> b) -> a -> c
. lbl -> TxSkelLabel
forall x. LabelConstrs x => x -> TxSkelLabel
TxSkelLabel

-- | Checks if a given label is present in the 'TxSkel'
hasLabelTweak :: (LabelConstrs lbl, MonadTweak m) => lbl -> m Bool
hasLabelTweak :: forall lbl (m :: * -> *).
(LabelConstrs lbl, MonadTweak m) =>
lbl -> m Bool
hasLabelTweak = (Optic' A_Lens NoIx TxSkel (Set TxSkelLabel) -> m (Set TxSkelLabel)
forall (m :: * -> *) k (is :: IxList) a.
(MonadTweak m, Is k A_Getter) =>
Optic' k is TxSkel a -> m a
viewTweak Optic' A_Lens NoIx TxSkel (Set TxSkelLabel)
txSkelLabelL m (Set TxSkelLabel) -> (Set TxSkelLabel -> Bool) -> m Bool
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&>) ((Set TxSkelLabel -> Bool) -> m Bool)
-> (lbl -> Set TxSkelLabel -> Bool) -> lbl -> m Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TxSkelLabel -> Set TxSkelLabel -> Bool
forall a. Ord a => a -> Set a -> Bool
Set.member (TxSkelLabel -> Set TxSkelLabel -> Bool)
-> (lbl -> TxSkelLabel) -> lbl -> Set TxSkelLabel -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. lbl -> TxSkelLabel
forall x. LabelConstrs x => x -> TxSkelLabel
TxSkelLabel

-- | Ensures a given label is present in the 'TxSkel'
ensureLabelTweak :: (LabelConstrs lbl, MonadTweak m) => lbl -> m ()
ensureLabelTweak :: forall lbl (m :: * -> *).
(LabelConstrs lbl, MonadTweak m) =>
lbl -> m ()
ensureLabelTweak = lbl -> m Bool
forall lbl (m :: * -> *).
(LabelConstrs lbl, MonadTweak m) =>
lbl -> m Bool
hasLabelTweak (lbl -> m Bool) -> (Bool -> m ()) -> lbl -> m ()
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Bool -> m ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard

-- | Removes a label from a 'TxSkel' when possible, fails otherwise
removeLabelTweak :: (LabelConstrs lbl, MonadTweak m) => lbl -> m ()
removeLabelTweak :: forall lbl (m :: * -> *).
(LabelConstrs lbl, MonadTweak m) =>
lbl -> m ()
removeLabelTweak lbl
lbl = do
  lbl -> m ()
forall lbl (m :: * -> *).
(LabelConstrs lbl, MonadTweak m) =>
lbl -> m ()
ensureLabelTweak lbl
lbl
  Optic' A_Lens NoIx TxSkel (Set TxSkelLabel)
-> (Set TxSkelLabel -> Set TxSkelLabel) -> m ()
forall (m :: * -> *) k (is :: IxList) a.
(MonadTweak m, Is k A_Setter) =>
Optic' k is TxSkel a -> (a -> a) -> m ()
overTweak Optic' A_Lens NoIx TxSkel (Set TxSkelLabel)
txSkelLabelL ((Set TxSkelLabel -> Set TxSkelLabel) -> m ())
-> (TxSkelLabel -> Set TxSkelLabel -> Set TxSkelLabel)
-> TxSkelLabel
-> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TxSkelLabel -> Set TxSkelLabel -> Set TxSkelLabel
forall a. Ord a => a -> Set a -> Set a
Set.delete (TxSkelLabel -> m ()) -> TxSkelLabel -> m ()
forall a b. (a -> b) -> a -> b
$ lbl -> TxSkelLabel
forall x. LabelConstrs x => x -> TxSkelLabel
TxSkelLabel lbl
lbl

-- | Apply a tweak to a given transaction if it has a specific label. Fails if
-- it does not.
--
-- >
-- > someEndpoint = do
-- >   ...
-- >   validateTxSkel' txSkelTemplate
-- >      { txSkelLabels =
-- >         [ "InitialMinting"
-- >         , "AuctionWorkflow"
-- >         , label SomeLabelType]
-- >      }
-- >
-- > someTest = someEndpoint & eveywhere (labelled SomeLabelType someTweak)
-- > anotherTest = someEndpoint & somewhere (labelled SomeLabelType someTweak)
labelled :: (LabelConstrs lbl, MonadTweak m) => lbl -> m a -> m a
labelled :: forall lbl (m :: * -> *) a.
(LabelConstrs lbl, MonadTweak m) =>
lbl -> m a -> m a
labelled lbl
lbl = (lbl -> m ()
forall lbl (m :: * -> *).
(LabelConstrs lbl, MonadTweak m) =>
lbl -> m ()
ensureLabelTweak lbl
lbl m () -> m a -> m a
forall a b. m a -> m b -> m b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>)

-- | `labelled` specialised to Text labels
--
-- >
-- > someEndpoint = do
-- >   ...
-- >   validateTxSkel' txSkelTemplate
-- >      { txSkelLabels =
-- >         [ "InitialMinting"
-- >         , "AuctionWorkflow"
-- >         , "Spending"
-- >         , label SomeLabelType]
-- >      }
-- >
-- > someTest = someEndpoint & somewhere (labelled' "Spending" doubleSatAttack)
labelled' :: (MonadTweak m) => Text -> m a -> m a
labelled' :: forall (m :: * -> *) a. MonadTweak m => Text -> m a -> m a
labelled' = Text -> m a -> m a
forall lbl (m :: * -> *) a.
(LabelConstrs lbl, MonadTweak m) =>
lbl -> m a -> m a
labelled