packages feed

yaftee-conduit-bytestring-0.1.0.1: src/Control/Monad/Yaftee/Pipe/ByteString/Lazy.hs

{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE BlockArguments, LambdaCase, OverloadedStrings #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE RequiredTypeArguments #-}
{-# LANGUAGE KindSignatures, TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Control.Monad.Yaftee.Pipe.ByteString.Lazy (

	-- * FROM/TO

	from, to,

	-- * HANDLE

	hGet, hGet', hPutStr, hPutStr',

	-- * LENGTH

	lengthRun, length, length',
	Length, lengthToByteString, lengthFromByteString

	) where

import Prelude hiding (length)
import Control.Monad
import Control.Monad.Fix
import Control.Monad.Yaftee.Eff qualified as Eff
import Control.Monad.Yaftee.Pipe qualified as Pipe
import Control.Monad.Yaftee.State qualified as State
import Control.Monad.Yaftee.IO qualified as IO
import Control.Monad.HigherFreer qualified as F
import Control.HigherOpenUnion qualified as U
import Data.HigherFunctor qualified as HFunctor
import Data.Bits
import Data.Maybe
import Data.Bool
import Data.Int
import Data.ByteString qualified as BS
import Data.ByteString.Lazy qualified as LBS
import System.IO (Handle, hIsEOF)

hGet :: (U.Member Pipe.P es, U.Base IO.I es) =>
	Int -> Handle -> Eff.E es i LBS.ByteString ()
hGet bfsz h = fix \go ->  Eff.effBase (not <$> hIsEOF h) >>= bool (pure ())
	(Eff.effBase (BS.hGetSome h bfsz) >>= Pipe.yield . LBS.fromStrict >> go)

hGet' :: (U.Member Pipe.P es, U.Base IO.I es) =>
	Int -> Handle -> Eff.E es i (Maybe LBS.ByteString) ()
hGet' bfsz h = fix \go -> Eff.effBase (not <$> hIsEOF h) >>= bool
	(Pipe.yield Nothing)
	(Eff.effBase (BS.hGetSome h bfsz)
		>>= Pipe.yield . Just . LBS.fromStrict >> go)

hPutStr :: (U.Member Pipe.P es, U.Base IO.I es) =>
	Handle -> Eff.E es LBS.ByteString o r
hPutStr h = fix \go -> (>> go) $ Eff.effBase . LBS.hPut h =<< Pipe.await

hPutStr' :: (U.Member Pipe.P es, U.Base IO.I es) =>
	Handle -> Eff.E es LBS.ByteString o ()
hPutStr' h = fix \go -> Pipe.isMore  >>= bool (pure ())
	((>> go) $ Eff.effBase . LBS.hPut h =<< Pipe.await)

lengthRun :: forall nm es i o r . HFunctor.Loose (U.U es) =>
	Eff.E (State.Named nm Length ': es) i o r -> Eff.E es i o (r, Length)
lengthRun = (`State.runN` (0 :: Length))

length :: forall nm -> (
	U.Member Pipe.P es,
	U.Member (State.Named nm Length) es ) =>
	Eff.E es LBS.ByteString LBS.ByteString r
length nm = forever $ Pipe.await >>= \bs ->
	State.modifyN nm (+ Length (LBS.length bs)) >> Pipe.yield bs

length' :: forall nm -> (
	U.Member Pipe.P es,
	U.Member (State.Named nm Length) es ) =>
	Eff.E es LBS.ByteString LBS.ByteString ()
length' nm = fix \go -> Pipe.awaitMaybe >>= \case
	Nothing -> pure ()
	Just bs -> (>> go)
		$ State.modifyN nm (+ Length (LBS.length bs)) >> Pipe.yield bs

newtype Length = Length { unLength :: Int64 }
	deriving (Show, Eq, Ord, Enum, Num, Real, Integral)

lengthToByteString :: Int -> Length -> LBS.ByteString
lengthToByteString n0 = LBS.pack . go n0 . unLength
	where
	go n _ | n < 1 = []
	go n ln = fromIntegral ln : go (n - 1) (ln `shiftR` 8)

lengthFromByteString :: Int -> LBS.ByteString -> Maybe Length
lengthFromByteString n0 = (Length <$>) . go n0 . LBS.unpack
	where
	go 0 [] = Just 0
	go n (w : ws)
		| n > 0 = (fromIntegral w .|.) . (`shiftL` 8) <$> go (n - 1) ws
	go _ _ = Nothing

from :: U.Member Pipe.P es => Int64 -> LBS.ByteString -> Eff.E es i LBS.ByteString ()
from _ "" = pure ()
from n s = Pipe.yield t >> from n d
	where (t, d) = LBS.splitAt n s

to :: HFunctor.Tight (U.U es) => Eff.E (Pipe.P ': es) i LBS.ByteString r -> Eff.E es i o LBS.ByteString
to p = (fromJust <$>) . Pipe.run $ fromPure . snd <$> p Pipe.=$= fix \go ->
	Pipe.isMore >>= bool (pure "") (LBS.append <$> Pipe.await <*> go)

fromPure :: F.H h i o a -> a
fromPure = \case F.Pure x -> x; _ -> error "not Pure"