-- | Execute handlers for specific communication patterns
--
-- See also "Network.GRPC.Common.StreamType" as well as
-- "Network.GRPC.Client.StreamType.CanCallRPC".
module Network.GRPC.Client.StreamType.IO (
    nonStreaming
  , clientStreaming
  , clientStreaming_
  , serverStreaming
  , biDiStreaming
  ) where

import Control.Monad.Reader

import Network.GRPC.Client
import Network.GRPC.Client.StreamType.CanCallRPC qualified as CanCallRPC
import Network.GRPC.Common
import Network.GRPC.Common.StreamType
import Network.GRPC.Spec

{-------------------------------------------------------------------------------
  Run client handlers

  We piggy-back on the definitions for a general monad stack with an instance of
  'CanCallRPC', but /run/ in a 'ReaderT' monad stack which satisfies that
  constraint. The caller can then just provide an explicit 'Connection'.
-------------------------------------------------------------------------------}

-- | Make a non-streaming RPC
--
-- Example usage:
--
-- > type GetFeature = Protobuf RouteGuide "getFeature"
-- >
-- > getFeature :: Connection -> Point -> IO ()
-- > getFeature conn point = do
-- >     features <- nonStreaming conn (rpc @GetFeature) point
-- >     print features
nonStreaming :: forall rpc m.
     Connection
  -> ClientHandler' NonStreaming (ReaderT Connection m) rpc
  -> Input rpc
  -> m (Output rpc)
nonStreaming :: forall {k} (rpc :: k) (m :: * -> *).
Connection
-> ClientHandler' 'NonStreaming (ReaderT Connection m) rpc
-> Input rpc
-> m (Output rpc)
nonStreaming Connection
conn ClientHandler' 'NonStreaming (ReaderT Connection m) rpc
h Input rpc
input = (ReaderT Connection m (Output rpc) -> Connection -> m (Output rpc))
-> Connection
-> ReaderT Connection m (Output rpc)
-> m (Output rpc)
forall a b c. (a -> b -> c) -> b -> a -> c
flip ReaderT Connection m (Output rpc) -> Connection -> m (Output rpc)
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT Connection
conn (ReaderT Connection m (Output rpc) -> m (Output rpc))
-> ReaderT Connection m (Output rpc) -> m (Output rpc)
forall a b. (a -> b) -> a -> b
$
    ClientHandler' 'NonStreaming (ReaderT Connection m) rpc
-> Input rpc -> ReaderT Connection m (Output rpc)
forall {k} (rpc :: k) (m :: * -> *).
ClientHandler' 'NonStreaming m rpc -> Input rpc -> m (Output rpc)
CanCallRPC.nonStreaming ClientHandler' 'NonStreaming (ReaderT Connection m) rpc
h Input rpc
input

-- | Generalization of 'clientStreaming_' with an additional result.
clientStreaming :: forall rpc m r.
     MonadIO m
  => Connection
  -> ClientHandler' ClientStreaming (ReaderT Connection m) rpc
  -> (    (NextElem (Input rpc) -> m ())
       -> m r
     )
  -> m (Output rpc, r)
clientStreaming :: forall {k} (rpc :: k) (m :: * -> *) r.
MonadIO m =>
Connection
-> ClientHandler' 'ClientStreaming (ReaderT Connection m) rpc
-> ((NextElem (Input rpc) -> m ()) -> m r)
-> m (Output rpc, r)
clientStreaming Connection
conn ClientHandler' 'ClientStreaming (ReaderT Connection m) rpc
h (NextElem (Input rpc) -> m ()) -> m r
k = (ReaderT Connection m (Output rpc, r)
 -> Connection -> m (Output rpc, r))
-> Connection
-> ReaderT Connection m (Output rpc, r)
-> m (Output rpc, r)
forall a b c. (a -> b -> c) -> b -> a -> c
flip ReaderT Connection m (Output rpc, r)
-> Connection -> m (Output rpc, r)
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT Connection
conn (ReaderT Connection m (Output rpc, r) -> m (Output rpc, r))
-> ReaderT Connection m (Output rpc, r) -> m (Output rpc, r)
forall a b. (a -> b) -> a -> b
$
    ClientHandler' 'ClientStreaming (ReaderT Connection m) rpc
-> ((NextElem (Input rpc) -> IO ()) -> ReaderT Connection m r)
-> ReaderT Connection m (Output rpc, r)
forall {k} (rpc :: k) (m :: * -> *) r.
ClientHandler' 'ClientStreaming m rpc
-> ((NextElem (Input rpc) -> IO ()) -> m r) -> m (Output rpc, r)
CanCallRPC.clientStreaming ClientHandler' 'ClientStreaming (ReaderT Connection m) rpc
h (((NextElem (Input rpc) -> IO ()) -> ReaderT Connection m r)
 -> ReaderT Connection m (Output rpc, r))
-> ((NextElem (Input rpc) -> IO ()) -> ReaderT Connection m r)
-> ReaderT Connection m (Output rpc, r)
forall a b. (a -> b) -> a -> b
$ \NextElem (Input rpc) -> IO ()
send -> m r -> ReaderT Connection m r
forall (m :: * -> *) a. Monad m => m a -> ReaderT Connection m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m r -> ReaderT Connection m r) -> m r -> ReaderT Connection m r
forall a b. (a -> b) -> a -> b
$
      (NextElem (Input rpc) -> m ()) -> m r
k (IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ())
-> (NextElem (Input rpc) -> IO ()) -> NextElem (Input rpc) -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NextElem (Input rpc) -> IO ()
send)

-- | Make a client-side streaming RPC
--
-- Example usage:
--
-- > type RecordRoute = Protobuf RouteGuide "recordRoute"
-- >
-- > recordRoute :: Connection -> [Point] -> IO ()
-- > recordRoute conn points = do
-- >     summary <- clientStreaming_ conn (rpc @RecordRoute) $ \send ->
-- >                  forM_ points send
-- >     print summary
clientStreaming_ :: forall rpc m.
     MonadIO m
  => Connection
  -> ClientHandler' ClientStreaming (ReaderT Connection m) rpc
  -> (   (NextElem (Input rpc) -> m ())
       -> m ()
     )
  -> m (Output rpc)
clientStreaming_ :: forall {k} (rpc :: k) (m :: * -> *).
MonadIO m =>
Connection
-> ClientHandler' 'ClientStreaming (ReaderT Connection m) rpc
-> ((NextElem (Input rpc) -> m ()) -> m ())
-> m (Output rpc)
clientStreaming_ Connection
conn ClientHandler' 'ClientStreaming (ReaderT Connection m) rpc
h (NextElem (Input rpc) -> m ()) -> m ()
k = (Output rpc, ()) -> Output rpc
forall a b. (a, b) -> a
fst ((Output rpc, ()) -> Output rpc)
-> m (Output rpc, ()) -> m (Output rpc)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Connection
-> ClientHandler' 'ClientStreaming (ReaderT Connection m) rpc
-> ((NextElem (Input rpc) -> m ()) -> m ())
-> m (Output rpc, ())
forall {k} (rpc :: k) (m :: * -> *) r.
MonadIO m =>
Connection
-> ClientHandler' 'ClientStreaming (ReaderT Connection m) rpc
-> ((NextElem (Input rpc) -> m ()) -> m r)
-> m (Output rpc, r)
clientStreaming Connection
conn ClientHandler' 'ClientStreaming (ReaderT Connection m) rpc
h (NextElem (Input rpc) -> m ()) -> m ()
k

-- | Make a server-side streaming RPC
--
-- Example usage:
--
-- > type ListFeatures = Protobuf RouteGuide "listFeatures"
-- >
-- > listFeatures :: Connection -> Rectangle -> IO ()
-- > listFeatures conn rect =
-- >     serverStreaming conn (rpc @ListFeatures) rect $ \recv ->
-- >       whileJust_ recv print
serverStreaming :: forall rpc m r.
     MonadIO m
  => Connection
  -> ClientHandler' ServerStreaming (ReaderT Connection m) rpc
  -> Input rpc
  -> (    m (NextElem (Output rpc))
       -> m r
     )
  -> m r
serverStreaming :: forall {k} (rpc :: k) (m :: * -> *) r.
MonadIO m =>
Connection
-> ClientHandler' 'ServerStreaming (ReaderT Connection m) rpc
-> Input rpc
-> (m (NextElem (Output rpc)) -> m r)
-> m r
serverStreaming Connection
conn ClientHandler' 'ServerStreaming (ReaderT Connection m) rpc
h Input rpc
input m (NextElem (Output rpc)) -> m r
k = (ReaderT Connection m r -> Connection -> m r)
-> Connection -> ReaderT Connection m r -> m r
forall a b c. (a -> b -> c) -> b -> a -> c
flip ReaderT Connection m r -> Connection -> m r
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT Connection
conn (ReaderT Connection m r -> m r) -> ReaderT Connection m r -> m r
forall a b. (a -> b) -> a -> b
$
    ClientHandler' 'ServerStreaming (ReaderT Connection m) rpc
-> Input rpc
-> (IO (NextElem (Output rpc)) -> ReaderT Connection m r)
-> ReaderT Connection m r
forall {k} (rpc :: k) (m :: * -> *) r.
Functor m =>
ClientHandler' 'ServerStreaming m rpc
-> Input rpc -> (IO (NextElem (Output rpc)) -> m r) -> m r
CanCallRPC.serverStreaming ClientHandler' 'ServerStreaming (ReaderT Connection m) rpc
h Input rpc
input ((IO (NextElem (Output rpc)) -> ReaderT Connection m r)
 -> ReaderT Connection m r)
-> (IO (NextElem (Output rpc)) -> ReaderT Connection m r)
-> ReaderT Connection m r
forall a b. (a -> b) -> a -> b
$ \IO (NextElem (Output rpc))
recv -> m r -> ReaderT Connection m r
forall (m :: * -> *) a. Monad m => m a -> ReaderT Connection m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m r -> ReaderT Connection m r) -> m r -> ReaderT Connection m r
forall a b. (a -> b) -> a -> b
$
      m (NextElem (Output rpc)) -> m r
k (IO (NextElem (Output rpc)) -> m (NextElem (Output rpc))
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO (NextElem (Output rpc))
recv)

-- | Make a bidirectional RPC
--
-- Example usage:
--
-- > type RouteChat = Protobuf RouteGuide "routeChat"
-- >
-- > routeChat :: Connection -> [RouteNote] -> IO ()
-- > routeChat conn notes =
-- >     biDiStreaming conn (rpc @RouteChat) $ \send recv ->
-- >       forM_ notes $ \note -> do
-- >         send note
-- >         print =<< recv
biDiStreaming :: forall rpc m r.
     MonadIO m
  => Connection
  -> ClientHandler' BiDiStreaming (ReaderT Connection m) rpc
  -> (    (NextElem (Input rpc) -> m ())
       -> m (NextElem (Output rpc))
       -> m r
     )
  -> m r
biDiStreaming :: forall {k} (rpc :: k) (m :: * -> *) r.
MonadIO m =>
Connection
-> ClientHandler' 'BiDiStreaming (ReaderT Connection m) rpc
-> ((NextElem (Input rpc) -> m ())
    -> m (NextElem (Output rpc)) -> m r)
-> m r
biDiStreaming Connection
conn ClientHandler' 'BiDiStreaming (ReaderT Connection m) rpc
h (NextElem (Input rpc) -> m ()) -> m (NextElem (Output rpc)) -> m r
k = (ReaderT Connection m r -> Connection -> m r)
-> Connection -> ReaderT Connection m r -> m r
forall a b c. (a -> b -> c) -> b -> a -> c
flip ReaderT Connection m r -> Connection -> m r
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT Connection
conn (ReaderT Connection m r -> m r) -> ReaderT Connection m r -> m r
forall a b. (a -> b) -> a -> b
$
    ClientHandler' 'BiDiStreaming (ReaderT Connection m) rpc
-> ((NextElem (Input rpc) -> IO ())
    -> IO (NextElem (Output rpc)) -> ReaderT Connection m r)
-> ReaderT Connection m r
forall {k} (rpc :: k) (m :: * -> *) r.
Functor m =>
ClientHandler' 'BiDiStreaming m rpc
-> ((NextElem (Input rpc) -> IO ())
    -> IO (NextElem (Output rpc)) -> m r)
-> m r
CanCallRPC.biDiStreaming ClientHandler' 'BiDiStreaming (ReaderT Connection m) rpc
h (((NextElem (Input rpc) -> IO ())
  -> IO (NextElem (Output rpc)) -> ReaderT Connection m r)
 -> ReaderT Connection m r)
-> ((NextElem (Input rpc) -> IO ())
    -> IO (NextElem (Output rpc)) -> ReaderT Connection m r)
-> ReaderT Connection m r
forall a b. (a -> b) -> a -> b
$ \NextElem (Input rpc) -> IO ()
send IO (NextElem (Output rpc))
recv -> m r -> ReaderT Connection m r
forall (m :: * -> *) a. Monad m => m a -> ReaderT Connection m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m r -> ReaderT Connection m r) -> m r -> ReaderT Connection m r
forall a b. (a -> b) -> a -> b
$
      (NextElem (Input rpc) -> m ()) -> m (NextElem (Output rpc)) -> m r
k (IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ())
-> (NextElem (Input rpc) -> IO ()) -> NextElem (Input rpc) -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NextElem (Input rpc) -> IO ()
send) (IO (NextElem (Output rpc)) -> m (NextElem (Output rpc))
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO (NextElem (Output rpc))
recv)