module Salak.Internal.Writable(
  -- ** Writable Value
    Writable
  , toWritable
  , getWritable
  , setWritable
  ) where

import           Control.Concurrent.MVar
import           Control.Monad

-- | Writable data structure. `Writable` is designed for working with `IO` value pased by salak.
-- It provide a way to override `IO` value provided by salak, can be used in the application which need to change
-- values of some configurations by overriding it directly. For example, logger function can use a log level property
-- to control which level of logs should be printed. By using `Writeable` value, we can change the property
-- directly.
data Writable a = Writable
  { valRef :: IO a
  , setRef :: MVar (Maybe a)
  }

-- | Convert a `IO` value to `Writable` value.
{-# INLINE toWritable #-}
toWritable :: IO a -> IO (Writable a)
toWritable valRef = do
  setRef <- newMVar Nothing
  return Writable{..}

-- | Get value.
{-# INLINE getWritable #-}
getWritable :: Writable a -> IO a
getWritable Writable{..} = do
  v <- readMVar setRef
  case v of
    Just a -> return a
    _      -> valRef

-- | Set or remove override value.
{-# INLINE setWritable #-}
setWritable :: Maybe a -> Writable a -> IO ()
setWritable s Writable{..} = void $ swapMVar setRef s