http2-grpc-proto-lens-0.1.1.0: src/Network/GRPC/HTTP2/ProtoLens.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Network.GRPC.HTTP2.ProtoLens where
import Data.Binary.Builder (fromByteString, putWord32be, singleton)
import Data.Binary.Get (getByteString, getInt8, getWord32be, runGetIncremental)
import qualified Data.ByteString.Char8 as ByteString
#if MIN_VERSION_base(4,11,0)
import Data.Kind (Type)
#else
#endif
import Data.ProtoLens.Encoding (decodeMessage, encodeMessage)
import Data.ProtoLens.Message (Message)
import Data.ProtoLens.Service.Types (HasMethod, HasMethodImpl (..), Service (..))
import Data.Proxy (Proxy (..))
import GHC.TypeLits (Symbol, symbolVal)
#if MIN_VERSION_base(4,11,0)
#else
import Data.Monoid ((<>))
#endif
import Network.GRPC.HTTP2.Encoding
import Network.GRPC.HTTP2.Types
-- | A proxy type for giving static information about RPCs.
#if MIN_VERSION_base(4,11,0)
data RPC (s :: Type) (m :: Symbol) = RPC
#else
data RPC (s :: *) (m :: Symbol) = RPC
#endif
instance (Service s, HasMethod s m) => IsRPC (RPC s m) where
path rpc = "/" <> pkg rpc Proxy <> "." <> srv rpc Proxy <> "/" <> meth rpc Proxy
where
pkg :: (Service s) => RPC s m -> Proxy (ServicePackage s) -> HeaderValue
pkg _ p = ByteString.pack $ symbolVal p
srv :: (Service s) => RPC s m -> Proxy (ServiceName s) -> HeaderValue
srv _ p = ByteString.pack $ symbolVal p
meth :: (Service s, HasMethod s m) => RPC s m -> Proxy (MethodName s m) -> HeaderValue
meth _ p = ByteString.pack $ symbolVal p
{-# INLINE path #-}
instance (Service s, HasMethod s m, i ~ MethodInput s m)
=> GRPCInput (RPC s m) i where
encodeInput _ = encode
decodeInput _ = decoder
instance (Service s, HasMethod s m, i ~ MethodOutput s m)
=> GRPCOutput (RPC s m) i where
encodeOutput _ = encode
decodeOutput _ = decoder
-- | Decoder for gRPC/HTTP2-encoded Protobuf messages.
decoder :: Message a => Compression -> Decoder (Either String a)
decoder compression = runGetIncremental $ do
isCompressed <- getInt8 -- 1byte
let decompress = if isCompressed == 0 then pure else _decompressionFunction compression
n <- getWord32be -- 4bytes
decodeMessage <$> (decompress =<< getByteString (fromIntegral n))
-- | Encodes as binary using gRPC/HTTP2 framing.
encode :: Message m => Compression -> m -> Builder
encode compression plain =
mconcat [ singleton (if _compressionByteSet compression then 1 else 0)
, putWord32be (fromIntegral $ ByteString.length bin)
, fromByteString bin
]
where
bin = _compressionFunction compression $ encodeMessage plain