{-# LANGUAGE DataKinds #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE EmptyCase #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} -- for `deriveSerDoc` {-# OPTIONS_GHC -Wno-redundant-constraints #-} module DemoProtocol where import Control.Monad.Identity import Control.Monad.Except import Data.Proxy import Data.SerDoc.Class import Data.SerDoc.Info import Data.SerDoc.TH import Data.Text (Text) import Data.Word import Network.TypedProtocol.Core data PongInfo = PongInfo { pongTimestamp :: Word64 , pongPeerID :: Word64 , pongMessage :: Text } deriving (Show, Eq) data DemoProtocol a where -- | Idle state: server waits for ping. IdleState :: DemoProtocol a -- | Awaiting pong state: server has received ping, client waits for pong. AwaitingPongState :: DemoProtocol a -- | End state: either side has terminated the session EndState :: DemoProtocol a data SingDemoProtocol a where SingIdleState :: SingDemoProtocol (IdleState :: DemoProtocol a) SingAwaitingPongState :: SingDemoProtocol (AwaitingPongState :: DemoProtocol a) SingEndState :: SingDemoProtocol (EndState :: DemoProtocol a) instance StateTokenI IdleState where stateToken = SingIdleState instance StateTokenI AwaitingPongState where stateToken = SingAwaitingPongState instance StateTokenI EndState where stateToken = SingEndState instance Protocol (DemoProtocol a) where data Message (DemoProtocol a) st st' where PingMessage :: Message (DemoProtocol a) IdleState AwaitingPongState PongMessage :: Message (DemoProtocol a) AwaitingPongState IdleState ComplexPongMessage :: Message (DemoProtocol a) AwaitingPongState IdleState EndMessage :: Message (DemoProtocol a) st EndState type StateAgency IdleState = ServerAgency type StateAgency AwaitingPongState = ClientAgency type StateAgency EndState = NobodyAgency type StateToken = SingDemoProtocol data DemoCodec a instance Codec (DemoCodec a) where type MonadEncode (DemoCodec a) = Identity type MonadDecode (DemoCodec a) = Except String data PongEnum = NormalPong | ComplexPong deriving (Show, Read, Eq, Ord, Enum, Bounded) data PingEnum = PingRequest | EndPing deriving (Show, Read, Eq, Ord, Enum, Bounded) deriving via (ViaEnum PongEnum) instance (Codec codec, HasInfo codec (DefEnumEncoding codec)) => HasInfo codec PongEnum deriving via (ViaEnum PingEnum) instance (Codec codec, HasInfo codec (DefEnumEncoding codec)) => HasInfo codec PingEnum instance HasInfo (DemoCodec b) () where info _ _ = basicField "()" (FixedSize 0) instance HasInfo (DemoCodec b) Text where info codec _ = compoundField "Text" [ ("length", info codec (Proxy @Word32)) , ("data", basicField "UTF8 dat" (FixedSize 0)) ] instance HasInfo (DemoCodec b) a => HasInfo (DemoCodec b) [a] where info codec (_ :: Proxy [a]) = compoundField "List" [ ( "length", info codec (Proxy @Word32)) , ( "values" , listField (VarSize "length") (info codec (Proxy @a)) ) ] instance HasInfo (DemoCodec b) a => HasInfo (DemoCodec b) (Maybe a) where info codec (_ :: Proxy (Maybe a)) = compoundField "Maybe" [ ("isJust", info codec (Proxy @Word32)) , ( "value" , sumField "isJust" [ ("Nothing", info codec (Proxy @())) , ("Just", info codec (Proxy @a)) ] ) ] instance HasInfo (DemoCodec b) (Message (DemoProtocol a) IdleState AwaitingPongState) where info codec _ = infoOf "PingRequest" $ info codec (Proxy @PingEnum) instance HasInfo (DemoCodec b) (Message (DemoProtocol a) st EndState) where info codec _ = infoOf "EndPing" $ info codec (Proxy @PingEnum) instance HasInfo (DemoCodec a) Word16 where info _ _ = basicField "Word16" (FixedSize 2) instance HasInfo (DemoCodec a) Word32 where info _ _ = basicField "Word32" (FixedSize 4) instance HasInfo (DemoCodec a) Word64 where info _ _ = basicField "Word64" (FixedSize 8) $(deriveSerDoc ''DemoCodec [] ''PongInfo) instance HasInfo (DemoCodec b) (Message (DemoProtocol a) AwaitingPongState IdleState) where info codec _ = compoundField "Pong" [ ("pongType", info codec (Proxy @PongEnum)) , ("pongData" , choiceField (IndexField "pongType") [ info codec (Proxy @()) , info codec (Proxy @PongInfo) ] ) ]