effectful-zoo-0.0.2.0: components/core/Effectful/Zoo/FileSystem.hs
module Effectful.Zoo.FileSystem
( FileSystem,
JsonDecodeError(..),
YamlDecodeError(..),
readByteStringFile,
readLazyByteStringFile,
readJsonFile,
readYamlFile,
) 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
import Effectful.Zoo.Core
import Effectful.Zoo.Core.Error.Static
import Effectful.Zoo.Core.Exception
import Effectful.Zoo.Log.Api.Text
import Effectful.Zoo.Log.Dynamic
import Effectful.Zoo.Unsafe
import HaskellWorks.Error.Types.JsonDecodeError
import HaskellWorks.Error.Types.YamlDecodeError
import HaskellWorks.Prelude
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)