{-# LANGUAGE DeriveDataTypeable,RankNTypes #-}
module Text.ProtocolBuffers.Unknown
( UnknownField(..),UnknownMessage(..),UnknownFieldValue(..)
, wireSizeUnknownField,wirePutUnknownField, wirePutUnknownFieldWithSize
, catch'Unknown, catch'Unknown', loadUnknown, discardUnknown
) where
import qualified Data.ByteString.Lazy as L
import qualified Data.Foldable as F
import Data.Generics
import Data.Sequence((|>))
import Data.Typeable()
import Control.Monad.Error.Class(catchError)
import Text.ProtocolBuffers.Basic
import Text.ProtocolBuffers.WireMessage
class UnknownMessage msg where
getUnknownField :: msg -> UnknownField
putUnknownField :: UnknownField -> msg -> msg
newtype UnknownField = UnknownField (Seq UnknownFieldValue)
deriving (Eq,Ord,Show,Read,Data,Typeable)
data UnknownFieldValue = UFV {-# UNPACK #-} !WireTag !ByteString
deriving (Eq,Ord,Show,Read,Data,Typeable)
instance Mergeable UnknownField where
mergeAppend (UnknownField m1) (UnknownField m2) = UnknownField (mappend m1 m2)
instance Default UnknownField where
defaultValue = UnknownField mempty
wireSizeUnknownField :: UnknownField -> WireSize
wireSizeUnknownField (UnknownField m) = F.foldl' aSize 0 m where
aSize old (UFV tag bs) = old + size'WireTag tag + L.length bs
wirePutUnknownField :: UnknownField -> Put
wirePutUnknownField (UnknownField m) = F.mapM_ aPut m where
aPut (UFV tag bs) = putVarUInt (getWireTag tag) >> putLazyByteString bs
wirePutUnknownFieldWithSize :: UnknownField -> PutM WireSize
wirePutUnknownFieldWithSize m =
wirePutUnknownField m >> return (wireSizeUnknownField m)
{-# INLINE catch'Unknown #-}
catch'Unknown :: (UnknownMessage a) => (WireTag -> a -> Get a) -> WireTag -> a -> Get a
catch'Unknown = catch'Unknown' loadUnknown
{-# INLINE catch'Unknown' #-}
catch'Unknown' :: (WireTag -> a -> Get a) -> (WireTag -> a -> Get a) -> WireTag -> a -> Get a
catch'Unknown' handleUnknown update'Self wire'Tag old'Self =
catchError (update'Self wire'Tag old'Self) (\_ -> handleUnknown wire'Tag old'Self)
{-# INLINE loadUnknown #-}
loadUnknown :: (UnknownMessage a) => WireTag -> a -> Get a
loadUnknown tag msg = do
let (fieldId,wireType) = splitWireTag tag
(UnknownField uf) = getUnknownField msg
bs <- wireGetFromWire fieldId wireType
let v' = seq bs $ UFV tag bs
uf' = seq v' $ uf |> v'
seq uf' $ return $ putUnknownField (UnknownField uf') msg
{-# INLINE discardUnknown #-}
discardUnknown :: WireTag -> a -> Get a
discardUnknown tag msg = do
let (fieldId,wireType) = splitWireTag tag
_bs <- wireGetFromWire fieldId wireType
return msg