packages feed

polysemy-scoped-fs-0.1.0.0: src/Polysemy/FS/Scoped/Text/Internal.hs

{-|
Copyright   : (c) Hisaket VioletRed, 2022
License     : AGPL-3.0-or-later
Maintainer  : hisaket@outlook.jp
Stability   : experimental
Portability : POSIX
-}

module Polysemy.FS.Scoped.Text.Internal where

import qualified System.IO as IO
import Polysemy
    ( Member, Sem, embed, Embed, InterpretersFor, Members, interpret )
import qualified Polysemy.SequentialAccess.Text as SAT
import qualified Polysemy.SequentialAccess as SA
import Polysemy.Internal.Kind ( Append )
import Polysemy.Resource ( Resource )
import Control.Category ( (>>>) )
import Data.Functor ( (<&>) )
import Polysemy.FS.Scoped.Internal as Scoped
    ( ScopedFile, scopedFileToIO, seekToBegin, seekToEnd )
import qualified Polysemy.FS.Scoped as Scoped
import Polysemy.Internal.Sing ( KnownList )
import qualified Data.Text.IO as TIO
import qualified GHC.IO.Handle as IO


readToIO :: Member (Embed IO) r => IO.Handle -> Sem (SAT.ReadLine ': SAT.ReadToEnd ': r) a -> Sem r a
readToIO h =
        interpret (\(SA.Read SAT.Line) -> embed $ TIO.hGetLine h)
    >>> interpret
            ( \(SA.Read SA.ToEnd) ->
                embed $ (TIO.hGetContents =<< IO.hDuplicate h) <* seekToEnd h
            )

extendToIO :: Member (Embed IO) r => IO.Handle -> Sem (SAT.Extend ': r) a -> Sem r a
extendToIO h = interpret \(SA.Extend t) -> embed $ restoreCursor h $ seekToEnd h *> TIO.hPutStr h t


restoreCursor :: IO.Handle -> IO a -> IO a
restoreCursor h m = do
    before <- IO.hTell h
    m <* IO.hSeek h IO.AbsoluteSeek before

clearToIO :: Member (Embed IO) r => IO.Handle -> Sem (SAT.Clear ': r) a -> Sem r a
clearToIO h = interpret \(SA.Resize SA.NullSize) -> embed $ IO.hSetFileSize h 0

cursorToIO :: Member (Embed IO) r => IO.Handle -> Sem (Append SAT.Cursor r) a -> Sem r a
cursorToIO h =
        interpret
            ( \SA.GetPosition -> embed $
                IO.hTell h >>= \case
                    0 ->  pure $ SA.End SA.TOF
                    _ ->  IO.hIsEOF h <&> \case
                            False -> SA.Intermediate
                            True -> SA.End SA.EOF
            )
    >>> interpret
            (\(SA.Seek end) -> embed $ case end of
                SA.TOF -> seekToBegin h
                SA.EOF -> seekToEnd h
            )

scopedTextFileToIO
    ::  (Members '[Embed IO, Resource] r, KnownList es)
    =>  IO.IOMode
    ->  (IO.Handle -> InterpretersFor es r)
    ->  (∀handle'. Sem (Scoped.ScopedFile mode es b handle' ': r) a)
    ->  Sem r a
scopedTextFileToIO = Scoped.scopedFileToIO IO.openFile