servant-streamly-0.1.0.0: src/Servant/Streamly.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- | This module exports 'ToSourceIO' and 'FromSourceIO' for all 'IsStream' instances.
module Servant.Streamly
( StreamlyToSourceIO(..)
)
where
import Control.Monad.IO.Class ( MonadIO(..)
, liftIO
)
import Control.Monad.Trans.Resource ( ResourceT
, runResourceT
)
import qualified Streamly
import qualified Streamly.Prelude as Streamly
import qualified Servant.API.Stream as Servant
import qualified Servant.Types.SourceT as Servant
-- | Helper class to implement @'ToSourceIO' 'IsStream'@ instance
-- for various monads.
class StreamlyToSourceIO m where
streamlyToSourceIO :: Streamly.IsStream t => t m a -> Servant.SourceIO a
instance StreamlyToSourceIO IO where
streamlyToSourceIO stream = Servant.SourceT
($ transform $ Streamly.adapt stream)
where
transform = Servant.Effect . Streamly.foldr Servant.Yield Servant.Stop
instance StreamlyToSourceIO (ResourceT IO) where
streamlyToSourceIO stream = Servant.SourceT
($ transform $ Streamly.adapt stream)
where
transform =
Servant.Effect . runResourceT . Streamly.foldr Servant.Yield Servant.Stop
instance (StreamlyToSourceIO m, Streamly.IsStream t) => Servant.ToSourceIO a (t m a) where
toSourceIO = streamlyToSourceIO
instance (Streamly.IsStream t) => Servant.FromSourceIO a (t IO a) where
fromSourceIO src =
Streamly.concatMapM id $ Streamly.yield $ Servant.unSourceT src go
where
go :: Streamly.IsStream t => Servant.StepT IO a -> IO (t IO a)
go step = case step of
Servant.Stop -> return Streamly.nil
Servant.Error e -> return $ Streamly.yieldM $ error e
Servant.Skip n -> go n
Servant.Yield x nextStep -> Streamly.cons x <$> go nextStep
Servant.Effect nextStep -> nextStep >>= go
-- {-# SPECIALIZE INLINE fromSourceIO :: Streamly.IsStream t => Servant.SourceIO a -> t IO a #-}
instance (Streamly.IsStream t) => Servant.FromSourceIO a (t (ResourceT IO) a) where
fromSourceIO src =
Streamly.concatMapM id $ Streamly.yield $ liftIO $ Servant.unSourceT src go
where
go :: Streamly.IsStream t => Servant.StepT IO a -> IO (t (ResourceT IO) a)
go step = case step of
Servant.Stop -> return Streamly.nil
Servant.Error e -> return $ Streamly.yieldM $ error e
Servant.Skip n -> go n
Servant.Yield x nextStep -> Streamly.cons x <$> go nextStep
Servant.Effect nextStep -> nextStep >>= go