-- | Provides the MonadFileReader effect.
--
-- @since 0.1
module Effects.FileSystem.FileReader
  ( -- * Effect
    MonadFileReader (..),
    OsPath,

    -- * UTF-8 Utils
    readFileUtf8,
    readFileUtf8Lenient,
    readFileUtf8ThrowM,
    FS.UTF8.decodeUtf8,
    FS.UTF8.decodeUtf8Lenient,
    FS.UTF8.decodeUtf8ThrowM,

    -- * Reexports
    ByteString,
    Text,
    UnicodeException,
  )
where

import Control.Monad ((>=>))
import Control.Monad.Catch (MonadThrow)
import Control.Monad.Trans.Class (MonadTrans (lift))
import Control.Monad.Trans.Reader (ReaderT)
import Data.ByteString (ByteString)
import Data.Text (Text)
import Data.Text.Encoding.Error (UnicodeException)
import FileSystem.IO qualified as FS.IO
import FileSystem.OsPath (OsPath)
import FileSystem.UTF8 qualified as FS.UTF8
import GHC.Stack (HasCallStack)

-- | Represents file-system reader effects.
--
-- @since 0.1
class (Monad m) => MonadFileReader m where
  -- | Reads a file.
  --
  -- @since 0.1
  readBinaryFile :: (HasCallStack) => OsPath -> m ByteString

-- | @since 0.1
instance MonadFileReader IO where
  readBinaryFile :: HasCallStack => OsPath -> IO ByteString
readBinaryFile = OsPath -> IO ByteString
FS.IO.readBinaryFileIO
  {-# INLINEABLE readBinaryFile #-}

-- | @since 0.1
instance (MonadFileReader m) => MonadFileReader (ReaderT e m) where
  readBinaryFile :: HasCallStack => OsPath -> ReaderT e m ByteString
readBinaryFile = m ByteString -> ReaderT e m ByteString
forall (m :: * -> *) a. Monad m => m a -> ReaderT e m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m ByteString -> ReaderT e m ByteString)
-> (OsPath -> m ByteString) -> OsPath -> ReaderT e m ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. OsPath -> m ByteString
forall (m :: * -> *).
(MonadFileReader m, HasCallStack) =>
OsPath -> m ByteString
readBinaryFile
  {-# INLINEABLE readBinaryFile #-}

-- | Reads a file as UTF-8.
--
-- @since 0.1
readFileUtf8 ::
  ( HasCallStack,
    MonadFileReader m
  ) =>
  OsPath ->
  m (Either UnicodeException Text)
readFileUtf8 :: forall (m :: * -> *).
(HasCallStack, MonadFileReader m) =>
OsPath -> m (Either UnicodeException Text)
readFileUtf8 = (ByteString -> Either UnicodeException Text)
-> m ByteString -> m (Either UnicodeException Text)
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Either UnicodeException Text
FS.UTF8.decodeUtf8 (m ByteString -> m (Either UnicodeException Text))
-> (OsPath -> m ByteString)
-> OsPath
-> m (Either UnicodeException Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. OsPath -> m ByteString
forall (m :: * -> *).
(MonadFileReader m, HasCallStack) =>
OsPath -> m ByteString
readBinaryFile
{-# INLINEABLE readFileUtf8 #-}

-- | Reads a file as UTF-8 in lenient mode.
--
-- @since 0.1
readFileUtf8Lenient ::
  ( HasCallStack,
    MonadFileReader m
  ) =>
  OsPath ->
  m Text
readFileUtf8Lenient :: forall (m :: * -> *).
(HasCallStack, MonadFileReader m) =>
OsPath -> m Text
readFileUtf8Lenient = (ByteString -> Text) -> m ByteString -> m Text
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
FS.UTF8.decodeUtf8Lenient (m ByteString -> m Text)
-> (OsPath -> m ByteString) -> OsPath -> m Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. OsPath -> m ByteString
forall (m :: * -> *).
(MonadFileReader m, HasCallStack) =>
OsPath -> m ByteString
readBinaryFile
{-# INLINEABLE readFileUtf8Lenient #-}

-- | Decodes a file as UTF-8. Throws 'UnicodeException' for decode errors.
--
-- @since 0.1
readFileUtf8ThrowM ::
  ( HasCallStack,
    MonadFileReader m,
    MonadThrow m
  ) =>
  OsPath ->
  m Text
readFileUtf8ThrowM :: forall (m :: * -> *).
(HasCallStack, MonadFileReader m, MonadThrow m) =>
OsPath -> m Text
readFileUtf8ThrowM = OsPath -> m ByteString
forall (m :: * -> *).
(MonadFileReader m, HasCallStack) =>
OsPath -> m ByteString
readBinaryFile (OsPath -> m ByteString)
-> (ByteString -> m Text) -> OsPath -> m Text
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> ByteString -> m Text
forall (m :: * -> *).
(HasCallStack, MonadThrow m) =>
ByteString -> m Text
FS.UTF8.decodeUtf8ThrowM
{-# INLINEABLE readFileUtf8ThrowM #-}