packages feed

ribosome-0.4.0.0: lib/Ribosome/Persist.hs

module Ribosome.Persist where

import Control.Exception (IOException, try)
import Control.Monad.Catch (MonadThrow)
import Data.Aeson (FromJSON, ToJSON, eitherDecodeFileStrict', encodeFile)
import Path (Abs, Dir, File, Path, Rel, addExtension, parent, parseAbsDir, parseRelDir, toFilePath, (</>))
import Path.IO (XdgDirectory(XdgCache), createDirIfMissing, doesFileExist, getXdgDir)

import Ribosome.Config.Setting (setting)
import qualified Ribosome.Config.Settings as S (persistenceDir)
import Ribosome.Control.Monad.Error (recoveryFor)
import Ribosome.Control.Monad.Ribo
import Ribosome.Data.PersistError (PersistError)
import qualified Ribosome.Data.PersistError as PersistError (PersistError(..))
import Ribosome.Data.SettingError (SettingError)

defaultPersistencePath :: MonadIO m => m (Path Abs Dir)
defaultPersistencePath =
  liftIO $ getXdgDir XdgCache Nothing

persistencePath ::
  MonadRibo m =>
  NvimE e m =>
  MonadThrow m =>
  MonadDeepError e SettingError m =>
  Path Rel File ->
  m (Path Abs File)
persistencePath path = do
  base <- defaultPersistencePath `recoveryFor` (parseAbsDir =<< setting S.persistenceDir)
  name <- parseRelDir . toString =<< pluginName
  return $ base </> name </> path

persistenceFile ::
  MonadRibo m =>
  NvimE e m =>
  MonadThrow m =>
  MonadDeepError e SettingError m =>
  Path Rel File ->
  m (Path Abs File)
persistenceFile path = do
  file <- persistencePath path
  createDirIfMissing True (parent file)
  addExtension ".json" file

persistStore ::
  MonadRibo m =>
  NvimE e m =>
  MonadThrow m =>
  MonadDeepError e SettingError m =>
  ToJSON a =>
  Path Rel File ->
  a ->
  m ()
persistStore path a = do
  file <- persistenceFile path
  liftIO $ encodeFile (toFilePath file) a

noSuchFile :: MonadDeepError e PersistError m => Path Abs File -> m a
noSuchFile = throwHoist . PersistError.NoSuchFile . toFilePath

ensureExistence ::
  MonadIO m =>
  MonadDeepError e PersistError m =>
  Path Abs File ->
  m ()
ensureExistence file = do
  exists <- liftIO $ doesFileExist file
  unless exists (noSuchFile file)

decodeError ::
  MonadDeepError e PersistError m =>
  Path Abs File ->
  Text ->
  m a
decodeError file = throwHoist . PersistError.Decode (toFilePath file)

fileNotReadable ::
  MonadDeepError e PersistError m =>
  Path Abs File ->
  IOException ->
  m (Either String a)
fileNotReadable file _ = throwHoist $ PersistError.FileNotReadable (toFilePath file)

safeDecodeFile ::
  MonadIO m =>
  MonadDeepError e PersistError m =>
  FromJSON a =>
  Path Abs File ->
  m a
safeDecodeFile file = do
  result <- either (fileNotReadable file) return =<< (liftIO . try . eitherDecodeFileStrict' . toFilePath $ file)
  either (decodeError file) return . mapLeft toText $ result

persistLoad ::
  MonadIO m =>
  MonadRibo m =>
  NvimE e m =>
  MonadThrow m =>
  MonadDeepError e SettingError m =>
  MonadDeepError e PersistError m =>
  FromJSON a =>
  Path Rel File ->
  m a
persistLoad path = do
  file <- persistenceFile path
  ensureExistence file
  safeDecodeFile file

mayPersistLoad ::
  MonadRibo m =>
  NvimE e m =>
  MonadDeepError e SettingError m =>
  MonadDeepError e PersistError m =>
  MonadThrow m =>
  FromJSON a =>
  Path Rel File ->
  m (Maybe a)
mayPersistLoad =
  catchAt recover . persistLoad
  where
    recover (PersistError.NoSuchFile _) =
      return Nothing
    recover e =
      throwHoist e