packages feed

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

module Seihou.Effect.FilesystemInterp
  ( runFilesystem,
  )
where

import Control.Monad (when)
import Data.Text.IO qualified as TIO
import Seihou.Effect.Filesystem (Filesystem (..))
import Seihou.Prelude
import System.Directory qualified as Dir

-- | Real IO interpreter for the Filesystem effect.
-- Delegates to System.Directory and Data.Text.IO.
runFilesystem :: (IOE :> es) => Eff (Filesystem : es) a -> Eff es a
runFilesystem = interpret $ \_ -> \case
  ReadFileText path -> liftIO (TIO.readFile path)
  WriteFileText path content -> liftIO (TIO.writeFile path content)
  CopyFile src dest -> liftIO (Dir.copyFile src dest)
  ListDirectory path -> liftIO (Dir.listDirectory path)
  CreateDirectoryIfMissing parents path ->
    liftIO (Dir.createDirectoryIfMissing parents path)
  DoesFileExist path -> liftIO (Dir.doesFileExist path)
  DoesDirectoryExist path -> liftIO (Dir.doesDirectoryExist path)
  GetCurrentDirectory -> liftIO Dir.getCurrentDirectory
  RemoveFile path -> liftIO (Dir.removeFile path)
  RemoveDirectoryIfEmpty path -> liftIO $ do
    -- Treat a missing directory as a no-op so this matches the pure
    -- interpreter's semantics. Without this guard, callers that walk
    -- a list of "maybe-now-empty" parents (e.g. cleanupEmptyDirs in
    -- Engine.Migrate / Engine.Remove) crash whenever a sibling
    -- operation — typically a user-authored 'rm -rf' RunCommand step
    -- in a migration chain — has already removed the directory.
    exists <- Dir.doesDirectoryExist path
    when exists $ do
      entries <- Dir.listDirectory path
      when (null entries) (Dir.removeDirectory path)
  RenamePath src dest -> liftIO (Dir.renamePath src dest)
  RemoveDirectoryRecursive path -> liftIO (Dir.removeDirectoryRecursive path)