module Ribosome.Interpreter.Persist where
import Data.Aeson (eitherDecodeFileStrict', encodeFile)
import Exon (exon)
import qualified Log
import Path (Abs, Dir, File, Path, Rel, parent, parseRelDir, parseRelFile, toFilePath, (</>))
import Path.IO (createDirIfMissing, doesFileExist)
import qualified Ribosome.Data.PersistError as PersistError
import Ribosome.Data.PersistError (PersistError)
import Ribosome.Data.PersistPathError (PersistPathError)
import qualified Ribosome.Effect.Persist as Persist
import Ribosome.Effect.Persist (Persist)
import Ribosome.Effect.PersistPath (PersistPath, persistRoot)
import Ribosome.Host.Data.BootError (BootError (BootError))
import Ribosome.Host.Interpret (with)
import Ribosome.Host.Path (pathText)
persistBase ::
Members [PersistPath !! PersistPathError, Stop PersistError] r =>
Sem r (Path Abs Dir)
persistBase :: forall (r :: EffectRow).
Members '[PersistPath !! PersistPathError, Stop PersistError] r =>
Sem r (Path Abs Dir)
persistBase =
(PersistPathError -> PersistError)
-> Sem (PersistPath : r) (Path Abs Dir) -> Sem r (Path Abs Dir)
forall err (eff :: (* -> *) -> * -> *) err' (r :: EffectRow) a.
Members '[Resumable err eff, Stop err'] r =>
(err -> err') -> Sem (eff : r) a -> Sem r a
resumeHoist PersistPathError -> PersistError
PersistError.Path Sem (PersistPath : r) (Path Abs Dir)
forall (r :: EffectRow).
Member PersistPath r =>
Sem r (Path Abs Dir)
persistRoot
loadFile ::
FromJSON a =>
Members [Stop PersistError, Log, Embed IO] r =>
Path Abs File ->
Sem r a
loadFile :: forall a (r :: EffectRow).
(FromJSON a, Members '[Stop PersistError, Log, Embed IO] r) =>
Path Abs File -> Sem r a
loadFile Path Abs File
file = do
Text -> Sem r ()
forall (r :: EffectRow).
(HasCallStack, Member Log r) =>
Text -> Sem r ()
Log.debug [exon|Loading persistence file: #{show file}|]
(String -> PersistError) -> Either String a -> Sem r a
forall err' (r :: EffectRow) err a.
Member (Stop err') r =>
(err -> err') -> Either err a -> Sem r a
stopEitherWith String -> PersistError
decodeFailed (Either String a -> Sem r a) -> Sem r (Either String a) -> Sem r a
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< PersistError -> Maybe (Either String a) -> Sem r (Either String a)
forall err (r :: EffectRow) a.
Member (Stop err) r =>
err -> Maybe a -> Sem r a
stopNote PersistError
notReadable (Maybe (Either String a) -> Sem r (Either String a))
-> Sem r (Maybe (Either String a)) -> Sem r (Either String a)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO (Either String a) -> Sem r (Maybe (Either String a))
forall (r :: EffectRow) a.
Member (Embed IO) r =>
IO a -> Sem r (Maybe a)
tryMaybe (String -> IO (Either String a)
forall a. FromJSON a => String -> IO (Either String a)
eitherDecodeFileStrict' (Path Abs File -> String
forall b t. Path b t -> String
toFilePath Path Abs File
file))
where
notReadable :: PersistError
notReadable =
Text -> PersistError
PersistError.Permission (Path Abs File -> Text
forall b t. Path b t -> Text
pathText Path Abs File
file)
decodeFailed :: String -> PersistError
decodeFailed =
Text -> Text -> PersistError
PersistError.Decode (Path Abs File -> Text
forall b t. Path b t -> Text
pathText Path Abs File
file) (Text -> PersistError)
-> (String -> Text) -> String -> PersistError
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
forall a. ToText a => a -> Text
toText
filepath ::
Members [PersistPath !! PersistPathError, Stop PersistError] r =>
Path Rel File ->
Path Rel Dir ->
Maybe (Path Rel File) ->
Sem r (Path Abs File)
filepath :: forall (r :: EffectRow).
Members '[PersistPath !! PersistPathError, Stop PersistError] r =>
Path Rel File
-> Path Rel Dir -> Maybe (Path Rel File) -> Sem r (Path Abs File)
filepath Path Rel File
singleFile Path Rel Dir
dir Maybe (Path Rel File)
subpath = do
Path Abs Dir
base <- Sem r (Path Abs Dir)
forall (r :: EffectRow).
Members '[PersistPath !! PersistPathError, Stop PersistError] r =>
Sem r (Path Abs Dir)
persistBase
pure (Path Abs Dir
base Path Abs Dir -> Path Rel File -> Path Abs File
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel File
-> (Path Rel File -> Path Rel File)
-> Maybe (Path Rel File)
-> Path Rel File
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Path Rel File
singleFile (Path Rel Dir
dir Path Rel Dir -> Path Rel File -> Path Rel File
forall b t. Path b Dir -> Path Rel t -> Path b t
</>) Maybe (Path Rel File)
subpath)
interpretPersist ::
ToJSON a =>
FromJSON a =>
Members [PersistPath !! PersistPathError, Error BootError, Log, Embed IO] r =>
Text ->
InterpreterFor (Persist a !! PersistError) r
interpretPersist :: forall a (r :: EffectRow).
(ToJSON a, FromJSON a,
Members
'[PersistPath !! PersistPathError, Error BootError, Log, Embed IO]
r) =>
Text -> InterpreterFor (Persist a !! PersistError) r
interpretPersist Text
name =
Sem r (Path Rel File, Path Rel Dir)
-> ((Path Rel File, Path Rel Dir)
-> InterpreterFor (Persist a !! PersistError) r)
-> InterpreterFor (Persist a !! PersistError) r
forall (r :: EffectRow) a (eff :: (* -> *) -> * -> *).
Sem r a -> (a -> InterpreterFor eff r) -> InterpreterFor eff r
with Sem r (Path Rel File, Path Rel Dir)
parse \ (Path Rel File
singleFile, Path Rel Dir
dir) ->
(forall x (r0 :: EffectRow).
Persist a (Sem r0) x -> Sem (Stop PersistError : r) x)
-> InterpreterFor (Persist a !! PersistError) r
forall err (eff :: (* -> *) -> * -> *) (r :: EffectRow).
FirstOrder eff "interpretResumable" =>
(forall x (r0 :: EffectRow).
eff (Sem r0) x -> Sem (Stop err : r) x)
-> InterpreterFor (Resumable err eff) r
interpretResumable \case
Persist.Store Maybe (Path Rel File)
subpath a
a -> do
Path Abs File
path <- Path Rel File
-> Path Rel Dir
-> Maybe (Path Rel File)
-> Sem (Stop PersistError : r) (Path Abs File)
forall (r :: EffectRow).
Members '[PersistPath !! PersistPathError, Stop PersistError] r =>
Path Rel File
-> Path Rel Dir -> Maybe (Path Rel File) -> Sem r (Path Abs File)
filepath Path Rel File
singleFile Path Rel Dir
dir Maybe (Path Rel File)
subpath
let base :: Path Abs Dir
base = Path Abs File -> Path Abs Dir
forall b t. Path b t -> Path b Dir
parent Path Abs File
path
PersistError -> Maybe () -> Sem (Stop PersistError : r) ()
forall err (r :: EffectRow) a.
Member (Stop err) r =>
err -> Maybe a -> Sem r a
stopNote (Text -> PersistError
PersistError.Permission (Path Abs Dir -> Text
forall b t. Path b t -> Text
pathText Path Abs Dir
base)) (Maybe () -> Sem (Stop PersistError : r) ())
-> Sem (Stop PersistError : r) (Maybe ())
-> Sem (Stop PersistError : r) ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO () -> Sem (Stop PersistError : r) (Maybe ())
forall (r :: EffectRow) a.
Member (Embed IO) r =>
IO a -> Sem r (Maybe a)
tryMaybe (Bool -> Path Abs Dir -> IO ()
forall (m :: * -> *) b. MonadIO m => Bool -> Path b Dir -> m ()
createDirIfMissing Bool
True Path Abs Dir
base)
IO () -> Sem (Stop PersistError : r) ()
forall (m :: * -> *) (r :: EffectRow) a.
Member (Embed m) r =>
m a -> Sem r a
embed (String -> a -> IO ()
forall a. ToJSON a => String -> a -> IO ()
encodeFile (Path Abs File -> String
forall b t. Path b t -> String
toFilePath Path Abs File
path) a
a)
Persist.Load Maybe (Path Rel File)
subpath -> do
Path Abs File
path <- Path Rel File
-> Path Rel Dir
-> Maybe (Path Rel File)
-> Sem (Stop PersistError : r) (Path Abs File)
forall (r :: EffectRow).
Members '[PersistPath !! PersistPathError, Stop PersistError] r =>
Path Rel File
-> Path Rel Dir -> Maybe (Path Rel File) -> Sem r (Path Abs File)
filepath Path Rel File
singleFile Path Rel Dir
dir Maybe (Path Rel File)
subpath
Text -> Sem (Stop PersistError : r) ()
forall (r :: EffectRow).
(HasCallStack, Member Log r) =>
Text -> Sem r ()
Log.debug [exon|Persistence path requested: #{show path}|]
Sem (Stop PersistError : r) Bool
-> Sem (Stop PersistError : r) (Maybe a)
-> Sem (Stop PersistError : r) (Maybe a)
-> Sem (Stop PersistError : r) (Maybe a)
forall (m :: * -> *) a. Monad m => m Bool -> m a -> m a -> m a
ifM (Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool)
-> Sem (Stop PersistError : r) (Maybe Bool)
-> Sem (Stop PersistError : r) Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO Bool -> Sem (Stop PersistError : r) (Maybe Bool)
forall (r :: EffectRow) a.
Member (Embed IO) r =>
IO a -> Sem r (Maybe a)
tryMaybe (Path Abs File -> IO Bool
forall (m :: * -> *) b. MonadIO m => Path b File -> m Bool
doesFileExist Path Abs File
path)) (Path Abs File -> Sem (Stop PersistError : r) (Maybe a)
forall a (r :: EffectRow).
(FromJSON a, Members '[Stop PersistError, Log, Embed IO] r) =>
Path Abs File -> Sem r a
loadFile Path Abs File
path) (Maybe a -> Sem (Stop PersistError : r) (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
forall a. Maybe a
Nothing)
where
parse :: Sem r (Path Rel File, Path Rel Dir)
parse =
BootError
-> Maybe (Path Rel File, Path Rel Dir)
-> Sem r (Path Rel File, Path Rel Dir)
forall e (r :: EffectRow) a.
Member (Error e) r =>
e -> Maybe a -> Sem r a
note (Text -> BootError
BootError [exon|Invalid persist name: #{name}|]) Maybe (Path Rel File, Path Rel Dir)
namePaths
namePaths :: Maybe (Path Rel File, Path Rel Dir)
namePaths =
(,) (Path Rel File -> Path Rel Dir -> (Path Rel File, Path Rel Dir))
-> Maybe (Path Rel File)
-> Maybe (Path Rel Dir -> (Path Rel File, Path Rel Dir))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> Maybe (Path Rel File)
forall (m :: * -> *). MonadThrow m => String -> m (Path Rel File)
parseRelFile (Text -> String
forall a. ToString a => a -> String
toString [exon|#{name}.json|]) Maybe (Path Rel Dir -> (Path Rel File, Path Rel Dir))
-> Maybe (Path Rel Dir) -> Maybe (Path Rel File, Path Rel Dir)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> String -> Maybe (Path Rel Dir)
forall (m :: * -> *). MonadThrow m => String -> m (Path Rel Dir)
parseRelDir (Text -> String
forall a. ToString a => a -> String
toString Text
name)
interpretPersistNull ::
∀ a err r .
InterpreterFor (Persist a !! err) r
interpretPersistNull :: forall a err (r :: EffectRow). InterpreterFor (Persist a !! err) r
interpretPersistNull =
(forall x (r0 :: EffectRow).
Persist a (Sem r0) x -> Sem (Stop err : r) x)
-> InterpreterFor (Resumable err (Persist a)) r
forall err (eff :: (* -> *) -> * -> *) (r :: EffectRow).
FirstOrder eff "interpretResumable" =>
(forall x (r0 :: EffectRow).
eff (Sem r0) x -> Sem (Stop err : r) x)
-> InterpreterFor (Resumable err eff) r
interpretResumable \case
Persist.Store Maybe (Path Rel File)
_ a
_ ->
Sem (Stop err : r) x
forall (f :: * -> *). Applicative f => f ()
unit
Persist.Load Maybe (Path Rel File)
_ ->
Maybe a -> Sem (Stop err : r) (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
forall a. Maybe a
Nothing