{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Control.Flipper.Adapters.Memory
    ( FlipperT(..)
    , evalFlipperT
    , execFlipperT
    , runFlipperT
    ) where

import           Control.Monad.State
import qualified Data.Map.Strict       as Map

import           Control.Flipper.Types

{- |
The 'FlipperT' transformer for in-memory feature switchable computation.
-}
newtype FlipperT m a = FlipperT { unFlipperT :: StateT Features m a }
    deriving ( Functor
             , Applicative
             , Monad
             , MonadIO
             , MonadState Features
             , MonadTrans
             )

instance (Monad m) => HasFeatureFlags (FlipperT m) where
    getFeatures = get

    getFeature fname = do
        features <- unFeatures <$> getFeatures
        return (Map.lookup fname features)

instance (Monad m) => ModifiesFeatureFlags (FlipperT m) where
    updateFeatures = put

    updateFeature fname feature  = update fname (\_ -> Just feature)

{- |
Evaluates a feature-switched computation, returning the final value and
discarding the final state of the feature switches.
-}
evalFlipperT :: (Monad m) => Features -> FlipperT m a -> m a
evalFlipperT features f = evalStateT (unFlipperT f) features

{- |
Executes a feature-switched computation, returning the final state of the
feature switches, discarding the final value of the computation.
-}
execFlipperT :: (Monad m) => Features -> FlipperT m a -> m Features
execFlipperT features f = execStateT (unFlipperT f) features

{- |
Runs a feature-switched computation, returning the final value and state of the
feature switches.
-}
runFlipperT :: (Monad m) => Features -> FlipperT m a -> m (a, Features)
runFlipperT features f = runStateT (unFlipperT f) features