protobuf-0.1.1: src/Data/ProtocolBuffers/Message.hs
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.ProtocolBuffers.Message
( Message(..)
) where
import Control.Applicative
import Control.DeepSeq (NFData(..))
import Data.Foldable
import Data.Monoid
import Data.Serialize.Get
import Data.Serialize.Put
import Data.Traversable
import qualified Data.TypeLevel as Tl
import GHC.Generics
import Data.ProtocolBuffers.Decode
import Data.ProtocolBuffers.Encode
import Data.ProtocolBuffers.Types
import Data.ProtocolBuffers.Wire
-- |
-- The way to embed a message within another message.
-- These embedded messages are stored as length-delimited fields.
--
-- For example:
--
-- @
--data Inner = Inner
-- { innerField :: 'Data.ProtocolBuffers.Required' 'Data.TypeLevel.D1' ('Data.ProtocolBuffers.Value' 'Data.Int.Int64')
-- } deriving ('GHC.Generics.Generic', 'Prelude.Show')
--
-- instance 'Encode' Inner
--instance 'Decode' Inner
--
-- data Outer = Outer
-- { outerField :: 'Data.ProtocolBuffers.Required' 'Data.TypeLevel.D1' ('Data.ProtocolBuffers.Message' Inner)
-- } deriving ('GHC.Generics.Generic', 'Prelude.Show')
--
-- instance 'Encode' Outer
--instance 'Decode' Outer
-- @
--
-- It's worth noting that @ 'Message' a @ is a 'Monoid' and 'NFData' instance. The 'Monoid' behavior models
-- that of the Protocol Buffers documentation, effectively 'Data.Monoid.Last'. It's done with a fairly big hammer
-- and it isn't possible to override this behavior. This can cause some less-obvious compile errors for
-- paramterized 'Message' types:
--
-- @
--data Inner = Inner{inner :: 'Required' 'D2' ('Value' 'Float')} deriving ('Generic', 'Show')
--instance 'Encode' Inner
--instance 'Decode' Inner
--
--data Outer a = Outer{outer :: 'Required' 'D3' ('Message' a)} deriving ('Generic', 'Show')
--instance 'Encode' a => 'Encode' (Outer a)
--instance 'Decode' a => 'Decode' (Outer a)
-- @
--
-- This fails because 'Decode' needs to know that the message can be merged. The resulting error
-- implies that you may want to add a constraint to the internal 'GMessageMonoid' class:
--
-- @
-- \/tmp\/tst.hs:18:10:
-- Could not deduce (protobuf-0.1:'Data.ProtocolBuffers.Message.GMessageMonoid' ('Rep' a))
-- arising from a use of `protobuf-0.1: 'Data.ProtocolBuffers.Decode' .$gdmdecode'
-- from the context ('Decode' a)
-- bound by the instance declaration at \/tmp\/tst.hs:18:10-39
-- Possible fix:
-- add an instance declaration for
-- (protobuf-0.1:'Data.ProtocolBuffers.Message.GMessageMonoid' ('Rep' a))
-- In the expression:
-- (protobuf-0.1:'Data.ProtocolBuffers.Decode'.$gdmdecode)
-- In an equation for `decode':
-- decode = (protobuf-0.1:'Data.ProtocolBuffers.Decode' .$gdmdecode)
-- In the instance declaration for `'Decode' (Outer a)'
-- @
--
-- The correct fix is to add the 'Monoid' constraint for the message:
--
-- @
-- - instance ('Encode' a) => 'Decode' (Outer a)
-- + instance ('Monoid' ('Message' a), 'Decode' a) => 'Decode' (Outer a)
-- @
--
newtype Message m = Message {runMessage :: m}
deriving (Eq, Foldable, Functor, Ord, Show, Traversable)
instance (Generic m, GMessageMonoid (Rep m)) => Monoid (Message m) where
mempty = Message . to $ gmempty
Message x `mappend` Message y = Message . to $ gmappend (from x) (from y)
instance (Decode a, Monoid (Message a), Tl.Nat n) => GDecode (K1 i (Field n (RequiredField (Always (Message a))))) where
gdecode = fieldDecode (Required . Always)
instance (Decode a, Monoid (Message a), Tl.Nat n) => GDecode (K1 i (Field n (OptionalField (Maybe (Message a))))) where
gdecode msg = fieldDecode (Optional . Just) msg <|> pure (K1 mempty)
class GMessageMonoid (f :: * -> *) where
gmempty :: f a
gmappend :: f a -> f a -> f a
instance GMessageMonoid f => GMessageMonoid (M1 i c f) where
gmempty = M1 gmempty
gmappend (M1 x) (M1 y) = M1 (gmappend x y)
instance (GMessageMonoid x, GMessageMonoid y) => GMessageMonoid (x :*: y) where
gmempty = gmempty :*: gmempty
gmappend (x1 :*: x2) (y1 :*: y2) = gmappend x1 y1 :*: gmappend x2 y2
instance (GMessageMonoid x, GMessageMonoid y) => GMessageMonoid (x :+: y) where
gmempty = L1 gmempty
gmappend _ = id
instance (Monoid c) => GMessageMonoid (K1 i c) where
gmempty = K1 mempty
gmappend (K1 x) (K1 y) = K1 $ mappend x y
instance GMessageMonoid U1 where
gmempty = U1
gmappend _ = id
instance (Generic m, GMessageNFData (Rep m)) => NFData (Message m) where
rnf = grnf . from . runMessage
class GMessageNFData f where
grnf :: f a -> ()
instance GMessageNFData f => GMessageNFData (M1 i c f) where
grnf = grnf . unM1
instance (GMessageNFData x, GMessageNFData y) => GMessageNFData (x :*: y) where
grnf (x :*: y) = grnf x `seq` grnf y
instance (GMessageNFData x, GMessageNFData y) => GMessageNFData (x :+: y) where
grnf (L1 x) = grnf x
grnf (R1 y) = grnf y
instance NFData c => GMessageNFData (K1 i c) where
grnf = rnf . unK1
instance GMessageNFData U1 where
grnf U1 = ()
type instance Optional n (Message a) = Field n (OptionalField (Maybe (Message a)))
type instance Required n (Message a) = Field n (RequiredField (Always (Message a)))
instance (Foldable f, Encode m) => EncodeWire (f (Message m)) where
encodeWire t =
traverse_ (encodeWire t . runPut . encode . runMessage)
instance Decode m => DecodeWire (Message m) where
decodeWire (DelimitedField _ bs) =
case runGet decodeMessage bs of
Right val -> pure $ Message val
Left err -> fail $ "Embedded message decoding failed: " ++ show err
decodeWire _ = empty
-- | Iso: @ 'FieldType' ('Required' n ('Message' a)) = a @
instance HasField (Field n (RequiredField (Always (Message a)))) where
type FieldType (Field n (RequiredField (Always (Message a)))) = a
getField = runMessage . runAlways. runRequired . runField
putField = Field . Required . Always . Message
-- | Iso: @ 'FieldType' ('Optional' n ('Message' a)) = 'Maybe' a @
instance HasField (Field n (OptionalField (Maybe (Message a)))) where
type FieldType (Field n (OptionalField (Maybe (Message a)))) = Maybe a
getField = fmap runMessage . runOptional . runField
putField = Field . Optional . fmap Message
-- | Iso: @ 'FieldType' ('Repeated' n ('Message' a)) = [a] @
instance HasField (Field n (RepeatedField [Message a])) where
type FieldType (Field n (RepeatedField [Message a])) = [a]
getField = fmap runMessage . runRepeated . runField
putField = Field . Repeated . fmap Message