packages feed

grpc-spec-1.0.0: test-grpc-spec/Test/Util/Protobuf.hs

-- | Utilities for working with Protobuf messages
module Test.Util.Protobuf (
    messageToExpr
  ) where

import Data.ProtoLens.Message
import Data.Proxy
import Data.Text qualified as Text
import Data.TreeDiff
import Data.TreeDiff.OMap qualified as OMap
import Control.Lens ((^.), Lens', (.~))
import Data.Map qualified as Map
import Data.Function ((&))
import Data.ProtoLens.Encoding.Wire

{-------------------------------------------------------------------------------
  TreeDiff
-------------------------------------------------------------------------------}

messageToExpr :: forall msg. Message msg => msg -> Expr
messageToExpr =
      Rec (Text.unpack $ messageName (Proxy @msg))
    . OMap.fromList
    . fields

fields :: Message msg => msg -> [(FieldName, Expr)]
fields msg = concat [
      map (knownField msg) allFields
    , map unknownField (msg ^. unknownFields)
    ]

knownField :: msg -> FieldDescriptor msg -> (FieldName, Expr)
knownField msg (FieldDescriptor name typ acc) = (
      name
    , case acc of
        PlainField _    f ->          plainField typ  $  msg ^. f
        OptionalField   f -> toExpr $ plainField typ <$> msg ^. f
        RepeatedField _ f -> toExpr $ plainField typ <$> msg ^. f
        MapField    k v f -> toExpr $
          (messageToExpr . pairToMsg k v) <$> Map.toList (msg ^. f)
    )

plainField :: FieldTypeDescriptor value -> value -> Expr
plainField (MessageField _)  = messageToExpr
plainField (ScalarField typ) = scalarField typ

scalarField :: ScalarField value -> value -> Expr
scalarField EnumField     = enumField
scalarField Int32Field    = toExpr
scalarField Int64Field    = toExpr
scalarField UInt32Field   = toExpr
scalarField UInt64Field   = toExpr
scalarField SInt32Field   = toExpr
scalarField SInt64Field   = toExpr
scalarField Fixed32Field  = toExpr
scalarField Fixed64Field  = toExpr
scalarField SFixed32Field = toExpr
scalarField SFixed64Field = toExpr
scalarField FloatField    = toExpr
scalarField DoubleField   = toExpr
scalarField BoolField     = toExpr
scalarField StringField   = toExpr
scalarField BytesField    = toExpr

enumField :: MessageEnum value => value -> Expr
enumField = toExpr . fromEnum

unknownField :: TaggedValue -> (FieldName, Expr)
unknownField (TaggedValue (Tag tag) value) = (show tag, wireValue value)

wireValue :: WireValue -> Expr
wireValue (Fixed32 x) = toExpr x
wireValue (Fixed64 x) = toExpr x
wireValue (Lengthy x) = toExpr x
wireValue (VarInt x)  = toExpr x
wireValue StartGroup  = toExpr ()
wireValue EndGroup    = toExpr ()

{-------------------------------------------------------------------------------
  Auxiliary: working with maps
-------------------------------------------------------------------------------}

pairToMsg :: Message msg => Lens' msg k -> Lens' msg v -> (k, v) -> msg
pairToMsg k v (x, y) = defMessage & k .~ x & v .~ y