{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Servant.Types.SourceT where
import Control.Monad.Except
(ExceptT (..), runExceptT, throwError)
import Control.Monad.Morph
(MFunctor (..))
import Control.Monad.Trans.Class
(MonadTrans (..))
import qualified Data.Attoparsec.ByteString as A
import qualified Data.ByteString as BS
import Data.Functor.Classes
(Show1 (..), showsBinaryWith, showsPrec1, showsUnaryWith)
import Data.Functor.Identity
(Identity (..))
import Prelude ()
import Prelude.Compat hiding
(readFile)
import System.IO
(Handle, IOMode (..), withFile)
import qualified Test.QuickCheck as QC
newtype SourceT m a = SourceT
{ unSourceT :: forall b. (StepT m a -> m b) -> m b
}
mapStepT :: (StepT m a -> StepT m b) -> SourceT m a -> SourceT m b
mapStepT f (SourceT m) = SourceT $ \k -> m (k . f)
{-# INLINE mapStepT #-}
data StepT m a
= Stop
| Error String
| Skip (StepT m a)
| Yield a (StepT m a)
| Effect (m (StepT m a))
deriving Functor
fromStepT :: StepT m a -> SourceT m a
fromStepT s = SourceT ($ s)
instance Functor m => Functor (SourceT m) where
fmap f = mapStepT (fmap f)
instance Identity ~ m => Foldable (SourceT m) where
foldr f z (SourceT m) = foldr f z (runIdentity (m Identity))
instance (Applicative m, Show1 m) => Show1 (SourceT m) where
liftShowsPrec sp sl d (SourceT m) = showsUnaryWith
(liftShowsPrec sp sl)
"fromStepT" d (Effect (m pure'))
where
pure' (Effect s) = s
pure' s = pure s
instance (Applicative m, Show1 m, Show a) => Show (SourceT m a) where
showsPrec = showsPrec1
instance MFunctor SourceT where
hoist f (SourceT m) = SourceT $ \k -> k $
Effect $ f $ fmap (hoist f) $ m return
instance Functor m => Semigroup (SourceT m a) where
SourceT withL <> SourceT withR = SourceT $ \ret ->
withL $ \l ->
withR $ \r ->
ret $ l <> r
instance Functor m => Monoid (SourceT m a) where
mempty = fromStepT mempty
mappend = (<>)
instance (QC.Arbitrary a, Monad m) => QC.Arbitrary (SourceT m a) where
arbitrary = fromStepT <$> QC.arbitrary
instance Identity ~ m => Foldable (StepT m) where
foldr f z = go where
go Stop = z
go (Error _) = z
go (Skip s) = go s
go (Yield a s) = f a (go s)
go (Effect (Identity s)) = go s
instance (Applicative m, Show1 m) => Show1 (StepT m) where
liftShowsPrec sp sl = go where
go _ Stop = showString "Stop"
go d (Skip s) = showsUnaryWith
go
"Skip" d s
go d (Error err) = showsUnaryWith
showsPrec
"Error" d err
go d (Effect ms) = showsUnaryWith
(liftShowsPrec go goList)
"Effect" d ms
go d (Yield x s) = showsBinaryWith
sp go
"Yield" d x s
goList = liftShowList sp sl
instance (Applicative m, Show1 m, Show a) => Show (StepT m a) where
showsPrec = showsPrec1
instance MonadTrans StepT where
lift = Effect . fmap (`Yield` Stop)
instance MFunctor StepT where
hoist f = go where
go Stop = Stop
go (Error err) = Error err
go (Skip s) = Skip (go s)
go (Yield x s) = Yield x (go s)
go (Effect ms) = Effect (f (fmap go ms))
instance Functor m => Semigroup (StepT m a) where
Stop <> r = r
Error err <> _ = Error err
Skip s <> r = Skip (s <> r)
Yield x s <> r = Yield x (s <> r)
Effect ms <> r = Effect ((<> r) <$> ms)
instance Functor m => Monoid (StepT m a) where
mempty = Stop
mappend = (<>)
instance (QC.Arbitrary a, Monad m) => QC.Arbitrary (StepT m a) where
arbitrary = QC.sized arb where
arb n | n <= 0 = pure Stop
| otherwise = QC.frequency
[ (1, pure Stop)
, (1, Skip <$> arb')
, (1, Effect . return <$> arb')
, (8, Yield <$> QC.arbitrary <*> arb')
]
where
arb' = arb (n - 1)
shrink Stop = []
shrink (Error _) = [Stop]
shrink (Skip s) = [s]
shrink (Effect _) = []
shrink (Yield x s) =
[ Yield x' s | x' <- QC.shrink x ] ++
[ Yield x s' | s' <- QC.shrink s ]
source :: [a] -> SourceT m a
source = fromStepT . foldr Yield Stop
runSourceT :: Monad m => SourceT m a -> ExceptT String m [a]
runSourceT (SourceT m) = ExceptT (m (runExceptT . runStepT))
runStepT :: Monad m => StepT m a -> ExceptT String m [a]
runStepT Stop = return []
runStepT (Error err) = throwError err
runStepT (Skip s) = runStepT s
runStepT (Yield x s) = fmap (x :) (runStepT s)
runStepT (Effect ms) = lift ms >>= runStepT
mapMaybe :: Functor m => (a -> Maybe b) -> SourceT m a -> SourceT m b
mapMaybe p (SourceT m) = SourceT $ \k -> m (k . mapMaybeStep p)
mapMaybeStep :: Functor m => (a -> Maybe b) -> StepT m a -> StepT m b
mapMaybeStep p = go where
go Stop = Stop
go (Error err) = Error err
go (Skip s) = Skip (go s)
go (Effect ms) = Effect (fmap go ms)
go (Yield x s) = case p x of
Nothing -> Skip (go s)
Just y -> Yield y (go s)
foreach
:: Monad m
=> (String -> m ())
-> (a -> m ())
-> SourceT m a
-> m ()
foreach f g src = unSourceT src (foreachStep f g)
foreachStep
:: Monad m
=> (String -> m ())
-> (a -> m ())
-> StepT m a
-> m ()
foreachStep f g = go where
go Stop = return ()
go (Skip s) = go s
go (Yield x s) = g x >> go s
go (Error err) = f err
go (Effect ms) = ms >>= go
fromAction :: Functor m => (a -> Bool) -> m a -> SourceT m a
fromAction stop action = SourceT ($ fromActionStep stop action)
{-# INLINE fromAction #-}
fromActionStep :: Functor m => (a -> Bool) -> m a -> StepT m a
fromActionStep stop action = loop where
loop = Effect $ fmap step action
step x
| stop x = Stop
| otherwise = Yield x loop
{-# INLINE fromActionStep #-}
readFile :: FilePath -> SourceT IO BS.ByteString
readFile fp =
SourceT $ \k ->
withFile fp ReadMode $ \hdl ->
k (readHandle hdl)
where
readHandle :: Handle -> StepT IO BS.ByteString
readHandle hdl = fromActionStep BS.null (BS.hGet hdl 4096)
transformWithAtto :: Monad m => A.Parser a -> SourceT m BS.ByteString -> SourceT m a
transformWithAtto parser = mapStepT (transformStepWithAtto parser)
transformStepWithAtto
:: forall a m. Monad m
=> A.Parser a -> StepT m BS.ByteString -> StepT m a
transformStepWithAtto parser = go (A.parse parser) where
p0 = A.parse parser
go :: (BS.ByteString -> A.Result a)
-> StepT m BS.ByteString -> StepT m a
go _ (Error err) = Error err
go p (Skip s) = Skip (go p s)
go p (Effect ms) = Effect (fmap (go p) ms)
go p Stop = case p mempty of
A.Fail _ _ err -> Error err
A.Done _ a -> Yield a Stop
A.Partial _ -> Stop
go p (Yield bs0 s) = loop p bs0 where
loop p' bs
| BS.null bs = Skip (go p' s)
| otherwise = case p' bs of
A.Fail _ _ err -> Error err
A.Done bs' a -> Yield a (loop p0 bs')
A.Partial p'' -> Skip (go p'' s)