effectful-zoo-0.0.6.0: components/core/Effectful/Zoo/FileSystem.hs
module Effectful.Zoo.FileSystem
( FileSystem,
JsonDecodeError(..),
YamlDecodeError(..),
appendStringFile,
createDirectoryIfMissing,
readByteStringFile,
readLazyByteStringFile,
readStringFile,
readJsonFile,
readYamlFile,
writeStringFile,
getCanonicalTemporaryDirectory,
createTempDirectory,
removePathForcibly,
doesFileExist,
doesDirectoryExist,
runFileSystem,
getCurrentDirectory,
listDirectory,
canonicalizePath,
) where
import Data.Aeson (FromJSON)
import Data.Aeson qualified as J
import Data.ByteString qualified as BS
import Data.ByteString.Lazy qualified as LBS
import Data.Text qualified as T
import Data.Yaml qualified as Y
import Effectful
import Effectful.Dispatch.Static
import Effectful.FileSystem (FileSystem, runFileSystem)
import Effectful.Zoo.Core
import Effectful.Zoo.Error.Static
import Effectful.Zoo.Exception
import Effectful.Zoo.Log.Static
import Effectful.Zoo.Unsafe
import HaskellWorks.Error.Types.JsonDecodeError
import HaskellWorks.Error.Types.YamlDecodeError
import HaskellWorks.Prelude
import System.Directory qualified as D
import System.IO qualified as IO
import System.IO.Temp qualified as IO
readStringFile :: ()
=> HasCallStack
=> r <: Error IOException
=> r <: FileSystem
=> r <: Log Text
=> FilePath
-> Eff r String
readStringFile filePath = withFrozenCallStack do
info $ "Reading bytestring from file: " <> T.pack filePath
unsafeFileSystemEff_ (IO.readFile filePath)
& trapIO @IOException throw
readLazyByteStringFile :: ()
=> HasCallStack
=> r <: Error IOException
=> r <: FileSystem
=> r <: Log Text
=> FilePath
-> Eff r LBS.ByteString
readLazyByteStringFile filePath = withFrozenCallStack do
info $ "Reading bytestring from file: " <> T.pack filePath
unsafeFileSystemEff_ (LBS.readFile filePath)
& trapIO @IOException throw
readByteStringFile :: ()
=> HasCallStack
=> r <: Error IOException
=> r <: FileSystem
=> r <: Log Text
=> FilePath
-> Eff r ByteString
readByteStringFile filePath = withFrozenCallStack do
info $ "Reading bytestring from file: " <> T.pack filePath
unsafeFileSystemEff_ (BS.readFile filePath)
& trapIO @IOException throw
-- | Read the 'filePath' file as JSON. Use @readJsonFile \@'Value'@ to decode into 'Value'.
readJsonFile :: forall a r. ()
=> FromJSON a
=> HasCallStack
=> r <: Error IOException
=> r <: Error JsonDecodeError
=> r <: FileSystem
=> r <: Log Text
=> FilePath
-> Eff r a
readJsonFile filePath = withFrozenCallStack do
info $ "Reading JSON file: " <> T.pack filePath
contents <- readLazyByteStringFile filePath
J.eitherDecode contents
& onLeft (throw . newJsonDecodeError)
-- | Read the 'filePath' file as YAML.
readYamlFile :: forall a r. ()
=> FromJSON a
=> HasCallStack
=> r <: Error IOException
=> r <: Error YamlDecodeError
=> r <: FileSystem
=> r <: Log Text
=> FilePath
-> Eff r a
readYamlFile filePath = withFrozenCallStack do
info $ "Reading YAML file: " <> T.pack filePath
contents <- LBS.toStrict <$> readLazyByteStringFile filePath
Y.decodeEither' contents
& onLeft (throw . YamlDecodeError . T.pack . Y.prettyPrintParseException)
writeStringFile :: ()
=> HasCallStack
=> r <: Error IOException
=> r <: FileSystem
=> r <: Log Text
=> FilePath
-> String
-> Eff r ()
writeStringFile filePath contents = withFrozenCallStack do
info $ "Writing string to file: " <> T.pack filePath
unsafeFileSystemEff_ (IO.writeFile filePath contents)
& trapIO @IOException throw
appendStringFile :: ()
=> HasCallStack
=> r <: Error IOException
=> r <: FileSystem
=> r <: Log Text
=> FilePath
-> String
-> Eff r ()
appendStringFile filePath contents = withFrozenCallStack do
info $ "Appending string to file: " <> T.pack filePath
unsafeFileSystemEff_ (IO.appendFile filePath contents)
& trapIO @IOException throw
getCanonicalTemporaryDirectory :: ()
=> HasCallStack
=> r <: Error IOException
=> r <: FileSystem
=> Eff r FilePath
getCanonicalTemporaryDirectory = withFrozenCallStack do
unsafeFileSystemEff_ IO.getCanonicalTemporaryDirectory
& trapIO @IOException throw
{-# INLINE getCanonicalTemporaryDirectory #-}
createTempDirectory :: ()
=> HasCallStack
=> r <: Error IOException
=> r <: FileSystem
=> FilePath
-> String
-> Eff r FilePath
createTempDirectory fp template = withFrozenCallStack do
unsafeFileSystemEff_ (IO.createTempDirectory fp template)
& trapIO @IOException throw
{-# INLINE createTempDirectory #-}
removePathForcibly :: ()
=> HasCallStack
=> r <: Error IOException
=> r <: FileSystem
=> r <: Log Text
=> FilePath
-> Eff r ()
removePathForcibly fp = withFrozenCallStack $ do
info $ "Calling: removePathForcibly " <> tshow fp
unsafeFileSystemEff_ (D.removePathForcibly fp)
& trapIO @IOException throw
doesFileExist :: ()
=> HasCallStack
=> r <: Error IOException
=> r <: FileSystem
=> r <: Log Text
=> FilePath
-> Eff r Bool
doesFileExist fp = withFrozenCallStack $ do
info "Calling: doesFileExist"
unsafeFileSystemEff_ (D.doesFileExist fp)
& trapIO @IOException throw
doesDirectoryExist :: ()
=> HasCallStack
=> r <: Error IOException
=> r <: FileSystem
=> r <: Log Text
=> FilePath
-> Eff r Bool
doesDirectoryExist fp = withFrozenCallStack $ do
info "Calling: doesDirectoryExist"
unsafeFileSystemEff_ (D.doesDirectoryExist fp)
& trapIO @IOException throw
getCurrentDirectory :: ()
=> HasCallStack
=> r <: Error IOException
=> r <: FileSystem
=> Eff r FilePath
getCurrentDirectory = withFrozenCallStack do
unsafeFileSystemEff_ D.getCurrentDirectory
& trapIO @IOException throw
{-# INLINE getCurrentDirectory #-}
canonicalizePath :: ()
=> HasCallStack
=> r <: Error IOException
=> r <: FileSystem
=> FilePath
-> Eff r FilePath
canonicalizePath fp = withFrozenCallStack do
unsafeFileSystemEff_ (D.canonicalizePath fp)
& trapIO @IOException throw
{-# INLINE canonicalizePath #-}
listDirectory :: ()
=> HasCallStack
=> r <: Error IOException
=> r <: FileSystem
=> r <: Log Text
=> FilePath
-> Eff r [FilePath]
listDirectory fp = withFrozenCallStack do
info $ "Calling: listDirectory " <> tshow fp
unsafeFileSystemEff_ (D.listDirectory fp)
& trapIO @IOException throw
{-# INLINE listDirectory #-}
createDirectoryIfMissing :: ()
=> HasCallStack
=> r <: Error IOException
=> r <: FileSystem
=> r <: Log Text
=> Bool
-> FilePath
-> Eff r ()
createDirectoryIfMissing createParents fp = withFrozenCallStack do
info $ "Calling: createDirectoryIfMissing " <> tshow fp
unsafeFileSystemEff_ (D.createDirectoryIfMissing createParents fp)
& trapIO @IOException throw
{-# INLINE createDirectoryIfMissing #-}