packages feed

grapesy-1.0.0: src/Network/GRPC/Common/NextElem.hs

-- | Elements in a stream (without metadata)
--
-- Intended for qualified import.
--
-- > import Network.GRPC.Common.NextElem qualified as NextElem
--
-- "Network.GRPC.Common" (intended for unqualified import) exports
-- @NextElem(..)@, but none of the operations on 'NextElem'.
module Network.GRPC.Common.NextElem (
    -- * Conversion
    toStreamElem
    -- * Iteration
  , mapM_
  , forM_
  , whileNext_
  , collect
  ) where

import Prelude hiding (mapM_)

import Control.Monad.State (StateT, execStateT, lift, modify)

import Network.GRPC.Common.StreamElem (StreamElem(..))
import Network.GRPC.Spec (NextElem(..))

{-------------------------------------------------------------------------------
  Conversion
-------------------------------------------------------------------------------}

toStreamElem :: b -> NextElem a -> StreamElem b a
toStreamElem b NoNextElem   = NoMoreElems b
toStreamElem _ (NextElem a) = StreamElem a

{-------------------------------------------------------------------------------
  Iteration
-------------------------------------------------------------------------------}

-- | Invoke the callback for each element, and then once more with 'NoNextElem'
--
-- >    mapM_ f [1,2,3]
-- > == do f (NextElem 1)
-- >       f (NextElem 2)
-- >       f (NextElem 3)
-- >       f NoNextElem
mapM_ :: forall m a. Monad m => (NextElem a -> m ()) -> [a] -> m ()
mapM_ f = go
  where
    go :: [a] -> m ()
    go []     = f NoNextElem
    go (x:xs) = f (NextElem x) >> go xs

-- | Like 'mapM_', but with the arguments in opposite order
forM_ :: Monad m => [a] -> (NextElem a -> m ()) -> m ()
forM_ = flip mapM_

-- | Invoke a function on each 'NextElem', until 'NoNextElem'
--
-- See also 'collect'.
whileNext_ :: forall m a. Monad m => m (NextElem a) -> (a -> m ()) -> m ()
whileNext_ f g = go
  where
    go :: m ()
    go = do
        ma <- f
        case ma of
          NoNextElem -> return ()
          NextElem a -> g a >> go

-- | Invoke the callback until it returns 'NoNextElem', collecting results
collect :: forall m a. Monad m => m (NextElem a) -> m [a]
collect f =
    reverse <$> flip execStateT [] aux
  where
    aux :: StateT [a] m ()
    aux = whileNext_ (lift f) $ modify . (:)