packages feed

seihou-core-0.4.0.0: src/Seihou/Effect/ManifestStoreInterp.hs

module Seihou.Effect.ManifestStoreInterp
  ( runManifestStore,
  )
where

import Control.Monad (unless)
import Data.ByteString.Lazy qualified as LBS
import Data.Text qualified as T
import Data.Text.Encoding qualified as TE
import Seihou.Effect.Filesystem (Filesystem, createDirectoryIfMissing, doesFileExist, readFileText, renamePath, writeFileText)
import Seihou.Effect.ManifestStore (ManifestStore (..))
import Seihou.Manifest.Types (manifestFromJSON, manifestToJSON)
import Seihou.Prelude
import System.FilePath (takeDirectory)

-- | Real interpreter for the ManifestStore effect.
-- Reads and writes manifest JSON via the Filesystem effect.
-- Write is atomic within one filesystem: writes to a temp path in the
-- manifest directory, then renames that complete file over the final path.
runManifestStore ::
  (Filesystem :> es) =>
  FilePath ->
  Eff (ManifestStore : es) a ->
  Eff es a
runManifestStore manifestPath = interpret $ \_ -> \case
  ReadManifest -> do
    exists <- doesFileExist manifestPath
    if not exists
      then pure (Right Nothing)
      else do
        content <- readFileText manifestPath
        let bs = LBS.fromStrict (TE.encodeUtf8 content)
        case manifestFromJSON bs of
          Left err -> pure (Left (T.pack err))
          Right manifest -> pure (Right (Just manifest))
  WriteManifest manifest -> do
    let bs = manifestToJSON manifest
        content = TE.decodeUtf8 (LBS.toStrict bs)
        tmpPath = manifestPath <> ".tmp"
        parentDir = takeDirectory manifestPath
    unless (parentDir == "." || null parentDir) $
      createDirectoryIfMissing True parentDir
    writeFileText tmpPath content
    renamePath tmpPath manifestPath