packages feed

atelier-core-0.1.0.0: src/Atelier/Effects/File.hs

{-# OPTIONS_GHC -Wno-redundant-constraints #-}

-- | A handle-based file IO effect.
--
-- A thin 'Eff' wrapper over the handle operations in "System.IO" and
-- "Data.Text.IO" ('withFile', 'hPutText', 'hGetLine', …), so file IO passes
-- through the effect system instead of raw 'IO'. 'Handle' and 'BufferMode' are
-- re-exported for convenience.
module Atelier.Effects.File
    ( File
    , Handle
    , BufferMode (..)
    , withFile
    , hClose
    , hFlush
    , hGetLine
    , hPutText
    , hPutTextLn
    , hPutLBs
    , hPutLBsLn
    , hIsEOF
    , hSetBuffering
    , runFile
    ) where

import Effectful (Dispatch (..), DispatchOf, Effect, IOE)
import Effectful.Dispatch.Static
    ( SideEffects (..)
    , StaticRep
    , evalStaticRep
    , unsafeEff_
    , unsafeSeqUnliftIO
    )
import System.IO (BufferMode (..), Handle)
import Prelude

import Data.ByteString.Lazy.Char8 qualified as LB8
import Data.Text.IO qualified as TIO
import System.IO qualified as IO


-- | Operations concerning file handles.
data File :: Effect


type instance DispatchOf File = Static WithSideEffects
data instance StaticRep File = File


-- | Lifted `System.IO.withFile`:
withFile :: (File :> es, HasCallStack) => FilePath -> IOMode -> (Handle -> Eff es a) -> Eff es a
withFile fp m f = unsafeSeqUnliftIO \unlift -> IO.withFile fp m (unlift . f)


-- | Lifted `System.IO.hClose`.
hClose :: (File :> es, HasCallStack) => Handle -> Eff es ()
hClose = unsafeEff_ . IO.hClose


-- | Lifted `System.IO.hFlush`.
hFlush :: (File :> es, HasCallStack) => Handle -> Eff es ()
hFlush = unsafeEff_ . IO.hFlush


-- | Lifted `System.IO.hGetLine`.
hGetLine :: (File :> es, HasCallStack) => Handle -> Eff es Text
hGetLine = unsafeEff_ . fmap toText . IO.hGetLine


-- | Lifted `Data.Text.IO.hPutStr`. Encodes via the handle's text encoding,
-- symmetric with `hGetLine`.
hPutText :: (File :> es, HasCallStack) => Handle -> Text -> Eff es ()
hPutText h = unsafeEff_ . TIO.hPutStr h


-- | `hPutText` with a `\n` at the end.
hPutTextLn :: (File :> es, HasCallStack) => Handle -> Text -> Eff es ()
hPutTextLn h = unsafeEff_ . TIO.hPutStrLn h


-- | Lifted `System.IO.hIsEOF`.
hIsEOF :: (File :> es, HasCallStack) => Handle -> Eff es Bool
hIsEOF = unsafeEff_ . IO.hIsEOF


-- | Lifted `Data.ByteString.Lazy.Char8.hPutStr`.
hPutLBs :: (File :> es, HasCallStack) => Handle -> LByteString -> Eff es ()
hPutLBs h s = unsafeEff_ $ LB8.hPutStr h s


-- | Lifted `System.IO.hSetBuffering`.
hSetBuffering :: (File :> es, HasCallStack) => Handle -> BufferMode -> Eff es ()
hSetBuffering h m = unsafeEff_ $ IO.hSetBuffering h m


-- | `hPutLBs` with a `\n` at the end.
hPutLBsLn :: (File :> es, HasCallStack) => Handle -> LByteString -> Eff es ()
hPutLBsLn h = hPutLBs h . (<> "\n")


-- | Run file operations.
runFile :: (HasCallStack, IOE :> es) => Eff (File : es) a -> Eff es a
runFile = evalStaticRep File