{-# LANGUAGE CPP #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE IncoherentInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE StrictData #-} {-# LANGUAGE Trustworthy #-} -------------------------------------------------------------------- -- | -- Module : Data.MessagePack.Object -- Copyright : (c) Hideyuki Tanaka, 2009-2015 -- License : BSD3 -- -- Maintainer: tanaka.hideyuki@gmail.com -- Stability : experimental -- Portability: portable -- -- MessagePack object definition -- -------------------------------------------------------------------- module Data.MessagePack.Types.Class ( MessagePack (..) , GMessagePack (..) , Config , defaultConfig ) where import Control.Applicative (Applicative, (<$>), (<*>)) import Control.Arrow ((***)) import Control.Monad.Validate (MonadValidate (..)) import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L import qualified Data.HashMap.Strict as HashMap import Data.Hashable (Hashable) import Data.Int (Int16, Int32, Int64, Int8) import qualified Data.IntMap.Strict as IntMap import qualified Data.Map as Map import qualified Data.Text as T import qualified Data.Text.Lazy as LT import qualified Data.Vector as V import qualified Data.Vector.Storable as VS import qualified Data.Vector.Unboxed as VU import Data.Word (Word, Word16, Word32, Word64, Word8) import GHC.Generics (Generic, Rep, from, to) import Data.MessagePack.Types.Assoc (Assoc (..)) import Data.MessagePack.Types.DecodeError (DecodeError) import Data.MessagePack.Types.Object (Object (..)) data Config = Config defaultConfig :: Config defaultConfig = Config -- Generic serialisation. class GMessagePack f where gToObject :: Config -> f a -> Object gFromObject :: ( Applicative m , Monad m , MonadValidate DecodeError m ) => Config -> Object -> m (f a) class MessagePack a where toObject :: Config -> a -> Object fromObjectWith :: ( Applicative m , Monad m , MonadValidate DecodeError m ) => Config -> Object -> m a default toObject :: (Generic a, GMessagePack (Rep a)) => Config -> a -> Object toObject = genericToObject default fromObjectWith :: ( Applicative m , Monad m , MonadValidate DecodeError m , Generic a, GMessagePack (Rep a)) => Config -> Object -> m a fromObjectWith = genericFromObject genericToObject :: (Generic a, GMessagePack (Rep a)) => Config -> a -> Object genericToObject cfg = gToObject cfg . from genericFromObject :: ( Applicative m , Monad m , MonadValidate DecodeError m , Generic a , GMessagePack (Rep a) ) => Config -> Object -> m a genericFromObject cfg x = to <$> gFromObject cfg x -- Instances for integral types (Int etc.). toInt :: Integral a => a -> Int64 toInt = fromIntegral fromInt :: Integral a => Int64 -> a fromInt = fromIntegral toWord :: Integral a => a -> Word64 toWord = fromIntegral fromWord :: Integral a => Word64 -> a fromWord = fromIntegral instance MessagePack Int64 where toObject _ i | i < 0 = ObjectInt i | otherwise = ObjectWord $ toWord i fromObjectWith _ = \case ObjectInt n -> return n ObjectWord n -> return $ toInt n _ -> refute "invalid encoding for integer type" instance MessagePack Word64 where toObject _ = ObjectWord fromObjectWith _ = \case ObjectWord n -> return n _ -> refute "invalid encoding for integer type" instance MessagePack Int where { toObject cfg = toObject cfg . toInt; fromObjectWith cfg o = fromInt <$> fromObjectWith cfg o } instance MessagePack Int8 where { toObject cfg = toObject cfg . toInt; fromObjectWith cfg o = fromInt <$> fromObjectWith cfg o } instance MessagePack Int16 where { toObject cfg = toObject cfg . toInt; fromObjectWith cfg o = fromInt <$> fromObjectWith cfg o } instance MessagePack Int32 where { toObject cfg = toObject cfg . toInt; fromObjectWith cfg o = fromInt <$> fromObjectWith cfg o } instance MessagePack Word where { toObject cfg = toObject cfg . toWord; fromObjectWith cfg o = fromWord <$> fromObjectWith cfg o } instance MessagePack Word8 where { toObject cfg = toObject cfg . toWord; fromObjectWith cfg o = fromWord <$> fromObjectWith cfg o } instance MessagePack Word16 where { toObject cfg = toObject cfg . toWord; fromObjectWith cfg o = fromWord <$> fromObjectWith cfg o } instance MessagePack Word32 where { toObject cfg = toObject cfg . toWord; fromObjectWith cfg o = fromWord <$> fromObjectWith cfg o } -- Core instances. instance MessagePack Object where toObject _ = id fromObjectWith _ = return instance MessagePack () where toObject _ _ = ObjectNil fromObjectWith _ = \case ObjectNil -> return () ObjectArray [] -> return () _ -> refute "invalid encoding for ()" instance MessagePack Bool where toObject _ = ObjectBool fromObjectWith _ = \case ObjectBool b -> return b _ -> refute "invalid encoding for Bool" instance MessagePack Float where toObject _ = ObjectFloat fromObjectWith _ = \case ObjectInt n -> return $ fromIntegral n ObjectWord n -> return $ fromIntegral n ObjectFloat f -> return f ObjectDouble d -> return $ realToFrac d _ -> refute "invalid encoding for Float" instance MessagePack Double where toObject _ = ObjectDouble fromObjectWith _ = \case ObjectInt n -> return $ fromIntegral n ObjectWord n -> return $ fromIntegral n ObjectFloat f -> return $ realToFrac f ObjectDouble d -> return d _ -> refute "invalid encoding for Double" -- Because of overlapping instance, this must be above [a]. -- IncoherentInstances and TypeSynonymInstances are required for this to work. instance MessagePack String where toObject cfg = toObject cfg . T.pack fromObjectWith cfg obj = T.unpack <$> fromObjectWith cfg obj -- Instances for binary and UTF-8 encoded string. instance MessagePack S.ByteString where toObject _ = ObjectBin fromObjectWith _ = \case ObjectBin r -> return r _ -> refute "invalid encoding for ByteString" instance MessagePack L.ByteString where toObject _ = ObjectBin . L.toStrict fromObjectWith cfg obj = L.fromStrict <$> fromObjectWith cfg obj instance MessagePack T.Text where toObject _ = ObjectStr fromObjectWith _ = \case ObjectStr s -> return s _ -> refute "invalid encoding for Text" instance MessagePack LT.Text where toObject cfg = toObject cfg . LT.toStrict fromObjectWith cfg obj = LT.fromStrict <$> fromObjectWith cfg obj -- Instances for array-like data structures. instance MessagePack a => MessagePack [a] where toObject cfg = ObjectArray . map (toObject cfg) fromObjectWith cfg = \case ObjectArray xs -> mapM (fromObjectWith cfg) xs _ -> refute "invalid encoding for list" instance MessagePack a => MessagePack (V.Vector a) where toObject cfg = ObjectArray . map (toObject cfg) . V.toList fromObjectWith cfg = \case ObjectArray o -> V.fromList <$> mapM (fromObjectWith cfg) o _ -> refute "invalid encoding for Vector" instance (MessagePack a, VU.Unbox a) => MessagePack (VU.Vector a) where toObject cfg = ObjectArray . map (toObject cfg) . VU.toList fromObjectWith cfg = \case ObjectArray o -> VU.fromList <$> mapM (fromObjectWith cfg) o _ -> refute "invalid encoding for Unboxed Vector" instance (MessagePack a, VS.Storable a) => MessagePack (VS.Vector a) where toObject cfg = ObjectArray . map (toObject cfg) . VS.toList fromObjectWith cfg = \case ObjectArray o -> VS.fromList <$> mapM (fromObjectWith cfg) o _ -> refute "invalid encoding for Storable Vector" -- Instances for map-like data structures. instance (MessagePack a, MessagePack b) => MessagePack (Assoc [(a, b)]) where toObject cfg (Assoc xs) = ObjectMap $ map (toObject cfg *** toObject cfg) xs fromObjectWith cfg = \case ObjectMap xs -> Assoc <$> mapM (\(k, v) -> (,) <$> fromObjectWith cfg k <*> fromObjectWith cfg v) xs _ -> refute "invalid encoding for Assoc" instance (MessagePack k, MessagePack v, Ord k) => MessagePack (Map.Map k v) where toObject cfg = toObject cfg . Assoc . Map.toList fromObjectWith cfg obj = Map.fromList . unAssoc <$> fromObjectWith cfg obj instance MessagePack v => MessagePack (IntMap.IntMap v) where toObject cfg = toObject cfg . Assoc . IntMap.toList fromObjectWith cfg obj = IntMap.fromList . unAssoc <$> fromObjectWith cfg obj instance (MessagePack k, MessagePack v, Hashable k, Eq k) => MessagePack (HashMap.HashMap k v) where toObject cfg = toObject cfg . Assoc . HashMap.toList fromObjectWith cfg obj = HashMap.fromList . unAssoc <$> fromObjectWith cfg obj -- Instances for various tuple arities. instance (MessagePack a1, MessagePack a2) => MessagePack (a1, a2) where toObject cfg (a1, a2) = ObjectArray [toObject cfg a1, toObject cfg a2] fromObjectWith cfg (ObjectArray [a1, a2]) = (,) <$> fromObjectWith cfg a1 <*> fromObjectWith cfg a2 fromObjectWith _ _ = refute "invalid encoding for tuple" instance (MessagePack a1, MessagePack a2, MessagePack a3) => MessagePack (a1, a2, a3) where toObject cfg (a1, a2, a3) = ObjectArray [toObject cfg a1, toObject cfg a2, toObject cfg a3] fromObjectWith cfg (ObjectArray [a1, a2, a3]) = (,,) <$> fromObjectWith cfg a1 <*> fromObjectWith cfg a2 <*> fromObjectWith cfg a3 fromObjectWith _ _ = refute "invalid encoding for tuple" instance (MessagePack a1, MessagePack a2, MessagePack a3, MessagePack a4) => MessagePack (a1, a2, a3, a4) where toObject cfg (a1, a2, a3, a4) = ObjectArray [toObject cfg a1, toObject cfg a2, toObject cfg a3, toObject cfg a4] fromObjectWith cfg (ObjectArray [a1, a2, a3, a4]) = (,,,) <$> fromObjectWith cfg a1 <*> fromObjectWith cfg a2 <*> fromObjectWith cfg a3 <*> fromObjectWith cfg a4 fromObjectWith _ _ = refute "invalid encoding for tuple" instance (MessagePack a1, MessagePack a2, MessagePack a3, MessagePack a4, MessagePack a5) => MessagePack (a1, a2, a3, a4, a5) where toObject cfg (a1, a2, a3, a4, a5) = ObjectArray [toObject cfg a1, toObject cfg a2, toObject cfg a3, toObject cfg a4, toObject cfg a5] fromObjectWith cfg (ObjectArray [a1, a2, a3, a4, a5]) = (,,,,) <$> fromObjectWith cfg a1 <*> fromObjectWith cfg a2 <*> fromObjectWith cfg a3 <*> fromObjectWith cfg a4 <*> fromObjectWith cfg a5 fromObjectWith _ _ = refute "invalid encoding for tuple" instance (MessagePack a1, MessagePack a2, MessagePack a3, MessagePack a4, MessagePack a5, MessagePack a6) => MessagePack (a1, a2, a3, a4, a5, a6) where toObject cfg (a1, a2, a3, a4, a5, a6) = ObjectArray [toObject cfg a1, toObject cfg a2, toObject cfg a3, toObject cfg a4, toObject cfg a5, toObject cfg a6] fromObjectWith cfg (ObjectArray [a1, a2, a3, a4, a5, a6]) = (,,,,,) <$> fromObjectWith cfg a1 <*> fromObjectWith cfg a2 <*> fromObjectWith cfg a3 <*> fromObjectWith cfg a4 <*> fromObjectWith cfg a5 <*> fromObjectWith cfg a6 fromObjectWith _ _ = refute "invalid encoding for tuple" instance (MessagePack a1, MessagePack a2, MessagePack a3, MessagePack a4, MessagePack a5, MessagePack a6, MessagePack a7) => MessagePack (a1, a2, a3, a4, a5, a6, a7) where toObject cfg (a1, a2, a3, a4, a5, a6, a7) = ObjectArray [toObject cfg a1, toObject cfg a2, toObject cfg a3, toObject cfg a4, toObject cfg a5, toObject cfg a6, toObject cfg a7] fromObjectWith cfg (ObjectArray [a1, a2, a3, a4, a5, a6, a7]) = (,,,,,,) <$> fromObjectWith cfg a1 <*> fromObjectWith cfg a2 <*> fromObjectWith cfg a3 <*> fromObjectWith cfg a4 <*> fromObjectWith cfg a5 <*> fromObjectWith cfg a6 <*> fromObjectWith cfg a7 fromObjectWith _ _ = refute "invalid encoding for tuple" instance (MessagePack a1, MessagePack a2, MessagePack a3, MessagePack a4, MessagePack a5, MessagePack a6, MessagePack a7, MessagePack a8) => MessagePack (a1, a2, a3, a4, a5, a6, a7, a8) where toObject cfg (a1, a2, a3, a4, a5, a6, a7, a8) = ObjectArray [toObject cfg a1, toObject cfg a2, toObject cfg a3, toObject cfg a4, toObject cfg a5, toObject cfg a6, toObject cfg a7, toObject cfg a8] fromObjectWith cfg (ObjectArray [a1, a2, a3, a4, a5, a6, a7, a8]) = (,,,,,,,) <$> fromObjectWith cfg a1 <*> fromObjectWith cfg a2 <*> fromObjectWith cfg a3 <*> fromObjectWith cfg a4 <*> fromObjectWith cfg a5 <*> fromObjectWith cfg a6 <*> fromObjectWith cfg a7 <*> fromObjectWith cfg a8 fromObjectWith _ _ = refute "invalid encoding for tuple" instance (MessagePack a1, MessagePack a2, MessagePack a3, MessagePack a4, MessagePack a5, MessagePack a6, MessagePack a7, MessagePack a8, MessagePack a9) => MessagePack (a1, a2, a3, a4, a5, a6, a7, a8, a9) where toObject cfg (a1, a2, a3, a4, a5, a6, a7, a8, a9) = ObjectArray [toObject cfg a1, toObject cfg a2, toObject cfg a3, toObject cfg a4, toObject cfg a5, toObject cfg a6, toObject cfg a7, toObject cfg a8, toObject cfg a9] fromObjectWith cfg (ObjectArray [a1, a2, a3, a4, a5, a6, a7, a8, a9]) = (,,,,,,,,) <$> fromObjectWith cfg a1 <*> fromObjectWith cfg a2 <*> fromObjectWith cfg a3 <*> fromObjectWith cfg a4 <*> fromObjectWith cfg a5 <*> fromObjectWith cfg a6 <*> fromObjectWith cfg a7 <*> fromObjectWith cfg a8 <*> fromObjectWith cfg a9 fromObjectWith _ _ = refute "invalid encoding for tuple"