{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE KindSignatures      #-}
{-# LANGUAGE LambdaCase          #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Network.GRPC.HighLevel.Client
  ( ClientError(..)
  , ClientRegisterable(..)
  , ClientRequest(..)
  , ClientResult(..)
  , GRPCMethodType(..)
  , MetadataMap(..)
  , RegisteredMethod
  , ServiceClient
  , StatusCode(..)
  , StatusDetails(..)
  , StreamRecv
  , StreamSend
  , TimeoutSeconds
  , WritesDone

  , LL.Client
  , LL.ClientConfig(..)
  , LL.ClientSSLConfig(..)
  , LL.ClientSSLKeyCertPair(..)
  , LL.Host(..)
  , LL.Port(..)

  , clientRequest

  -- * Client utility functions
  , acquireClient
  , simplifyServerStreaming
  , simplifyUnary
  )

where

import           Control.Monad.Managed                 (Managed, liftIO,
                                                        managed)
import qualified Data.ByteString.Lazy                  as BL
import           Network.GRPC.HighLevel.Server         (convertRecv,
                                                        convertSend)
import           Network.GRPC.LowLevel                 (GRPCIOError (..),
                                                        GRPCMethodType (..),
                                                        MetadataMap (..),
                                                        StatusCode (..),
                                                        StatusDetails (..),
                                                        StreamRecv, StreamSend)
import qualified Network.GRPC.LowLevel                 as LL
import           Network.GRPC.LowLevel.CompletionQueue (TimeoutSeconds)
import           Network.GRPC.LowLevel.Op              (WritesDone)
import           Proto3.Suite                          (Message, fromByteString,
                                                        toLazyByteString)
import           Proto3.Wire.Decode                    (ParseError)

newtype RegisteredMethod (mt :: GRPCMethodType) request response
  = RegisteredMethod (LL.RegisteredMethod mt)
  deriving Int -> RegisteredMethod mt request response -> ShowS
[RegisteredMethod mt request response] -> ShowS
RegisteredMethod mt request response -> String
(Int -> RegisteredMethod mt request response -> ShowS)
-> (RegisteredMethod mt request response -> String)
-> ([RegisteredMethod mt request response] -> ShowS)
-> Show (RegisteredMethod mt request response)
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
forall (mt :: GRPCMethodType) request response.
Int -> RegisteredMethod mt request response -> ShowS
forall (mt :: GRPCMethodType) request response.
[RegisteredMethod mt request response] -> ShowS
forall (mt :: GRPCMethodType) request response.
RegisteredMethod mt request response -> String
showList :: [RegisteredMethod mt request response] -> ShowS
$cshowList :: forall (mt :: GRPCMethodType) request response.
[RegisteredMethod mt request response] -> ShowS
show :: RegisteredMethod mt request response -> String
$cshow :: forall (mt :: GRPCMethodType) request response.
RegisteredMethod mt request response -> String
showsPrec :: Int -> RegisteredMethod mt request response -> ShowS
$cshowsPrec :: forall (mt :: GRPCMethodType) request response.
Int -> RegisteredMethod mt request response -> ShowS
Show

type ServiceClient service = service ClientRequest ClientResult

data ClientError
  = ClientErrorNoParse ParseError
  | ClientIOError GRPCIOError
  deriving (Int -> ClientError -> ShowS
[ClientError] -> ShowS
ClientError -> String
(Int -> ClientError -> ShowS)
-> (ClientError -> String)
-> ([ClientError] -> ShowS)
-> Show ClientError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ClientError] -> ShowS
$cshowList :: [ClientError] -> ShowS
show :: ClientError -> String
$cshow :: ClientError -> String
showsPrec :: Int -> ClientError -> ShowS
$cshowsPrec :: Int -> ClientError -> ShowS
Show, ClientError -> ClientError -> Bool
(ClientError -> ClientError -> Bool)
-> (ClientError -> ClientError -> Bool) -> Eq ClientError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ClientError -> ClientError -> Bool
$c/= :: ClientError -> ClientError -> Bool
== :: ClientError -> ClientError -> Bool
$c== :: ClientError -> ClientError -> Bool
Eq)

data ClientRequest (streamType :: GRPCMethodType) request response where
  ClientNormalRequest :: request -> TimeoutSeconds -> MetadataMap -> ClientRequest 'Normal request response
  ClientWriterRequest :: TimeoutSeconds -> MetadataMap -> (StreamSend request -> IO ()) -> ClientRequest 'ClientStreaming request response
  -- | The final field will be invoked once, and it should repeatedly
  -- invoke its final argument (of type @(StreamRecv response)@)
  -- in order to obtain the streaming response incrementally.
  ClientReaderRequest :: request -> TimeoutSeconds -> MetadataMap -> (LL.ClientCall -> MetadataMap -> StreamRecv response -> IO ()) -> ClientRequest 'ServerStreaming request response
  ClientBiDiRequest :: TimeoutSeconds -> MetadataMap -> (LL.ClientCall -> MetadataMap -> StreamRecv response -> StreamSend request -> WritesDone -> IO ()) -> ClientRequest 'BiDiStreaming request response

data ClientResult (streamType :: GRPCMethodType) response where
  ClientNormalResponse :: response -> MetadataMap -> MetadataMap -> StatusCode -> StatusDetails -> ClientResult 'Normal response
  ClientWriterResponse :: Maybe response -> MetadataMap -> MetadataMap -> StatusCode -> StatusDetails -> ClientResult 'ClientStreaming response
  ClientReaderResponse :: MetadataMap -> StatusCode -> StatusDetails -> ClientResult 'ServerStreaming response
  ClientBiDiResponse   :: MetadataMap -> StatusCode -> StatusDetails -> ClientResult 'BiDiStreaming response
  ClientErrorResponse  :: ClientError -> ClientResult streamType response

class ClientRegisterable (methodType :: GRPCMethodType) where
  clientRegisterMethod :: LL.Client
                       -> LL.MethodName
                       -> IO (RegisteredMethod methodType request response)

instance ClientRegisterable 'Normal where
  clientRegisterMethod :: Client
-> MethodName -> IO (RegisteredMethod 'Normal request response)
clientRegisterMethod Client
client MethodName
methodName =
    RegisteredMethod 'Normal
-> RegisteredMethod 'Normal request response
forall (mt :: GRPCMethodType) request response.
RegisteredMethod mt -> RegisteredMethod mt request response
RegisteredMethod (RegisteredMethod 'Normal
 -> RegisteredMethod 'Normal request response)
-> IO (RegisteredMethod 'Normal)
-> IO (RegisteredMethod 'Normal request response)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Client -> MethodName -> IO (RegisteredMethod 'Normal)
LL.clientRegisterMethodNormal Client
client MethodName
methodName

instance ClientRegisterable 'ClientStreaming where
  clientRegisterMethod :: Client
-> MethodName
-> IO (RegisteredMethod 'ClientStreaming request response)
clientRegisterMethod Client
client MethodName
methodName =
    RegisteredMethod 'ClientStreaming
-> RegisteredMethod 'ClientStreaming request response
forall (mt :: GRPCMethodType) request response.
RegisteredMethod mt -> RegisteredMethod mt request response
RegisteredMethod (RegisteredMethod 'ClientStreaming
 -> RegisteredMethod 'ClientStreaming request response)
-> IO (RegisteredMethod 'ClientStreaming)
-> IO (RegisteredMethod 'ClientStreaming request response)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Client -> MethodName -> IO (RegisteredMethod 'ClientStreaming)
LL.clientRegisterMethodClientStreaming Client
client MethodName
methodName

instance ClientRegisterable 'ServerStreaming where
  clientRegisterMethod :: Client
-> MethodName
-> IO (RegisteredMethod 'ServerStreaming request response)
clientRegisterMethod Client
client MethodName
methodName =
    RegisteredMethod 'ServerStreaming
-> RegisteredMethod 'ServerStreaming request response
forall (mt :: GRPCMethodType) request response.
RegisteredMethod mt -> RegisteredMethod mt request response
RegisteredMethod (RegisteredMethod 'ServerStreaming
 -> RegisteredMethod 'ServerStreaming request response)
-> IO (RegisteredMethod 'ServerStreaming)
-> IO (RegisteredMethod 'ServerStreaming request response)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Client -> MethodName -> IO (RegisteredMethod 'ServerStreaming)
LL.clientRegisterMethodServerStreaming Client
client MethodName
methodName

instance ClientRegisterable 'BiDiStreaming where
  clientRegisterMethod :: Client
-> MethodName
-> IO (RegisteredMethod 'BiDiStreaming request response)
clientRegisterMethod Client
client MethodName
methodName =
    RegisteredMethod 'BiDiStreaming
-> RegisteredMethod 'BiDiStreaming request response
forall (mt :: GRPCMethodType) request response.
RegisteredMethod mt -> RegisteredMethod mt request response
RegisteredMethod (RegisteredMethod 'BiDiStreaming
 -> RegisteredMethod 'BiDiStreaming request response)
-> IO (RegisteredMethod 'BiDiStreaming)
-> IO (RegisteredMethod 'BiDiStreaming request response)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Client -> MethodName -> IO (RegisteredMethod 'BiDiStreaming)
LL.clientRegisterMethodBiDiStreaming Client
client MethodName
methodName

clientRequest :: (Message request, Message response) =>
                 LL.Client -> RegisteredMethod streamType request response
              -> ClientRequest streamType request response -> IO (ClientResult streamType response)
clientRequest :: Client
-> RegisteredMethod streamType request response
-> ClientRequest streamType request response
-> IO (ClientResult streamType response)
clientRequest Client
client (RegisteredMethod RegisteredMethod streamType
method) (ClientNormalRequest request
req Int
timeout MetadataMap
meta) =
    Either GRPCIOError NormalRequestResult
-> ClientResult 'Normal response
forall response.
Message response =>
Either GRPCIOError NormalRequestResult
-> ClientResult 'Normal response
mkResponse (Either GRPCIOError NormalRequestResult
 -> ClientResult 'Normal response)
-> IO (Either GRPCIOError NormalRequestResult)
-> IO (ClientResult 'Normal response)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Client
-> RegisteredMethod 'Normal
-> Int
-> ByteString
-> MetadataMap
-> IO (Either GRPCIOError NormalRequestResult)
LL.clientRequest Client
client RegisteredMethod streamType
RegisteredMethod 'Normal
method Int
timeout (ByteString -> ByteString
BL.toStrict (request -> ByteString
forall a. Message a => a -> ByteString
toLazyByteString request
req)) MetadataMap
meta
  where
    mkResponse :: Either GRPCIOError NormalRequestResult
-> ClientResult 'Normal response
mkResponse (Left GRPCIOError
ioError_) = ClientError -> ClientResult 'Normal response
forall (streamType :: GRPCMethodType) response.
ClientError -> ClientResult streamType response
ClientErrorResponse (GRPCIOError -> ClientError
ClientIOError GRPCIOError
ioError_)
    mkResponse (Right NormalRequestResult
rsp) =
      case ByteString -> Either ParseError response
forall a. Message a => ByteString -> Either ParseError a
fromByteString (NormalRequestResult -> ByteString
LL.rspBody NormalRequestResult
rsp) of
        Left ParseError
err -> ClientError -> ClientResult 'Normal response
forall (streamType :: GRPCMethodType) response.
ClientError -> ClientResult streamType response
ClientErrorResponse (ParseError -> ClientError
ClientErrorNoParse ParseError
err)
        Right response
parsedRsp ->
          response
-> MetadataMap
-> MetadataMap
-> StatusCode
-> StatusDetails
-> ClientResult 'Normal response
forall response.
response
-> MetadataMap
-> MetadataMap
-> StatusCode
-> StatusDetails
-> ClientResult 'Normal response
ClientNormalResponse response
parsedRsp (NormalRequestResult -> MetadataMap
LL.initMD NormalRequestResult
rsp) (NormalRequestResult -> MetadataMap
LL.trailMD NormalRequestResult
rsp) (NormalRequestResult -> StatusCode
LL.rspCode NormalRequestResult
rsp) (NormalRequestResult -> StatusDetails
LL.details NormalRequestResult
rsp)
clientRequest Client
client (RegisteredMethod RegisteredMethod streamType
method) (ClientWriterRequest Int
timeout MetadataMap
meta StreamSend request -> IO ()
handler) =
    Either
  GRPCIOError
  (Maybe ByteString, MetadataMap, MetadataMap, StatusCode,
   StatusDetails)
-> ClientResult 'ClientStreaming response
forall response.
Message response =>
Either
  GRPCIOError
  (Maybe ByteString, MetadataMap, MetadataMap, StatusCode,
   StatusDetails)
-> ClientResult 'ClientStreaming response
mkResponse (Either
   GRPCIOError
   (Maybe ByteString, MetadataMap, MetadataMap, StatusCode,
    StatusDetails)
 -> ClientResult 'ClientStreaming response)
-> IO
     (Either
        GRPCIOError
        (Maybe ByteString, MetadataMap, MetadataMap, StatusCode,
         StatusDetails))
-> IO (ClientResult 'ClientStreaming response)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Client
-> RegisteredMethod 'ClientStreaming
-> Int
-> MetadataMap
-> ClientWriterHandler
-> IO
     (Either
        GRPCIOError
        (Maybe ByteString, MetadataMap, MetadataMap, StatusCode,
         StatusDetails))
LL.clientWriter Client
client RegisteredMethod streamType
RegisteredMethod 'ClientStreaming
method Int
timeout MetadataMap
meta (StreamSend request -> IO ()
handler (StreamSend request -> IO ())
-> (StreamSend ByteString -> StreamSend request)
-> ClientWriterHandler
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StreamSend ByteString -> StreamSend request
forall a. Message a => StreamSend ByteString -> StreamSend a
convertSend)
  where
    mkResponse :: Either
  GRPCIOError
  (Maybe ByteString, MetadataMap, MetadataMap, StatusCode,
   StatusDetails)
-> ClientResult 'ClientStreaming response
mkResponse (Left GRPCIOError
ioError_) = ClientError -> ClientResult 'ClientStreaming response
forall (streamType :: GRPCMethodType) response.
ClientError -> ClientResult streamType response
ClientErrorResponse (GRPCIOError -> ClientError
ClientIOError GRPCIOError
ioError_)
    mkResponse (Right (Maybe ByteString
rsp_, MetadataMap
initMD_, MetadataMap
trailMD_, StatusCode
rspCode_, StatusDetails
details_)) =
      case Either ParseError (Maybe response)
-> (ByteString -> Either ParseError (Maybe response))
-> Maybe ByteString
-> Either ParseError (Maybe response)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Maybe response -> Either ParseError (Maybe response)
forall a b. b -> Either a b
Right Maybe response
forall a. Maybe a
Nothing) ((response -> Maybe response)
-> Either ParseError response -> Either ParseError (Maybe response)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap response -> Maybe response
forall a. a -> Maybe a
Just (Either ParseError response -> Either ParseError (Maybe response))
-> (ByteString -> Either ParseError response)
-> ByteString
-> Either ParseError (Maybe response)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Either ParseError response
forall a. Message a => ByteString -> Either ParseError a
fromByteString) Maybe ByteString
rsp_ of
        Left ParseError
err -> ClientError -> ClientResult 'ClientStreaming response
forall (streamType :: GRPCMethodType) response.
ClientError -> ClientResult streamType response
ClientErrorResponse (ParseError -> ClientError
ClientErrorNoParse ParseError
err)
        Right Maybe response
parsedRsp ->
          Maybe response
-> MetadataMap
-> MetadataMap
-> StatusCode
-> StatusDetails
-> ClientResult 'ClientStreaming response
forall response.
Maybe response
-> MetadataMap
-> MetadataMap
-> StatusCode
-> StatusDetails
-> ClientResult 'ClientStreaming response
ClientWriterResponse Maybe response
parsedRsp MetadataMap
initMD_ MetadataMap
trailMD_ StatusCode
rspCode_ StatusDetails
details_
clientRequest Client
client (RegisteredMethod RegisteredMethod streamType
method) (ClientReaderRequest request
req Int
timeout MetadataMap
meta ClientCall -> MetadataMap -> StreamRecv response -> IO ()
handler) =
    Either GRPCIOError (MetadataMap, StatusCode, StatusDetails)
-> ClientResult 'ServerStreaming response
forall response.
Either GRPCIOError (MetadataMap, StatusCode, StatusDetails)
-> ClientResult 'ServerStreaming response
mkResponse (Either GRPCIOError (MetadataMap, StatusCode, StatusDetails)
 -> ClientResult 'ServerStreaming response)
-> IO (Either GRPCIOError (MetadataMap, StatusCode, StatusDetails))
-> IO (ClientResult 'ServerStreaming response)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Client
-> RegisteredMethod 'ServerStreaming
-> Int
-> ByteString
-> MetadataMap
-> ClientReaderHandler
-> IO (Either GRPCIOError (MetadataMap, StatusCode, StatusDetails))
LL.clientReader Client
client RegisteredMethod streamType
RegisteredMethod 'ServerStreaming
method Int
timeout (ByteString -> ByteString
BL.toStrict (request -> ByteString
forall a. Message a => a -> ByteString
toLazyByteString request
req)) MetadataMap
meta (\ClientCall
cc MetadataMap
m StreamRecv ByteString
recv -> ClientCall -> MetadataMap -> StreamRecv response -> IO ()
handler ClientCall
cc MetadataMap
m (StreamRecv ByteString -> StreamRecv response
forall a. Message a => StreamRecv ByteString -> StreamRecv a
convertRecv StreamRecv ByteString
recv))
  where
    mkResponse :: Either GRPCIOError (MetadataMap, StatusCode, StatusDetails)
-> ClientResult 'ServerStreaming response
mkResponse (Left GRPCIOError
ioError_) = ClientError -> ClientResult 'ServerStreaming response
forall (streamType :: GRPCMethodType) response.
ClientError -> ClientResult streamType response
ClientErrorResponse (GRPCIOError -> ClientError
ClientIOError GRPCIOError
ioError_)
    mkResponse (Right (MetadataMap
meta_, StatusCode
rspCode_, StatusDetails
details_)) =
      MetadataMap
-> StatusCode
-> StatusDetails
-> ClientResult 'ServerStreaming response
forall response.
MetadataMap
-> StatusCode
-> StatusDetails
-> ClientResult 'ServerStreaming response
ClientReaderResponse MetadataMap
meta_ StatusCode
rspCode_ StatusDetails
details_
clientRequest Client
client (RegisteredMethod RegisteredMethod streamType
method) (ClientBiDiRequest Int
timeout MetadataMap
meta ClientCall
-> MetadataMap
-> StreamRecv response
-> StreamSend request
-> WritesDone
-> IO ()
handler) =
    Either GRPCIOError (MetadataMap, StatusCode, StatusDetails)
-> ClientResult 'BiDiStreaming response
forall response.
Either GRPCIOError (MetadataMap, StatusCode, StatusDetails)
-> ClientResult 'BiDiStreaming response
mkResponse (Either GRPCIOError (MetadataMap, StatusCode, StatusDetails)
 -> ClientResult 'BiDiStreaming response)
-> IO (Either GRPCIOError (MetadataMap, StatusCode, StatusDetails))
-> IO (ClientResult 'BiDiStreaming response)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Client
-> RegisteredMethod 'BiDiStreaming
-> Int
-> MetadataMap
-> ClientRWHandler
-> IO (Either GRPCIOError (MetadataMap, StatusCode, StatusDetails))
LL.clientRW Client
client RegisteredMethod streamType
RegisteredMethod 'BiDiStreaming
method Int
timeout MetadataMap
meta (\ClientCall
cc IO (Either GRPCIOError MetadataMap)
_m StreamRecv ByteString
recv StreamSend ByteString
send WritesDone
writesDone -> ClientCall
-> MetadataMap
-> StreamRecv response
-> StreamSend request
-> WritesDone
-> IO ()
handler ClientCall
cc MetadataMap
meta (StreamRecv ByteString -> StreamRecv response
forall a. Message a => StreamRecv ByteString -> StreamRecv a
convertRecv StreamRecv ByteString
recv) (StreamSend ByteString -> StreamSend request
forall a. Message a => StreamSend ByteString -> StreamSend a
convertSend StreamSend ByteString
send) WritesDone
writesDone)
  where
    mkResponse :: Either GRPCIOError (MetadataMap, StatusCode, StatusDetails)
-> ClientResult 'BiDiStreaming response
mkResponse (Left GRPCIOError
ioError_) = ClientError -> ClientResult 'BiDiStreaming response
forall (streamType :: GRPCMethodType) response.
ClientError -> ClientResult streamType response
ClientErrorResponse (GRPCIOError -> ClientError
ClientIOError GRPCIOError
ioError_)
    mkResponse (Right (MetadataMap
meta_, StatusCode
rspCode_, StatusDetails
details_)) =
      MetadataMap
-> StatusCode
-> StatusDetails
-> ClientResult 'BiDiStreaming response
forall response.
MetadataMap
-> StatusCode
-> StatusDetails
-> ClientResult 'BiDiStreaming response
ClientBiDiResponse MetadataMap
meta_ StatusCode
rspCode_ StatusDetails
details_

acquireClient
  :: LL.ClientConfig
     -- ^ The client configuration (host, port, SSL settings, etc)
  -> (LL.Client -> IO (ServiceClient service))
     -- ^ The client implementation (typically generated)
  -> Managed (ServiceClient service)
acquireClient :: ClientConfig
-> (Client -> IO (ServiceClient service))
-> Managed (ServiceClient service)
acquireClient ClientConfig
cfg Client -> IO (ServiceClient service)
impl = do
  GRPC
g <- (forall r. (GRPC -> IO r) -> IO r) -> Managed GRPC
forall (m :: * -> *) a.
MonadManaged m =>
(forall r. (a -> IO r) -> IO r) -> m a
managed forall r. (GRPC -> IO r) -> IO r
LL.withGRPC
  Client
c <- (forall r. (Client -> IO r) -> IO r) -> Managed Client
forall (m :: * -> *) a.
MonadManaged m =>
(forall r. (a -> IO r) -> IO r) -> m a
managed (GRPC -> ClientConfig -> (Client -> IO r) -> IO r
forall a. GRPC -> ClientConfig -> (Client -> IO a) -> IO a
LL.withClient GRPC
g ClientConfig
cfg)
  IO (ServiceClient service) -> Managed (ServiceClient service)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (Client -> IO (ServiceClient service)
impl Client
c)

-- | A utility for simplifying server-streaming gRPC client requests; you can
-- use this to avoid 'ClientRequest' and 'ClientResult' pattern-matching
-- boilerplate at call sites.
simplifyServerStreaming :: TimeoutSeconds
                           -- ^ RPC call timeout, in seconds
                        -> MetadataMap
                           -- ^ RPC call metadata
                        -> (ClientError -> IO StatusDetails)
                           -- ^ Handler for client errors
                        -> (StatusCode -> StatusDetails -> IO StatusDetails)
                           -- ^ Handler for non-StatusOk response
                        -> (ClientRequest 'ServerStreaming request response
                            -> IO (ClientResult 'ServerStreaming response))
                           -- ^ Endpoint implementation (typically generated by grpc-haskell)
                        -> request
                           -- ^ Request payload
                        -> (LL.ClientCall -> MetadataMap -> StreamRecv response -> IO ())
                           -- ^ Stream handler; note that the 'StreamRecv'
                           -- action must be called repeatedly in order to
                           -- consume the stream
                        -> IO StatusDetails
simplifyServerStreaming :: Int
-> MetadataMap
-> (ClientError -> IO StatusDetails)
-> (StatusCode -> StatusDetails -> IO StatusDetails)
-> (ClientRequest 'ServerStreaming request response
    -> IO (ClientResult 'ServerStreaming response))
-> request
-> (ClientCall -> MetadataMap -> StreamRecv response -> IO ())
-> IO StatusDetails
simplifyServerStreaming Int
timeout MetadataMap
meta ClientError -> IO StatusDetails
clientError StatusCode -> StatusDetails -> IO StatusDetails
nonStatusOkError ClientRequest 'ServerStreaming request response
-> IO (ClientResult 'ServerStreaming response)
f request
x ClientCall -> MetadataMap -> StreamRecv response -> IO ()
handler = do

  let request :: ClientRequest 'ServerStreaming request response
request = request
-> Int
-> MetadataMap
-> (ClientCall -> MetadataMap -> StreamRecv response -> IO ())
-> ClientRequest 'ServerStreaming request response
forall request response.
request
-> Int
-> MetadataMap
-> (ClientCall -> MetadataMap -> StreamRecv response -> IO ())
-> ClientRequest 'ServerStreaming request response
ClientReaderRequest request
x Int
timeout MetadataMap
meta ClientCall -> MetadataMap -> StreamRecv response -> IO ()
handler

  ClientResult 'ServerStreaming response
response <- ClientRequest 'ServerStreaming request response
-> IO (ClientResult 'ServerStreaming response)
f ClientRequest 'ServerStreaming request response
request

  case ClientResult 'ServerStreaming response
response of
    ClientReaderResponse MetadataMap
_ StatusCode
StatusOk StatusDetails
details
      -> StatusDetails -> IO StatusDetails
forall (f :: * -> *) a. Applicative f => a -> f a
pure StatusDetails
details

    ClientReaderResponse MetadataMap
_ StatusCode
statusCode StatusDetails
details
      -> StatusCode -> StatusDetails -> IO StatusDetails
nonStatusOkError StatusCode
statusCode StatusDetails
details

    ClientErrorResponse ClientError
err
      -> ClientError -> IO StatusDetails
clientError ClientError
err

-- | A utility for simplifying unary gRPC client requests; you can use this to
-- avoid 'ClientRequest' and 'ClientResult' pattern-matching boilerplate at
-- call sites.
simplifyUnary :: TimeoutSeconds
                -- ^ RPC call timeout, in seconds
              -> MetadataMap
                 -- ^ RPC call metadata
              -> (ClientError -> IO (response, StatusDetails))
                 -- ^ Handler for client errors
              -> (response -> StatusCode -> StatusDetails -> IO (response, StatusDetails))
                 -- ^ Handler for non-StatusOK responses
              -> (ClientRequest 'Normal request response -> IO (ClientResult 'Normal response))
                 -- ^ Endpoint implementation (typically generated by grpc-haskell)
              -> (request -> IO (response, StatusDetails))
                 -- ^ The simplified happy-path (StatusOk) unary call action
simplifyUnary :: Int
-> MetadataMap
-> (ClientError -> IO (response, StatusDetails))
-> (response
    -> StatusCode -> StatusDetails -> IO (response, StatusDetails))
-> (ClientRequest 'Normal request response
    -> IO (ClientResult 'Normal response))
-> request
-> IO (response, StatusDetails)
simplifyUnary Int
timeout MetadataMap
meta ClientError -> IO (response, StatusDetails)
clientError response
-> StatusCode -> StatusDetails -> IO (response, StatusDetails)
nonStatusOkError ClientRequest 'Normal request response
-> IO (ClientResult 'Normal response)
f request
x = do

  let request :: ClientRequest 'Normal request response
request = request
-> Int -> MetadataMap -> ClientRequest 'Normal request response
forall request response.
request
-> Int -> MetadataMap -> ClientRequest 'Normal request response
ClientNormalRequest request
x Int
timeout MetadataMap
meta

  ClientResult 'Normal response
response <- ClientRequest 'Normal request response
-> IO (ClientResult 'Normal response)
f ClientRequest 'Normal request response
request

  case ClientResult 'Normal response
response of
    ClientNormalResponse response
y MetadataMap
_ MetadataMap
_ StatusCode
StatusOk StatusDetails
details
      -> (response, StatusDetails) -> IO (response, StatusDetails)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (response
y, StatusDetails
details)

    ClientNormalResponse response
y MetadataMap
_ MetadataMap
_ StatusCode
code StatusDetails
details
      -> response
-> StatusCode -> StatusDetails -> IO (response, StatusDetails)
nonStatusOkError response
y StatusCode
code StatusDetails
details

    ClientErrorResponse ClientError
err
      -> ClientError -> IO (response, StatusDetails)
clientError ClientError
err