{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
module Crypto.Lol.Types.Proto
(Protoable(..), msgPut, msgGet
,uToString, uFromString
,readProtoType, parseProtoFile
,writeProtoType, writeProtoFile
,ProtoReadable
) where
import Crypto.Proto.Lol.TypeRep (TypeRep(TypeRep))
import Control.Monad.Except
import qualified Data.ByteString.Lazy as BS
import Data.Foldable (toList)
import Data.Sequence
import GHC.Fingerprint
import Prelude hiding (length)
import System.Directory
import Text.ProtocolBuffers (messageGet, messagePut)
import Text.ProtocolBuffers.Basic (uToString, uFromString)
import Text.ProtocolBuffers.Header
type ProtoReadable a = (Protoable a, Wire (ProtoType a), ReflectDescriptor (ProtoType a))
class Protoable a where
type ProtoType a
toProto :: a -> ProtoType a
fromProto :: MonadError String m => ProtoType a -> m a
instance (Protoable a) => Protoable [a] where
type ProtoType [a] = Seq (ProtoType a)
toProto = fromList . map toProto
fromProto = mapM fromProto . toList
instance (Protoable a, Protoable b) => Protoable (a,b) where
type ProtoType (a,b) = (ProtoType a, ProtoType b)
toProto (a,b) = (toProto a, toProto b)
fromProto (a,b) = do
a' <- fromProto a
b' <- fromProto b
return (a',b')
msgPut :: (ReflectDescriptor (ProtoType a), Wire (ProtoType a), Protoable a)
=> a -> ByteString
msgPut = messagePut . toProto
msgGet :: (ReflectDescriptor (ProtoType a), Wire (ProtoType a), Protoable a)
=> ByteString -> Either String (a, ByteString)
msgGet bs = do
(msg, bs') <- messageGet bs
p <- fromProto msg
return (p, bs')
readProtoType :: (ReflectDescriptor a, Wire a, MonadIO m, MonadError String m)
=> FilePath -> m a
readProtoType file = do
fileExists <- liftIO $ doesFileExist file
unless fileExists $ throwError $
"Error reading " ++ file ++ ": file does not exist."
bs <- liftIO $ BS.readFile file
case messageGet bs of
(Left str) -> throwError $
"Error when reading from protocol buffer. Got string " ++ str
(Right (a,bs')) -> do
unless (BS.null bs') $ throwError
"Error when reading from protocol buffer. There were leftover bits!"
return a
writeProtoType :: (ReflectDescriptor a, Wire a) => FilePath -> a -> IO ()
writeProtoType fileName = BS.writeFile fileName . messagePut
parseProtoFile :: (ProtoReadable a, MonadIO m, MonadError String m)
=> FilePath -> m a
parseProtoFile file = fromProto =<< readProtoType file
writeProtoFile :: (ProtoReadable a, MonadIO m) => FilePath -> a -> m ()
writeProtoFile file = liftIO . writeProtoType file . toProto
instance Protoable Fingerprint where
type ProtoType Fingerprint = TypeRep
toProto (Fingerprint a b) = TypeRep a b
fromProto (TypeRep a b) = return $ Fingerprint a b