module Network.MQTT.Types
(
Message(..)
, SomeMessage(..)
, MqttHeader(..)
, MessageBody(..)
, Connect(..)
, ConnAck(..)
, Publish(..)
, Subscribe(..)
, SubAck(..)
, Unsubscribe(..)
, SimpleMsg(..)
, Will(..)
, QoS(..)
, MsgID
, getMsgID
, Topic
, fromTopic
, toTopic
, matches
, MqttText(..)
, MsgType(..)
, toMsgType
, toMsgType'
, toSMsgType
, SMsgType
, Sing( SCONNECT
, SCONNACK
, SPUBLISH
, SPUBACK
, SPUBREC
, SPUBREL
, SPUBCOMP
, SSUBSCRIBE
, SSUBACK
, SUNSUBSCRIBE
, SUNSUBACK
, SPINGREQ
, SPINGRESP
, SDISCONNECT)
) where
import Data.ByteString (ByteString)
import Data.Singletons.TH
import Data.String (IsString(..))
import Data.Text (Text)
import qualified Data.Text as T
import Data.Word
data Message (t :: MsgType)
= Message
{ header :: MqttHeader
, body :: MessageBody t
}
data SomeMessage where
SomeMessage :: Message t -> SomeMessage
data MqttHeader
= Header
{
dup :: Bool
, qos :: QoS
, retain :: Bool
}
deriving (Eq, Ord, Show)
data MessageBody (t :: MsgType) where
MConnect :: Connect -> MessageBody CONNECT
MConnAck :: ConnAck -> MessageBody CONNACK
MPublish :: Publish -> MessageBody PUBLISH
MPubAck :: SimpleMsg -> MessageBody PUBACK
MPubRec :: SimpleMsg -> MessageBody PUBREC
MPubRel :: SimpleMsg -> MessageBody PUBREL
MPubComp :: SimpleMsg -> MessageBody PUBCOMP
MSubscribe :: Subscribe -> MessageBody SUBSCRIBE
MSubAck :: SubAck -> MessageBody SUBACK
MUnsubscribe :: Unsubscribe -> MessageBody UNSUBSCRIBE
MUnsubAck :: SimpleMsg -> MessageBody UNSUBACK
MPingReq :: MessageBody PINGREQ
MPingResp :: MessageBody PINGRESP
MDisconnect :: MessageBody DISCONNECT
data Connect
= Connect
{ cleanSession :: Bool
, will :: Maybe Will
, clientID :: MqttText
, username :: Maybe MqttText
, password :: Maybe MqttText
, keepAlive :: Word16
} deriving (Show, Eq)
newtype ConnAck = ConnAck { returnCode :: Word8 }
deriving (Show, Eq)
data Publish
= Publish
{ topic :: Topic
, pubMsgID :: Maybe MsgID
, payload :: ByteString
} deriving (Show, Eq)
data Subscribe
= Subscribe
{ subscribeMsgID :: MsgID
, subTopics :: [(Topic, QoS)]
} deriving (Show, Eq)
data SubAck
= SubAck
{ subAckMsgID :: MsgID
, granted :: [QoS]
} deriving (Show, Eq)
data Unsubscribe
= Unsubscribe
{ unsubMsgID :: MsgID
, unsubTopics :: [Topic]
} deriving (Show, Eq)
newtype SimpleMsg = SimpleMsg { msgID :: MsgID }
deriving (Show, Eq)
data QoS
= NoConfirm
| Confirm
| Handshake
deriving (Eq, Ord, Enum, Show)
data Will
= Will
{ wRetain :: Bool
, wQoS :: QoS
, wTopic :: Topic
, wMsg :: MqttText
}
deriving (Eq, Show)
newtype MqttText = MqttText { text :: Text }
deriving (Eq, Show, IsString)
data Topic = Topic { levels :: [Text], orig :: Text }
type MsgID = Word16
getMsgID :: MessageBody t -> Maybe MsgID
getMsgID (MConnect _) = Nothing
getMsgID (MConnAck _) = Nothing
getMsgID (MPublish pub) = pubMsgID pub
getMsgID (MPubAck simple) = Just (msgID simple)
getMsgID (MPubRec simple) = Just (msgID simple)
getMsgID (MPubRel simple) = Just (msgID simple)
getMsgID (MPubComp simple) = Just (msgID simple)
getMsgID (MSubscribe sub) = Just (subscribeMsgID sub)
getMsgID (MSubAck subA) = Just (subAckMsgID subA)
getMsgID (MUnsubscribe unsub) = Just (unsubMsgID unsub)
getMsgID (MUnsubAck simple) = Just (msgID simple)
getMsgID MPingReq = Nothing
getMsgID MPingResp = Nothing
getMsgID MDisconnect = Nothing
instance Show Topic where
show (Topic _ t) = show t
instance Eq Topic where
Topic _ t1 == Topic _ t2 = t1 == t2
matches :: Topic -> Topic -> Bool
matches (Topic t1 _) (Topic t2 _) = go t1 t2
where
go [] [] = True
go [] (l:_) = l == "#"
go (l:_) [] = l == "#"
go (l1:ls1) (l2:ls2) = l1 == "#" || l2 == "#"
|| ((l1 == "+" || l2 == "+" || l1 == l2)
&& go ls1 ls2)
toTopic :: MqttText -> Topic
toTopic (MqttText txt) = Topic (T.split (== '/') txt) txt
fromTopic :: Topic -> MqttText
fromTopic = MqttText . orig
instance IsString Topic where
fromString str = let txt = T.pack str in
Topic (T.split (== '/') txt) txt
data MsgType
= CONNECT
| CONNACK
| PUBLISH
| PUBACK
| PUBREC
| PUBREL
| PUBCOMP
| SUBSCRIBE
| SUBACK
| UNSUBSCRIBE
| UNSUBACK
| PINGREQ
| PINGRESP
| DISCONNECT
deriving (Eq, Enum, Ord, Show)
genSingletons [''MsgType]
singDecideInstance ''MsgType
toMsgType :: Message t -> MsgType
toMsgType msg =
case body msg of
MConnect _ -> CONNECT
MConnAck _ -> CONNACK
MPublish _ -> PUBLISH
MPubAck _ -> PUBACK
MPubRec _ -> PUBREC
MPubRel _ -> PUBREL
MPubComp _ -> PUBCOMP
MSubscribe _ -> SUBSCRIBE
MSubAck _ -> SUBACK
MUnsubscribe _ -> UNSUBSCRIBE
MUnsubAck _ -> UNSUBACK
MPingReq -> PINGREQ
MPingResp -> PINGRESP
MDisconnect -> DISCONNECT
toMsgType' :: SomeMessage -> MsgType
toMsgType' (SomeMessage msg) = toMsgType msg
toSMsgType :: Message t -> SMsgType t
toSMsgType msg =
case body msg of
MConnect _ -> SCONNECT
MConnAck _ -> SCONNACK
MPublish _ -> SPUBLISH
MPubAck _ -> SPUBACK
MPubRec _ -> SPUBREC
MPubRel _ -> SPUBREL
MPubComp _ -> SPUBCOMP
MSubscribe _ -> SSUBSCRIBE
MSubAck _ -> SSUBACK
MUnsubscribe _ -> SUNSUBSCRIBE
MUnsubAck _ -> SUNSUBACK
MPingReq -> SPINGREQ
MPingResp -> SPINGRESP
MDisconnect -> SDISCONNECT