{-# LANGUAGE CPP #-}
module RIO.Prelude.IO
  ( withLazyFile
  , withLazyFileUtf8
  , readFileBinary
  , writeFileBinary
  , readFileUtf8
  , writeFileUtf8
  , hPutBuilder
  ) where

import           RIO.Prelude.Reexports
import qualified Data.ByteString         as B
import qualified Data.ByteString.Builder as BB
import qualified Data.ByteString.Lazy    as BL
import qualified Data.Text.Lazy          as TL
import qualified Data.Text.Lazy.IO       as TL
import qualified Data.Text.IO            as T
import           System.IO               (hSetEncoding, utf8)


-- | Lazily get the contents of a file. Unlike 'BL.readFile', this
-- ensures that if an exception is thrown, the file handle is closed
-- immediately.
withLazyFile :: MonadUnliftIO m => FilePath -> (BL.ByteString -> m a) -> m a
withLazyFile fp inner = withBinaryFile fp ReadMode $ inner <=< liftIO . BL.hGetContents

-- | Lazily read a file in UTF8 encoding.
--
-- @since 0.1.13
withLazyFileUtf8 :: MonadUnliftIO m => FilePath -> (TL.Text -> m a) -> m a
withLazyFileUtf8 fp inner = withFile fp ReadMode $ \h ->
  inner =<< liftIO (hSetEncoding h utf8 >> TL.hGetContents h)

-- | Write a file in UTF8 encoding
--
-- This function will use OS-specific line ending handling.
writeFileUtf8 :: MonadIO m => FilePath -> Text -> m ()
writeFileUtf8 fp text = liftIO $ withFile fp WriteMode $ \h -> do
  hSetEncoding h utf8
  T.hPutStr h text

hPutBuilder :: MonadIO m => Handle -> Builder -> m ()
hPutBuilder h = liftIO . BB.hPutBuilder h
{-# INLINE hPutBuilder #-}

-- | Same as 'B.readFile', but generalized to 'MonadIO'
readFileBinary :: MonadIO m => FilePath -> m ByteString
readFileBinary = liftIO . B.readFile

-- | Same as 'B.writeFile', but generalized to 'MonadIO'
writeFileBinary :: MonadIO m => FilePath -> ByteString -> m ()
writeFileBinary fp = liftIO . B.writeFile fp

-- | Read a file in UTF8 encoding, throwing an exception on invalid character
-- encoding.
--
-- This function will use OS-specific line ending handling.
readFileUtf8 :: MonadIO m => FilePath -> m Text
readFileUtf8 fp = liftIO $ withFile fp ReadMode $ \h -> do
  hSetEncoding h utf8
  T.hGetContents h