protobuf-0.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
-- @
--
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