{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
module Network.GRPC.HTTP2.Proto3Wire where
import Data.Bifunctor (first)
import Data.Binary.Builder (fromByteString, singleton, putWord32be)
import Data.Binary.Get (getByteString, getInt8, getWord32be, runGetIncremental)
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as ByteString
import Data.ByteString.Lazy (toStrict)
import qualified Proto3.Wire.Encode as PBEnc
import qualified Proto3.Wire.Decode as PBDec
import Network.GRPC.HTTP2.Types
import Network.GRPC.HTTP2.Encoding
data RPC = RPC { pkg :: ByteString, srv :: ByteString, meth :: ByteString }
instance IsRPC RPC where
path rpc = "/" <> pkg rpc <> "." <> srv rpc <> "/" <> meth rpc
{-# INLINE path #-}
class Proto3WireEncoder a where
proto3WireEncode :: a -> PBEnc.MessageBuilder
proto3WireDecode :: PBDec.Parser PBDec.RawMessage a
instance (Proto3WireEncoder i) => GRPCInput RPC i where
encodeInput _ = encode
decodeInput _ = decoder
instance (Proto3WireEncoder o) => GRPCOutput RPC o where
encodeOutput _ = encode
decodeOutput _ = decoder
encode :: Proto3WireEncoder 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 $ toStrict $ PBEnc.toLazyByteString (proto3WireEncode plain)
decoder :: Proto3WireEncoder a => Compression -> Decoder (Either String a)
decoder compression = runGetIncremental $ do
isCompressed <- getInt8
let decompress = if isCompressed == 0 then pure else _decompressionFunction compression
n <- getWord32be
first show . PBDec.parse proto3WireDecode <$> (decompress =<< getByteString (fromIntegral n))