-- | Some useful functions to work with Data.Acid.

module Serokell.AcidState.Util
       (
         -- | Simple helpers
         exceptStateToUpdate
       , exceptStateToUpdateGeneric
       , readerToQuery
       , stateToUpdate

         -- | Utilities
       , createAndDiscardArchive
       , tidyLocalState
       ) where

import           Control.Exception    (Exception, throw)
import           Control.Monad.Except (ExceptT, runExceptT)
import           Control.Monad.Reader (Reader, asks, runReader)
import           Control.Monad.State  (State, runState, state)
import           Control.Monad.Trans  (MonadIO (liftIO))
import           Data.Acid            (AcidState, Query, Update, createArchive,
                                       createCheckpoint)
import           System.Directory     (removeDirectoryRecursive)
import           System.FilePath      ((</>))

readerToQuery :: Reader s a -> Query s a
readerToQuery = asks . runReader

stateToUpdate :: State s a -> Update s a
stateToUpdate = state . runState

exceptStateToUpdate
    :: (Exception e)
    => ExceptT e (State s) a -> Update s a
exceptStateToUpdate = exceptStateToUpdateGeneric id

exceptStateToUpdateGeneric
  :: (Exception exc)
  => (e -> exc) -> ExceptT e (State s) a -> Update s a
exceptStateToUpdateGeneric toException u =
    state $
    runState $
    do res <- runExceptT u
       either (throw . toException) return res

-- | Archive unnecessary data (see createArchive docs for details) and
-- discard it. Works for local state.
createAndDiscardArchive :: MonadIO m => AcidState st -> FilePath -> m ()
createAndDiscardArchive st path =
    liftIO $ createArchive st >> removeDirectoryRecursive (path </> "Archive")

-- | Apply all updates and remove all data from local state which is
-- unnecessary for state restoration.
tidyLocalState :: MonadIO m => AcidState st -> FilePath -> m ()
tidyLocalState st path =
    liftIO (createCheckpoint st) >> createAndDiscardArchive st path