dbus-core-0.8.3: src/messages.anansi
:# Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com>
:#
:# This program is free software: you can redistribute it and/or modify
:# it under the terms of the GNU General Public License as published by
:# the Free Software Foundation, either version 3 of the License, or
:# any later version.
:#
:# This program is distributed in the hope that it will be useful,
:# but WITHOUT ANY WARRANTY; without even the implied warranty of
:# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
:# GNU General Public License for more details.
:#
:# You should have received a copy of the GNU General Public License
:# along with this program. If not, see <http://www.gnu.org/licenses/>.
\section{Messages}
To prevent internal details of messages from leaking out to clients,
declarations are contained in an internal module and then re-exported
in the public module.
:f DBus/Message.hs
|copyright|
module DBus.Message (
|message exports|
) where
import DBus.Message.Internal
:
:f DBus/Message/Internal.hs
|copyright|
|text extensions|
module DBus.Message.Internal where
|text imports|
import qualified Data.Set as S
import Data.Word (Word8, Word32)
import Data.Maybe (fromMaybe)
import qualified DBus.Types as T
import DBus.Util (maybeIndex)
:
:f DBus/Message/Internal.hs
class Message a where
messageTypeCode :: a -> Word8
messageHeaderFields :: a -> [HeaderField]
messageFlags :: a -> S.Set Flag
messageBody :: a -> [T.Variant]
:
:d message exports
Message ( messageFlags
, messageBody
)
:
\subsection{Flags}
The instance of {\tt Ord} only exists for storing flags in a set. Flags have
no inherent ordering.
:f DBus/Message/Internal.hs
data Flag
= NoReplyExpected
| NoAutoStart
deriving (Show, Eq, Ord)
:
:d message exports
, Flag (..)
:
:f Tests.hs
instance Arbitrary Flag where
arbitrary = elements [NoReplyExpected, NoAutoStart]
:
\subsection{Header fields}
:f DBus/Message/Internal.hs
data HeaderField
= Path T.ObjectPath
| Interface T.InterfaceName
| Member T.MemberName
| ErrorName T.ErrorName
| ReplySerial Serial
| Destination T.BusName
| Sender T.BusName
| Signature T.Signature
deriving (Show, Eq)
:
\subsection{Serials}
{\tt Serial} is just a wrapper around {\tt Word32}, to provide a bit of
added type-safety.
:f DBus/Message/Internal.hs
|apidoc Serial|
newtype Serial = Serial { serialValue :: Word32 }
deriving (Eq, Ord)
instance Show Serial where
show (Serial x) = show x
instance T.Variable Serial where
toVariant (Serial x) = T.toVariant x
fromVariant = fmap Serial . T.fromVariant
:
Additionally, some useful functions exist for incrementing serials.
:f DBus/Message/Internal.hs
firstSerial :: Serial
firstSerial = Serial 1
nextSerial :: Serial -> Serial
nextSerial (Serial x) = Serial (x + 1)
:
The {\tt Serial} constructor isn't useful to clients, because building
arbitrary serials doesn't make any sense.
:d message exports
, Serial
, serialValue
, firstSerial
, nextSerial
:
:f Tests.hs
instance Arbitrary Serial where
arbitrary = fmap Serial arbitrary
:
\subsection{Message types}
:f DBus/Message/Internal.hs
maybe' :: (a -> b) -> Maybe a -> [b]
maybe' f = maybe [] (\x' -> [f x'])
:
\subsubsection{Method calls}
:f DBus/Message/Internal.hs
data MethodCall = MethodCall
{ methodCallPath :: T.ObjectPath
, methodCallMember :: T.MemberName
, methodCallInterface :: Maybe T.InterfaceName
, methodCallDestination :: Maybe T.BusName
, methodCallFlags :: S.Set Flag
, methodCallBody :: [T.Variant]
}
deriving (Show, Eq)
instance Message MethodCall where
messageTypeCode _ = 1
messageFlags = methodCallFlags
messageBody = methodCallBody
messageHeaderFields m = concat
[ [ Path $ methodCallPath m
, Member $ methodCallMember m
]
, maybe' Interface . methodCallInterface $ m
, maybe' Destination . methodCallDestination $ m
]
:
:d message exports
, MethodCall (..)
:
:f Tests.hs
instance Arbitrary MethodCall where
arbitrary = do
path <- arbitrary
member <- arbitrary
iface <- arbitrary
dest <- arbitrary
flags <- fmap Set.fromList arbitrary
Structure body <- arbitrary
return $ MethodCall path member iface dest flags body
:
\subsubsection{Method returns}
:f DBus/Message/Internal.hs
data MethodReturn = MethodReturn
{ methodReturnSerial :: Serial
, methodReturnDestination :: Maybe T.BusName
, methodReturnBody :: [T.Variant]
}
deriving (Show, Eq)
instance Message MethodReturn where
messageTypeCode _ = 2
messageFlags _ = S.fromList [NoReplyExpected, NoAutoStart]
messageBody = methodReturnBody
messageHeaderFields m = concat
[ [ ReplySerial $ methodReturnSerial m
]
, maybe' Destination . methodReturnDestination $ m
]
:
:d message exports
, MethodReturn (..)
:
:f Tests.hs
instance Arbitrary MethodReturn where
arbitrary = do
serial <- arbitrary
dest <- arbitrary
Structure body <- arbitrary
return $ MethodReturn serial dest body
:
\subsubsection{Errors}
:f DBus/Message/Internal.hs
data Error = Error
{ errorName :: T.ErrorName
, errorSerial :: Serial
, errorDestination :: Maybe T.BusName
, errorBody :: [T.Variant]
}
deriving (Show, Eq)
instance Message Error where
messageTypeCode _ = 3
messageFlags _ = S.fromList [NoReplyExpected, NoAutoStart]
messageBody = errorBody
messageHeaderFields m = concat
[ [ ErrorName $ errorName m
, ReplySerial $ errorSerial m
]
, maybe' Destination . errorDestination $ m
]
:
Errors usually contain a human-readable message in their first body field.
This function lets it be retrieved easily, with a fallback if no valid
message was found.
:f DBus/Message/Internal.hs
errorMessage :: Error -> Text
errorMessage msg = fromMaybe "(no error message)" $ do
field <- maybeIndex (errorBody msg) 0
text <- T.fromVariant field
if TL.null text
then Nothing
else return text
:
:d message exports
, Error (..)
, errorMessage
:
:f Tests.hs
instance Arbitrary Error where
arbitrary = do
name <- arbitrary
serial <- arbitrary
dest <- arbitrary
Structure body <- arbitrary
return $ Error name serial dest body
:
\subsubsection{Signals}
:f DBus/Message/Internal.hs
data Signal = Signal
{ signalPath :: T.ObjectPath
, signalMember :: T.MemberName
, signalInterface :: T.InterfaceName
, signalDestination :: Maybe T.BusName
, signalBody :: [T.Variant]
}
deriving (Show, Eq)
instance Message Signal where
messageTypeCode _ = 4
messageFlags _ = S.fromList [NoReplyExpected, NoAutoStart]
messageBody = signalBody
messageHeaderFields m = concat
[ [ Path $ signalPath m
, Member $ signalMember m
, Interface $ signalInterface m
]
, maybe' Destination . signalDestination $ m
]
:
:d message exports
, Signal (..)
:
:f Tests.hs
instance Arbitrary Signal where
arbitrary = do
path <- arbitrary
member <- arbitrary
iface <- arbitrary
dest <- arbitrary
Structure body <- arbitrary
return $ Signal path member iface dest body
:
\subsubsection{Unknown messages}
Unknown messages are used for storing information about messages without
a recognised type code. They are not instances of {\tt Message}, because
if they were, then clients could accidentally send invalid messages over
the bus.
:f DBus/Message/Internal.hs
data Unknown = Unknown
{ unknownType :: Word8
, unknownFlags :: S.Set Flag
, unknownBody :: [T.Variant]
}
deriving (Show, Eq)
:
:d message exports
, Unknown (..)
:
\subsection{Received messages}
Messages received from a bus have additional fields which do not make sense
when sending.
If a message has an unknown type, its serial and origin are still useful
for sending an error reply.
:f DBus/Message/Internal.hs
|apidoc ReceivedMessage|
data ReceivedMessage
= ReceivedMethodCall Serial (Maybe T.BusName) MethodCall
| ReceivedMethodReturn Serial (Maybe T.BusName) MethodReturn
| ReceivedError Serial (Maybe T.BusName) Error
| ReceivedSignal Serial (Maybe T.BusName) Signal
| ReceivedUnknown Serial (Maybe T.BusName) Unknown
deriving (Show, Eq)
:
:f DBus/Message/Internal.hs
receivedSerial :: ReceivedMessage -> Serial
receivedSerial (ReceivedMethodCall s _ _) = s
receivedSerial (ReceivedMethodReturn s _ _) = s
receivedSerial (ReceivedError s _ _) = s
receivedSerial (ReceivedSignal s _ _) = s
receivedSerial (ReceivedUnknown s _ _) = s
:
:f DBus/Message/Internal.hs
receivedSender :: ReceivedMessage -> Maybe T.BusName
receivedSender (ReceivedMethodCall _ s _) = s
receivedSender (ReceivedMethodReturn _ s _) = s
receivedSender (ReceivedError _ s _) = s
receivedSender (ReceivedSignal _ s _) = s
receivedSender (ReceivedUnknown _ s _) = s
:
:f DBus/Message/Internal.hs
receivedBody :: ReceivedMessage -> [T.Variant]
receivedBody (ReceivedMethodCall _ _ x) = messageBody x
receivedBody (ReceivedMethodReturn _ _ x) = messageBody x
receivedBody (ReceivedError _ _ x) = messageBody x
receivedBody (ReceivedSignal _ _ x) = messageBody x
receivedBody (ReceivedUnknown _ _ x) = unknownBody x
:
:d message exports
, ReceivedMessage (..)
, receivedSerial
, receivedSender
, receivedBody
: