packages feed

yaftee-conduit-bytestring-ft-0.1.0.0: src/Control/Monad/Yaftee/Pipe/ByteString/FingerTree/OnDemand.hs

{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE BlockArguments, LambdaCase, OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables, TypeApplications #-}
{-# LANGUAGE RequiredTypeArguments #-}
{-# LANGUAGE DataKinds, ConstraintKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Control.Monad.Yaftee.Pipe.ByteString.FingerTree.OnDemand (

	-- * RUN

	run, States,

	-- * ON DEMAND

	onDemand, onDemandWithInitial, Members, Request(..), ByteString

	) where

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.Except qualified as Except
import Control.HigherOpenUnion qualified as U
import Data.TypeLevel.List
import Data.HigherFunctor qualified as HFunctor
import Data.Bool
import Data.ByteString.FingerTree qualified as BSF

run :: forall nm es i o a . HFunctor.Loose (U.U es) =>
	Eff.E (States nm `Append` es) i o a ->
	Eff.E es i o ((a, Request), ByteString)
run = (flip (State.runN @nm) $ ByteString BSF.Empty)
	. (flip (State.runN @nm) $ RequestBuffer 100)

type States nm = '[State.Named nm Request, State.Named nm ByteString]

onDemand :: forall nm -> (
	U.Member Pipe.P es,
	Members nm es, U.Member (Except.E String) es ) =>
	Eff.E es BSF.ByteString BSF.ByteString r
onDemand nm = fix \go -> State.getN nm >>= \case
	RequestBytes ln -> takeBytes nm ln
		>>= maybe (Except.throw errne) ((>> go) . Pipe.yield)
	RequestBuffer ln -> takeBuffer nm ln
		>>= maybe (Except.throw errne) ((>> go) . Pipe.yield)
	RequestString -> takeString nm
		>>= maybe (Except.throw errne) ((>> go) . Pipe.yield)
	RequestPushBack bs ->
		State.modifyN nm (ByteString . (bs `BSF.append`) . unByteString) >>
		Pipe.yield "" >> go
	where
	errne :: String
	errne = "Not enough ByteString"

onDemandWithInitial :: forall nm -> (
	U.Member Pipe.P es,
	Members nm es, U.Member (Except.E String) es ) =>
	BSF.ByteString -> Eff.E es BSF.ByteString BSF.ByteString r
onDemandWithInitial nm ib = do
	State.putN nm $ ByteString ib
	onDemand nm

type Members nm es = (
	U.Member (State.Named nm Request) es,
	U.Member (State.Named nm ByteString) es )

data Request
	= RequestBytes Int
	| RequestBuffer Int
	| RequestString
	| RequestPushBack BSF.ByteString
	deriving Show

newtype ByteString = ByteString { unByteString :: BSF.ByteString } deriving Show

takeBytes :: forall nm -> (
	U.Member Pipe.P es,
	U.Member (State.Named nm ByteString) es ) =>
	Int -> Eff.E es BSF.ByteString o (Maybe BSF.ByteString)
takeBytes nm ln = State.getsN nm unByteString >>= \bs ->
	case BSF.splitAt' ln bs of
		Nothing -> readMore nm >>= bool (pure Nothing) (takeBytes nm ln)
		Just (t, d) -> Just t <$ State.putN nm (ByteString d)

takeBuffer :: forall nm -> (
	U.Member Pipe.P es,
	U.Member (State.Named nm ByteString) es ) =>
	Int -> Eff.E es BSF.ByteString o (Maybe BSF.ByteString)
takeBuffer nm ln = State.getsN nm unByteString >>= \bs ->
	case BSF.splitAt' ln bs of
		Nothing -> readMore nm >>= bool
			(bool	(Just bs
					<$ State.putN nm (ByteString BSF.Empty))
				(pure Nothing) (BSF.null bs))
			(takeBuffer nm ln)
		Just (t, d) -> Just t <$ State.putN nm (ByteString d)

takeString ::
	forall nm ->
	(U.Member Pipe.P es, U.Member (State.Named nm ByteString) es) =>
	Eff.E es BSF.ByteString o (Maybe BSF.ByteString)
takeString nm = State.getsN nm unByteString >>= \bs ->
	case splitString bs of
		Nothing -> readMore nm >>= bool (pure Nothing) (takeString nm)
		Just (t, d) -> Just t <$ State.putN nm (ByteString d)

splitString :: BSF.ByteString -> Maybe (BSF.ByteString, BSF.ByteString)
splitString bs = case BSF.span (/= 0) bs of
	(_, "") -> Nothing
	(t, BSF.uncons -> Just (z, d)) -> Just (BSF.snoc t z, d)
	_ -> error "Never occur"

readMore :: forall nm -> (
	U.Member Pipe.P es,
	U.Member (State.Named nm ByteString) es ) =>
	Eff.E es BSF.ByteString o Bool
readMore nm = Pipe.awaitMaybe >>= \case
	Nothing -> pure False
	Just bs -> True <$ State.modifyN nm
		(ByteString . (`BSF.append` bs) . unByteString)