packages feed

dbus-core-0.9: 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}

:d DBus.Message
class Message a where
	messageTypeCode     :: a -> Word8
	messageHeaderFields :: a -> [HeaderField]
	messageFlags        :: a -> Set Flag
	messageBody         :: a -> [Variant]
:

:d DBus.Message
maybe' :: (a -> b) -> Maybe a -> [b]
maybe' f = maybe [] (\x' -> [f x'])
:

\subsection{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.

:d DBus.Message
data Unknown = Unknown
	{ unknownType    :: Word8
	, unknownFlags   :: Set Flag
	, unknownBody    :: [Variant]
	}
	deriving (Show, Eq)
:

\clearpage
\subsection{Header fields}

\begin{multicols}{2}

TODO

\vfill

\columnbreak

:d DBus.Message
data HeaderField
	= HeaderPath        ObjectPath
	| HeaderInterface   InterfaceName
	| HeaderMember      MemberName
	| HeaderErrorName   ErrorName
	| HeaderReplySerial Serial
	| HeaderDestination BusName
	| HeaderSender      BusName
	| HeaderSignature   Signature
	deriving (Show, Eq)
:

\end{multicols}

\subsubsection{Flags}

\begin{multicols}{2}

The instance of {\tt Ord} only exists for storing flags in a set. Flags have
no inherent ordering.

\vfill

\columnbreak

:d DBus.Message
data Flag
	= NoReplyExpected
	| NoAutoStart
	deriving (Show, Eq, Ord)
:

\end{multicols}

\subsubsection{Serials}

\begin{multicols}{2}

{\tt Serial} is just a wrapper around {\tt Word32}, to provide a bit of
added type-safety.

\vfill

\columnbreak

:d DBus.Message
|apidoc DBus.Message.Serial|
newtype Serial = Serial Word32
	deriving (Eq, Ord, Show)

instance IsVariant Serial where
	toVariant (Serial x) = toVariant x
	fromVariant = fmap Serial . fromVariant
:

\end{multicols}

\begin{multicols}{2}

Additionally, some useful functions exist for incrementing serials.

\vfill

\columnbreak

:d DBus.Message
serialValue :: Serial -> Word32
serialValue (Serial x) = x
:

\end{multicols}

\begin{multicols}{2}

The {\tt Serial} constructor isn't useful to clients, because building
arbitrary serials doesn't make any sense.

\vfill

\columnbreak

\end{multicols}

\clearpage
\subsection{Message types}

\subsubsection{Method calls}

:d DBus.Message
data MethodCall = MethodCall
	{ methodCallPath        :: ObjectPath
	, methodCallMember      :: MemberName
	, methodCallInterface   :: Maybe InterfaceName
	, methodCallDestination :: Maybe BusName
	, methodCallFlags       :: Set Flag
	, methodCallBody        :: [Variant]
	}
	deriving (Show, Eq)

instance Message MethodCall where
	messageTypeCode _ = 1
	messageFlags      = methodCallFlags
	messageBody       = methodCallBody
	messageHeaderFields m = concat
		[ [ HeaderPath (methodCallPath m)
		  , HeaderMember (methodCallMember m)
		  ]
		, maybe' HeaderInterface (methodCallInterface m)
		, maybe' HeaderDestination (methodCallDestination m)
		]
:

\clearpage
\subsubsection{Method returns}

:d DBus.Message
data MethodReturn = MethodReturn
	{ methodReturnSerial      :: Serial
	, methodReturnDestination :: Maybe BusName
	, methodReturnBody        :: [Variant]
	}
	deriving (Show, Eq)

instance Message MethodReturn where
	messageTypeCode _ = 2
	messageFlags    _ = Data.Set.fromList [NoReplyExpected, NoAutoStart]
	messageBody       = methodReturnBody
	messageHeaderFields m = concat
		[ [ HeaderReplySerial (methodReturnSerial m)
		  ]
		, maybe' HeaderDestination (methodReturnDestination m)
		]
:

\clearpage
\subsubsection{Errors}

:d DBus.Message
data Error = Error
	{ errorName        :: ErrorName
	, errorSerial      :: Serial
	, errorDestination :: Maybe BusName
	, errorBody        :: [Variant]
	}
	deriving (Show, Eq)

instance Message Error where
	messageTypeCode _ = 3
	messageFlags    _ = Data.Set.fromList [NoReplyExpected, NoAutoStart]
	messageBody       = errorBody
	messageHeaderFields m = concat
		[ [ HeaderErrorName (errorName m)
		  , HeaderReplySerial (errorSerial m)
		  ]
		, maybe' HeaderDestination (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.

:d DBus.Message
errorMessage :: Error -> Text
errorMessage msg = fromMaybe "(no error message)" $ do
	field <- maybeIndex (errorBody msg) 0
	text <- fromVariant field
	if Data.Text.null text
		then Nothing
		else return text
:

\clearpage
\subsubsection{Signals}

:d DBus.Message
data Signal = Signal
	{ signalDestination :: Maybe BusName
	, signalPath        :: ObjectPath
	, signalInterface   :: InterfaceName
	, signalMember      :: MemberName
	, signalBody        :: [Variant]
	}
	deriving (Show, Eq)

instance Message Signal where
	messageTypeCode _ = 4
	messageFlags    _ = Data.Set.fromList [NoReplyExpected, NoAutoStart]
	messageBody       = signalBody
	messageHeaderFields m = concat
		[ [ HeaderPath (signalPath m)
		  , HeaderMember (signalMember m)
		  , HeaderInterface (signalInterface m)
		  ]
		, maybe' HeaderDestination (signalDestination m)
		]
:

\clearpage
\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.

:d DBus.Message
|apidoc DBus.Message.ReceivedMessage|
data ReceivedMessage
	= ReceivedMethodCall   Serial (Maybe BusName) MethodCall
	| ReceivedMethodReturn Serial (Maybe BusName) MethodReturn
	| ReceivedError        Serial (Maybe BusName) Error
	| ReceivedSignal       Serial (Maybe BusName) Signal
	| ReceivedUnknown      Serial (Maybe BusName) Unknown
	deriving (Show, Eq)
:

:d DBus.Message
receivedSerial :: ReceivedMessage -> Serial
receivedSerial (ReceivedMethodCall   s _ _) = s
receivedSerial (ReceivedMethodReturn s _ _) = s
receivedSerial (ReceivedError        s _ _) = s
receivedSerial (ReceivedSignal       s _ _) = s
receivedSerial (ReceivedUnknown      s _ _) = s
:

:d DBus.Message
receivedSender :: ReceivedMessage -> Maybe BusName
receivedSender (ReceivedMethodCall   _ s _) = s
receivedSender (ReceivedMethodReturn _ s _) = s
receivedSender (ReceivedError        _ s _) = s
receivedSender (ReceivedSignal       _ s _) = s
receivedSender (ReceivedUnknown      _ s _) = s
:

:d DBus.Message
receivedBody :: ReceivedMessage -> [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
: