packages feed

dbus-core 0.8.3.1 → 0.8.4

raw patch · 30 files changed

+2885/−1378 lines, 30 filesdep +libxml-saxdep +xml-typesdep −HaXmldep −monads-tfdep −prettydep ~parsecPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: libxml-sax, xml-types

Dependencies removed: HaXml, monads-tf, pretty, transformers

Dependency ranges changed: parsec

API changes (from Hackage documentation)

- DBus.Types: instance Eq Array
- DBus.Types: instance Eq BusName
- DBus.Types: instance Eq Dictionary
- DBus.Types: instance Eq ErrorName
- DBus.Types: instance Eq InterfaceName
- DBus.Types: instance Eq MemberName
- DBus.Types: instance Eq ObjectPath
- DBus.Types: instance Eq Signature
- DBus.Types: instance Eq Structure
- DBus.Types: instance Eq Type
- DBus.Types: instance Eq Variant
- DBus.Types: instance IsString BusName
- DBus.Types: instance IsString ErrorName
- DBus.Types: instance IsString InterfaceName
- DBus.Types: instance IsString MemberName
- DBus.Types: instance IsString ObjectPath
- DBus.Types: instance IsString Signature
- DBus.Types: instance Ord BusName
- DBus.Types: instance Ord ErrorName
- DBus.Types: instance Ord InterfaceName
- DBus.Types: instance Ord MemberName
- DBus.Types: instance Ord ObjectPath
- DBus.Types: instance Ord Signature
- DBus.Types: instance Show Array
- DBus.Types: instance Show BusName
- DBus.Types: instance Show Dictionary
- DBus.Types: instance Show ErrorName
- DBus.Types: instance Show InterfaceName
- DBus.Types: instance Show MemberName
- DBus.Types: instance Show ObjectPath
- DBus.Types: instance Show Signature
- DBus.Types: instance Show Structure
- DBus.Types: instance Show Type
- DBus.Types: instance Show Variant
- DBus.Types: instance Variable Array
- DBus.Types: instance Variable Bool
- DBus.Types: instance Variable BusName
- DBus.Types: instance Variable ByteString
- DBus.Types: instance Variable Dictionary
- DBus.Types: instance Variable Double
- DBus.Types: instance Variable ErrorName
- DBus.Types: instance Variable Int16
- DBus.Types: instance Variable Int32
- DBus.Types: instance Variable Int64
- DBus.Types: instance Variable InterfaceName
- DBus.Types: instance Variable MemberName
- DBus.Types: instance Variable ObjectPath
- DBus.Types: instance Variable Signature
- DBus.Types: instance Variable String
- DBus.Types: instance Variable Structure
- DBus.Types: instance Variable Text
- DBus.Types: instance Variable Variant
- DBus.Types: instance Variable Word16
- DBus.Types: instance Variable Word32
- DBus.Types: instance Variable Word64
- DBus.Types: instance Variable Word8
+ DBus.Introspection: instance Monad XmlWriter

Files

dbus-core.cabal view
@@ -1,5 +1,5 @@ name: dbus-core-version: 0.8.3.1+version: 0.8.4 synopsis: Low-level D-Bus protocol implementation license: GPL-3 license-file: License.txt@@ -34,18 +34,16 @@    build-depends:       base >=4.0 && < 5.0-    , parsec >= 3.0 && < 4+    , parsec >= 2.0 && < 3.2     , binary >= 0.4 && < 0.6     , bytestring >= 0.9 && < 0.10     , data-binary-ieee754 >= 0.3 && < 0.5-    , HaXml >= 1.20 && < 1.21-    , pretty >= 1.0 && < 1.1     , text >= 0.7 && < 0.8-    , transformers >= 0.2 && < 0.3-    , monads-tf >= 0.1 && < 0.2     , containers >= 0.1 && < 0.4     , unix >= 2.2 && < 2.5     , network >= 2.2 && < 2.3+    , libxml-sax >= 0.5 && < 0.6+    , xml-types >= 0.1 && < 0.2    exposed-modules:     DBus.Address@@ -63,6 +61,7 @@    other-modules:     DBus.Message.Internal+    DBus.Types.Internal     DBus.Wire.Internal     DBus.Wire.Marshal     DBus.Wire.Unmarshal@@ -86,11 +85,6 @@     ghc-options: -fhpc    other-modules:-    DBus.Address-    DBus.Constants-    DBus.Introspection-    DBus.Message-    DBus.Types-    DBus.Wire+    DBus.Types.Internal     DBus.Util 
hs/DBus/Address.hs view
@@ -1,3 +1,7 @@++#line 19 "src/addresses.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,7 +16,13 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 20 "src/addresses.anansi"++#line 52 "src/introduction.anansi" {-# LANGUAGE OverloadedStrings #-}++#line 21 "src/addresses.anansi" module DBus.Address 	( Address 	, addressMethod@@ -20,17 +30,25 @@ 	, mkAddresses 	, strAddress 	) where++#line 56 "src/introduction.anansi" import Data.Text.Lazy (Text) import qualified Data.Text.Lazy as TL +#line 29 "src/addresses.anansi"+ import Data.Char (ord, chr) import qualified Data.Map as M import Text.Printf (printf)-import qualified Text.Parsec as P-import Text.Parsec ((<|>))+import qualified Text.ParserCombinators.Parsec as P+import Text.ParserCombinators.Parsec ((<|>)) import DBus.Util (hexToInt, eitherToMaybe)++#line 74 "src/addresses.anansi" optionallyEncoded :: [Char] optionallyEncoded = ['0'..'9'] ++ ['a'..'z'] ++ ['A'..'Z'] ++ "-_/\\*."++#line 82 "src/addresses.anansi" data Address = Address 	{ addressMethod     :: Text 	, addressParameters :: M.Map Text Text@@ -39,6 +57,8 @@ instance Show Address where 	showsPrec d x = showParen (d> 10) $ 		showString "Address " . shows (strAddress x)++#line 97 "src/addresses.anansi" mkAddresses :: Text -> Maybe [Address] mkAddresses s = eitherToMaybe . P.parse parser "" . TL.unpack $ s where 	address = do@@ -63,6 +83,8 @@ 		P.char '%' 		hex <- P.count 2 P.hexDigit 		return . chr . hexToInt $ hex++#line 128 "src/addresses.anansi" strAddress :: Address -> Text strAddress (Address t ps) = TL.concat [t, ":", ps'] where 	ps' = TL.intercalate "," $ do
hs/DBus/Authentication.hs view
@@ -1,3 +1,7 @@++#line 27 "src/authentication.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,7 +16,13 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 28 "src/authentication.anansi"++#line 52 "src/introduction.anansi" {-# LANGUAGE OverloadedStrings #-}++#line 29 "src/authentication.anansi" {-# LANGUAGE DeriveDataTypeable #-} module DBus.Authentication 	( Command@@ -21,31 +31,54 @@ 	, authenticate 	, realUserID 	) where++#line 56 "src/introduction.anansi" import Data.Text.Lazy (Text) import qualified Data.Text.Lazy as TL++#line 38 "src/authentication.anansi"++#line 45 "src/authentication.anansi" import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString.Lazy as ByteString+import Data.ByteString.Lazy.Char8 () import qualified DBus.UUID as UUID++#line 61 "src/authentication.anansi" import Data.Typeable (Typeable) import qualified Control.Exception as E++#line 78 "src/authentication.anansi" import Data.Word (Word8)++#line 94 "src/authentication.anansi" import Control.Monad (liftM) import Data.Char (chr) import Data.Text.Lazy.Encoding (encodeUtf8) import DBus.Util (readUntil, dropEnd)++#line 115 "src/authentication.anansi" import System.Posix.User (getRealUserID) import Data.Char (ord) import Text.Printf (printf)++#line 136 "src/authentication.anansi" import Data.Maybe (isJust)++#line 52 "src/authentication.anansi" type Command = Text newtype Mechanism = Mechanism 	{ mechanismRun :: (Command -> IO Command) -> IO UUID.UUID 	}++#line 66 "src/authentication.anansi" data AuthenticationError 	= AuthenticationError Text 	deriving (Show, Typeable)  instance E.Exception AuthenticationError++#line 82 "src/authentication.anansi" authenticate :: Mechanism -> (ByteString -> IO ()) -> IO Word8              -> IO UUID.UUID authenticate mech put getByte = do@@ -53,12 +86,16 @@ 	uuid <- mechanismRun mech (putCommand put getByte) 	put "BEGIN\r\n" 	return uuid++#line 101 "src/authentication.anansi" putCommand :: Monad m => (ByteString -> m ()) -> m Word8 -> Command -> m Command putCommand put get cmd = do 	let getC = liftM (chr . fromIntegral) get 	put $ encodeUtf8 cmd 	put "\r\n" 	liftM (TL.pack . dropEnd 2) $ readUntil "\r\n" getC++#line 121 "src/authentication.anansi" realUserID :: Mechanism realUserID = Mechanism $ \sendCmd -> do 	uid <- getRealUserID@@ -68,6 +105,8 @@ 	case eitherUUID of 		Right uuid -> return uuid 		Left err -> E.throwIO $ AuthenticationError err++#line 140 "src/authentication.anansi" checkOK :: Command -> Either Text UUID.UUID checkOK cmd = if validUUID then Right uuid else Left errorMsg where 	validUUID = TL.isPrefixOf "OK " cmd && isJust maybeUUID
hs/DBus/Bus.hs view
@@ -1,3 +1,7 @@++#line 19 "src/bus.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,7 +16,13 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 20 "src/bus.anansi"++#line 52 "src/introduction.anansi" {-# LANGUAGE OverloadedStrings #-}++#line 21 "src/bus.anansi" module DBus.Bus 	( getBus 	, getFirstBus@@ -20,9 +30,13 @@ 	, getSessionBus 	, getStarterBus 	) where++#line 56 "src/introduction.anansi" import Data.Text.Lazy (Text) import qualified Data.Text.Lazy as TL +#line 29 "src/bus.anansi"+ import qualified Control.Exception as E import Control.Monad (when) import Data.Maybe (fromJust, isNothing)@@ -36,36 +50,64 @@ import qualified DBus.Message as M import qualified DBus.Types as T import DBus.Util (fromRight)++#line 50 "src/bus.anansi" busForConnection :: C.Connection -> IO (C.Connection, T.BusName) busForConnection c = fmap ((,) c) $ sendHello c ++#line 99 "src/api-docs.anansi" -- | Similar to 'C.connect', but additionally sends @Hello@ messages to the -- central bus.++#line 54 "src/bus.anansi" getBus :: Auth.Mechanism -> A.Address -> IO (C.Connection, T.BusName) getBus = ((busForConnection =<<) .) . C.connect++#line 62 "src/bus.anansi"++#line 104 "src/api-docs.anansi" -- | Similar to 'C.connectFirst', but additionally sends @Hello@ messages to -- the central bus.++#line 63 "src/bus.anansi" getFirstBus :: [(Auth.Mechanism, A.Address)] -> IO (C.Connection, T.BusName) getFirstBus = (busForConnection =<<) . C.connectFirst++#line 74 "src/bus.anansi"++#line 109 "src/api-docs.anansi" -- | Connect to the bus specified in the environment variable -- @DBUS_SYSTEM_BUS_ADDRESS@, or to -- @unix:path=\/var\/run\/dbus\/system_bus_socket@ if @DBUS_SYSTEM_BUS_ADDRESS@ -- is not set.++#line 75 "src/bus.anansi" getSystemBus :: IO (C.Connection, T.BusName) getSystemBus = getBus' $ fromEnv `E.catch` noEnv where 	defaultAddr = "unix:path=/var/run/dbus/system_bus_socket" 	fromEnv = getEnv "DBUS_SYSTEM_BUS_ADDRESS" 	noEnv (E.SomeException _) = return defaultAddr ++#line 116 "src/api-docs.anansi" -- | Connect to the bus specified in the environment variable -- @DBUS_SESSION_BUS_ADDRESS@, which must be set.++#line 82 "src/bus.anansi" getSessionBus :: IO (C.Connection, T.BusName) getSessionBus = getBus' $ getEnv "DBUS_SESSION_BUS_ADDRESS" ++#line 121 "src/api-docs.anansi" -- | Connect to the bus specified in the environment variable -- @DBUS_STARTER_ADDRESS@, which must be set.++#line 86 "src/bus.anansi" getStarterBus :: IO (C.Connection, T.BusName) getStarterBus = getBus' $ getEnv "DBUS_STARTER_ADDRESS"++#line 91 "src/bus.anansi" getBus' :: IO String -> IO (C.Connection, T.BusName) getBus' io = do 	addr <- fmap TL.pack io@@ -73,6 +115,8 @@ 		Just [x] -> getBus Auth.realUserID x 		Just  xs -> getFirstBus [(Auth.realUserID,x) | x <- xs] 		_        -> E.throwIO $ C.InvalidAddress addr++#line 103 "src/bus.anansi" hello :: M.MethodCall hello = M.MethodCall dbusPath 	"Hello"@@ -80,6 +124,8 @@ 	(Just dbusName) 	Set.empty 	[]++#line 113 "src/bus.anansi" sendHello :: C.Connection -> IO T.BusName sendHello c = do 	serial <- fromRight `fmap` C.send c return hello@@ -92,6 +138,8 @@ 		E.throwIO $ E.AssertionFailed "Invalid response to Hello()" 	 	return . fromJust $ name++#line 128 "src/bus.anansi" waitForReply :: C.Connection -> M.Serial -> IO M.MethodReturn waitForReply c serial = do 	received <- C.receive c
hs/DBus/Connection.hs view
@@ -1,3 +1,7 @@++#line 19 "src/connections.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,39 +16,83 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 20 "src/connections.anansi"++#line 52 "src/introduction.anansi" {-# LANGUAGE OverloadedStrings #-}++#line 21 "src/connections.anansi" {-# LANGUAGE DeriveDataTypeable #-} module DBus.Connection (++#line 53 "src/connections.anansi" 	  Connection 	, connectionAddress 	, connectionUUID++#line 288 "src/connections.anansi" 	, ConnectionError (..)++#line 327 "src/connections.anansi" 	, connect 	, connectFirst++#line 340 "src/connections.anansi" 	, connectionClose++#line 370 "src/connections.anansi" 	, send++#line 394 "src/connections.anansi" 	, receive++#line 24 "src/connections.anansi" 	) where++#line 56 "src/introduction.anansi" import Data.Text.Lazy (Text) import qualified Data.Text.Lazy as TL++#line 26 "src/connections.anansi"++#line 36 "src/connections.anansi" import qualified Control.Concurrent as C import qualified DBus.Address as A import qualified DBus.Message as M import qualified DBus.UUID as UUID++#line 75 "src/connections.anansi" import qualified Data.ByteString.Lazy as L import Data.Word (Word32)++#line 107 "src/connections.anansi" import qualified Network as N import qualified Data.Map as Map++#line 147 "src/connections.anansi" import qualified Network.Socket as NS-import qualified Text.Parsec as P++#line 193 "src/connections.anansi"+import qualified Text.ParserCombinators.Parsec as P import Control.Monad (unless) import Data.Binary.Get (runGet, getWord16host) import Data.Binary.Put (runPut, putWord16be)++#line 255 "src/connections.anansi" import qualified System.IO as I++#line 272 "src/connections.anansi" import qualified Control.Exception as E import Data.Typeable (Typeable)++#line 297 "src/connections.anansi" import qualified DBus.Authentication as Auth++#line 353 "src/connections.anansi" import qualified DBus.Wire as W++#line 43 "src/connections.anansi" data Connection = Connection 	{ connectionAddress    :: A.Address 	, connectionTransport  :: Transport@@ -52,23 +100,35 @@ 	, connectionReadMVar   :: C.MVar () 	, connectionUUID       :: UUID.UUID 	}++#line 62 "src/connections.anansi" instance Show Connection where 	showsPrec d con = showParen (d > 10) strCon where 		addr = A.strAddress $ connectionAddress con 		strCon = s "<Connection " . shows addr . s ">" 		s = showString++#line 80 "src/connections.anansi"++#line 59 "src/api-docs.anansi" -- | A 'Transport' is anything which can send and receive bytestrings, -- typically via a socket.++#line 81 "src/connections.anansi" data Transport = Transport 	{ transportSend :: L.ByteString -> IO () 	, transportRecv :: Word32 -> IO L.ByteString 	, transportClose :: IO () 	}++#line 92 "src/connections.anansi" connectTransport :: A.Address -> IO Transport connectTransport a = transport' (A.addressMethod a) a where 	transport' "unix" = unix 	transport' "tcp"  = tcp 	transport' _      = E.throwIO . UnknownMethod++#line 112 "src/connections.anansi" unix :: A.Address -> IO Transport unix a = port >>= N.connectTo "localhost" >>= handleTransport where 	params = A.addressParameters a@@ -86,6 +146,8 @@ 		(Nothing, Nothing) -> E.throwIO $ BadParameters a tooFew 		(Just x, Nothing) -> return $ TL.unpack x 		(Nothing, Just x) -> return $ '\x00' : TL.unpack x++#line 151 "src/connections.anansi" tcp :: A.Address -> IO Transport tcp a = openHandle >>= handleTransport where 	params = A.addressParameters a@@ -95,13 +157,19 @@ 		addresses <- getAddresses family 		socket <- openSocket port addresses 		NS.socketToHandle socket I.ReadWriteMode++#line 165 "src/connections.anansi" 	hostname = maybe "localhost" TL.unpack $ Map.lookup "host" params++#line 169 "src/connections.anansi" 	unknownFamily x = TL.concat ["Unknown socket family for TCP transport: ", x] 	getFamily = case Map.lookup "family" params of 		Just "ipv4" -> return NS.AF_INET 		Just "ipv6" -> return NS.AF_INET6 		Nothing     -> return NS.AF_UNSPEC 		Just x      -> E.throwIO $ BadParameters a $ unknownFamily x++#line 178 "src/connections.anansi" 	missingPort = "TCP transport requires the ``port'' parameter." 	badPort x = TL.concat ["Invalid socket port for TCP transport: ", x] 	getPort = case Map.lookup "port" params of@@ -109,14 +177,21 @@ 		Just x -> case P.parse parseWord16 "" (TL.unpack x) of 			Right x' -> return $ NS.PortNum x' 			Left  _  -> E.throwIO $ BadParameters a $ badPort x++#line 200 "src/connections.anansi" 	parseWord16 = do 		chars <- P.many1 P.digit 		P.eof 		let value = read chars :: Integer 		unless (value > 0 && value <= 65535) $-			P.parserFail "bad port" >> return ()+			-- Calling 'fail' is acceptable here, because Parsec 2+			-- offers no other error reporting mechanism, and+			-- implements 'fail'.+			fail "bad port" 		let word = fromIntegral value 		return $ runGet getWord16host (runPut (putWord16be word))++#line 214 "src/connections.anansi" 	getAddresses family = do 		let hints = NS.defaultHints 			{ NS.addrFlags = [NS.AI_ADDRCONFIG]@@ -124,9 +199,13 @@ 			, NS.addrSocketType = NS.Stream 			} 		NS.getAddrInfo (Just hints) (Just hostname) Nothing++#line 228 "src/connections.anansi" 	setPort port (NS.SockAddrInet  _ x)     = NS.SockAddrInet port x 	setPort port (NS.SockAddrInet6 _ x y z) = NS.SockAddrInet6 port x y z 	setPort _    addr                       = addr++#line 238 "src/connections.anansi" 	openSocket _ [] = E.throwIO $ NoWorkingAddress [a] 	openSocket port (addr:addrs) = E.catch (openSocket' port addr) $ 		\(E.SomeException _) -> openSocket port addrs@@ -136,11 +215,15 @@ 		                  (NS.addrProtocol addr) 		NS.connect sock . setPort port . NS.addrAddress $ addr 		return sock++#line 259 "src/connections.anansi" handleTransport :: I.Handle -> IO Transport handleTransport h = do 	I.hSetBuffering h I.NoBuffering 	I.hSetBinaryMode h True 	return $ Transport (L.hPut h) (L.hGet h . fromIntegral) (I.hClose h)++#line 277 "src/connections.anansi" data ConnectionError 	= InvalidAddress Text 	| BadParameters A.Address Text@@ -149,8 +232,14 @@ 	deriving (Show, Typeable)  instance E.Exception ConnectionError++#line 301 "src/connections.anansi"++#line 64 "src/api-docs.anansi" -- | Open a connection to some address, using a given authentication -- mechanism. If the connection fails, a 'ConnectionError' will be thrown.++#line 302 "src/connections.anansi" connect :: Auth.Mechanism -> A.Address -> IO Connection connect mechanism a = do 	t <- connectTransport a@@ -159,18 +248,34 @@ 	readLock <- C.newMVar () 	serialMVar <- C.newMVar M.firstSerial 	return $ Connection a t serialMVar readLock uuid++#line 317 "src/connections.anansi"++#line 69 "src/api-docs.anansi" -- | Try to open a connection to various addresses, returning the first -- connection which could be successfully opened.++#line 318 "src/connections.anansi" connectFirst :: [(Auth.Mechanism, A.Address)] -> IO Connection connectFirst orig = connectFirst' orig where 	allAddrs = [a | (_, a) <- orig] 	connectFirst'     [] = E.throwIO $ NoWorkingAddress allAddrs 	connectFirst' ((mech, a):as) = E.catch (connect mech a) $ 		\(E.SomeException _) -> connectFirst' as++#line 334 "src/connections.anansi"++#line 74 "src/api-docs.anansi" -- | Close an open connection. Once closed, the 'Connection' is no longer -- valid and must not be used.++#line 335 "src/connections.anansi" connectionClose :: Connection -> IO () connectionClose = transportClose . connectionTransport++#line 357 "src/connections.anansi"++#line 79 "src/api-docs.anansi" -- | Send a single message, with a generated 'M.Serial'. The second parameter -- exists to prevent race conditions when registering a reply handler; it -- receives the serial the message /will/ be sent with, before it's actually@@ -179,6 +284,8 @@ -- Only one message may be sent at a time; if multiple threads attempt to -- send messages in parallel, one will block until after the other has -- finished.++#line 358 "src/connections.anansi" send :: M.Message a => Connection -> (M.Serial -> IO b) -> a      -> IO (Either W.MarshalError b) send (Connection _ t mvar _ _) io msg = withSerial mvar $ \serial ->@@ -188,6 +295,8 @@ 			transportSend t bytes 			return $ Right x 		Left  err   -> return $ Left err++#line 374 "src/connections.anansi" withSerial :: C.MVar M.Serial -> (M.Serial -> IO a) -> IO a withSerial m io = E.block $ do 	s <- C.takeMVar m@@ -195,12 +304,18 @@ 	x <- E.unblock (io s) `E.onException` C.putMVar m s' 	C.putMVar m s' 	return x++#line 387 "src/connections.anansi"++#line 90 "src/api-docs.anansi" -- | Receive the next message from the connection, blocking until one is -- available. -- -- Only one message may be received at a time; if multiple threads attempt -- to receive messages in parallel, one will block until after the other has -- finished.++#line 388 "src/connections.anansi" receive :: Connection -> IO (Either W.UnmarshalError M.ReceivedMessage) receive (Connection _ t _ lock _) = C.withMVar lock $ \_ -> 	W.unmarshalMessage $ transportRecv t
hs/DBus/Constants.hs view
@@ -1,3 +1,7 @@++#line 19 "src/constants.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,6 +16,8 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 20 "src/constants.anansi" {-# LANGUAGE OverloadedStrings #-} module DBus.Constants where import qualified DBus.Types as T@@ -25,6 +31,8 @@  arrayMaximumLength :: Word32 arrayMaximumLength = 67108864++#line 38 "src/constants.anansi" dbusName :: T.BusName dbusName = "org.freedesktop.DBus" @@ -33,6 +41,8 @@  dbusInterface :: T.InterfaceName dbusInterface = "org.freedesktop.DBus"++#line 51 "src/constants.anansi" interfaceIntrospectable :: T.InterfaceName interfaceIntrospectable = "org.freedesktop.DBus.Introspectable" @@ -41,6 +51,8 @@  interfacePeer :: T.InterfaceName interfacePeer = "org.freedesktop.DBus.Peer"++#line 64 "src/constants.anansi" errorFailed :: T.ErrorName errorFailed = "org.freedesktop.DBus.Error.Failed" 
hs/DBus/Introspection.hs view
@@ -1,3 +1,7 @@++#line 44 "src/introspection.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,7 +16,13 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 45 "src/introspection.anansi"++#line 52 "src/introduction.anansi" {-# LANGUAGE OverloadedStrings #-}++#line 46 "src/introspection.anansi" module DBus.Introspection 	( Object (..) 	, Interface (..)@@ -24,16 +34,28 @@ 	, toXML 	, fromXML 	) where++#line 56 "src/introduction.anansi" import Data.Text.Lazy (Text) import qualified Data.Text.Lazy as TL-import qualified Text.XML.HaXml as H-import Text.XML.HaXml.Parse (xmlParse')-import DBus.Util (eitherToMaybe)-import Data.Char (chr)-import Data.Maybe (fromMaybe)-import Text.XML.HaXml.Pretty (document)-import Text.PrettyPrint.HughesPJ (render)++#line 58 "src/introspection.anansi"++#line 105 "src/introspection.anansi"+import qualified Data.XML.Types as X+import qualified Text.XML.LibXML.SAX as SAX+import Control.Monad.ST (runST)+import qualified Data.STRef as ST+import Data.Text.Encoding (encodeUtf8)++#line 254 "src/introspection.anansi"+import Control.Monad ((>=>))+import Data.Maybe (fromMaybe, listToMaybe)++#line 59 "src/introspection.anansi" import qualified DBus.Types as T++#line 65 "src/introspection.anansi" data Object = Object T.ObjectPath [Interface] [Object] 	deriving (Show, Eq) @@ -54,176 +76,269 @@  data PropertyAccess = Read | Write 	deriving (Show, Eq)++#line 94 "src/introspection.anansi" fromXML :: T.ObjectPath -> Text -> Maybe Object fromXML path text = do-	doc <- eitherToMaybe . xmlParse' "" . TL.unpack $ text-	let (H.Document _ _ root _) = doc+	root <- parseElement text 	parseRoot path root-parseRoot :: T.ObjectPath -> H.Element a -> Maybe Object++#line 113 "src/introspection.anansi"+parseElement :: Text -> Maybe X.Element+parseElement text = runST $ do+	stackRef <- ST.newSTRef [([], [])]+	let callbacks = SAX.ParserCallbacks+		{ SAX.parsedBeginElement = \_ attrs -> do+			ST.modifySTRef stackRef ((attrs, []):)+		, SAX.parsedEndElement = \name -> do+			stack <- ST.readSTRef stackRef+			let (attrs, children'):stack' = stack+			let e = X.Element name attrs (map X.NodeElement (reverse children'))+			let (pAttrs, pChildren):stack'' = stack'+			let parent = (pAttrs, e:pChildren)+			ST.writeSTRef stackRef (parent:stack'')+		, SAX.parsedCharacters = \_ -> return ()+		, SAX.parsedComment = \_ -> return ()+		, SAX.parsedProcessingInstruction = \_ -> return ()+		}+	let onError _ = ST.writeSTRef stackRef []+	p <- SAX.newParser callbacks onError Nothing+	mapM_ (SAX.parse p . encodeUtf8) $ TL.toChunks text+	SAX.parseComplete p+	stack <- ST.readSTRef stackRef+	return $ case stack of+		[] -> Nothing+		(_, children'):_ -> Just $ head children'++#line 148 "src/introspection.anansi"+parseRoot :: T.ObjectPath -> X.Element -> Maybe Object parseRoot defaultPath e = do-	path <- case getAttr "name" e of+	path <- case getattrM "name" e of 		Nothing -> Just defaultPath 		Just x  -> T.mkObjectPath x-	parseObject' path e-parseChild :: T.ObjectPath -> H.Element a -> Maybe Object+	parseObject path e++#line 161 "src/introspection.anansi"+parseChild :: T.ObjectPath -> X.Element -> Maybe Object parseChild parentPath e = do 	let parentPath' = case T.strObjectPath parentPath of 		"/" -> "/" 		x   -> TL.append x "/"-	pathSegment <- getAttr "name" e+	pathSegment <- getattrM "name" e 	path <- T.mkObjectPath $ TL.append parentPath' pathSegment-	parseObject' path e-parseObject' :: T.ObjectPath -> H.Element a -> Maybe Object-parseObject' path e@(H.Elem "node" _ _)  = do-	interfaces <- children parseInterface (H.tag "interface") e-	children' <- children (parseChild path) (H.tag "node") e+	parseObject path e++#line 175 "src/introspection.anansi"+parseObject :: T.ObjectPath -> X.Element -> Maybe Object+parseObject path e | X.elementName e == toName "node" = do+	interfaces <- children parseInterface (named "interface") e+	children' <- children (parseChild path) (named "node") e 	return $ Object path interfaces children'-parseObject' _ _ = Nothing-parseInterface :: H.Element a -> Maybe Interface+parseObject _ _ = Nothing++#line 186 "src/introspection.anansi"+parseInterface :: X.Element -> Maybe Interface parseInterface e = do-	name <- T.mkInterfaceName =<< getAttr "name" e-	methods <- children parseMethod (H.tag "method") e-	signals <- children parseSignal (H.tag "signal") e-	properties <- children parseProperty (H.tag "property") e+	name <- T.mkInterfaceName =<< getattrM "name" e+	methods <- children parseMethod (named "method") e+	signals <- children parseSignal (named "signal") e+	properties <- children parseProperty (named "property") e 	return $ Interface name methods signals properties-parseMethod :: H.Element a -> Maybe Method++#line 199 "src/introspection.anansi"+parseMethod :: X.Element -> Maybe Method parseMethod e = do-	name <- T.mkMemberName =<< getAttr "name" e+	name <- T.mkMemberName =<< getattrM "name" e 	paramsIn <- children parseParameter (isParam ["in", ""]) e 	paramsOut <- children parseParameter (isParam ["out"]) e 	return $ Method name paramsIn paramsOut-parseSignal :: H.Element a -> Maybe Signal++#line 210 "src/introspection.anansi"+parseSignal :: X.Element -> Maybe Signal parseSignal e = do-	name <- T.mkMemberName =<< getAttr "name" e+	name <- T.mkMemberName =<< getattrM "name" e 	params <- children parseParameter (isParam ["out", ""]) e 	return $ Signal name params-parseParameter :: H.Element a -> Maybe Parameter++#line 220 "src/introspection.anansi"+parseParameter :: X.Element -> Maybe Parameter parseParameter e = do-	let name = getAttr' "name" e+	let name = getattr "name" e 	sig <- parseType e 	return $ Parameter name sig-parseType :: H.Element a -> Maybe T.Signature++#line 228 "src/introspection.anansi"+parseType :: X.Element -> Maybe T.Signature parseType e = do-	sig <- T.mkSignature =<< getAttr "type" e+	sig <- T.mkSignature =<< getattrM "type" e 	case T.signatureTypes sig of 		[_] -> Just sig 		_   -> Nothing-parseProperty :: H.Element a -> Maybe Property++#line 240 "src/introspection.anansi"+parseProperty :: X.Element -> Maybe Property parseProperty e = do-	let name = getAttr' "name" e+	let name = getattr "name" e 	sig <- parseType e-	access <- case getAttr' "access" e of+	access <- case getattr "access" e of 		""          -> Just [] 		"read"      -> Just [Read] 		"write"     -> Just [Write] 		"readwrite" -> Just [Read, Write] 		_           -> Nothing 	return $ Property name sig access-attrValue :: H.AttValue -> Maybe Text-attrValue attr = fmap (TL.pack . concat) $ mapM unescape parts where-	(H.AttValue parts) = attr-	-	unescape (Left x) = Just x-	unescape (Right (H.RefEntity x)) = lookup x namedRefs-	unescape (Right (H.RefChar x)) = Just [chr x]-	-	namedRefs =-		[ ("lt", "<")-		, ("gt", ">")-		, ("amp", "&")-		, ("apos", "'")-		, ("quot", "\"")-		]-getAttr :: String -> H.Element a -> Maybe Text-getAttr name (H.Elem _ attrs _) = lookup name attrs >>= attrValue -getAttr' :: String -> H.Element a -> Text-getAttr' = (fromMaybe "" .) . getAttr+#line 259 "src/introspection.anansi"+getattrM :: Text -> X.Element -> Maybe Text+getattrM name = fmap attrText . listToMaybe . attrs where+	attrText = textContent . X.attributeContent+	attrs = X.elementAttributes >=> X.isNamed (toName name)+	textContent cs = TL.concat [t | X.ContentText t <- cs] -isParam :: [Text] -> H.CFilter a-isParam dirs content = do-	arg@(H.CElem e _) <- H.tag "arg" content-	let direction = getAttr' "direction" e-	[arg | direction `elem` dirs]+getattr :: Text -> X.Element -> Text+getattr = (fromMaybe "" .) . getattrM -children :: Monad m => (H.Element a -> m b) -> H.CFilter a -> H.Element a -> m [b]-children f filt (H.Elem _ _ contents) =-	mapM f [x | (H.CElem x _) <- concatMap filt contents]-dtdPublicID, dtdSystemID :: String-dtdPublicID = "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"-dtdSystemID = "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"+isParam :: [Text] -> X.Element -> [X.Element]+isParam dirs = named "arg" >=> checkDir where+	checkDir e = [e | getattr "direction" e `elem` dirs]++children :: Monad m => (X.Element -> m b) -> (X.Element -> [X.Element]) -> X.Element -> m [b]+children f p = mapM f . concatMap p . X.elementChildren++named :: X.Named a => Text -> a -> [a]+named = X.isNamed . toName++toName :: Text -> X.Name+toName t = X.Name t Nothing Nothing++#line 293 "src/introspection.anansi"+newtype XmlWriter a = XmlWriter { runXmlWriter :: Maybe (a, Text) }++instance Monad XmlWriter where+	return a = XmlWriter $ Just (a, TL.empty)+	m >>= f = XmlWriter $ do+		(a, w) <- runXmlWriter m+		(b, w') <- runXmlWriter (f a)+		return (b, TL.append w w')++#line 304 "src/introspection.anansi"+tell :: Text -> XmlWriter ()+tell t = XmlWriter $ Just ((), t)++#line 309 "src/introspection.anansi" toXML :: Object -> Maybe Text-toXML obj = fmap (TL.pack . render . document) doc where-	prolog = H.Prolog Nothing [] (Just doctype) []-	doctype = H.DTD "node" (Just (H.PUBLIC-		(H.PubidLiteral dtdPublicID)-		(H.SystemLiteral dtdSystemID))) []-	doc = do-		root <- xmlRoot obj-		return $ H.Document prolog H.emptyST root []-xmlRoot :: Object -> Maybe (H.Element a)-xmlRoot obj@(Object path _ _) = do-	(H.CElem root _) <- xmlObject' (T.strObjectPath path) obj-	return root-xmlObject :: T.ObjectPath -> Object -> Maybe (H.Content a)-xmlObject parentPath obj@(Object path _ _) = do-	let path' = T.strObjectPath path-	    parent' = T.strObjectPath parentPath-	relpath <- if TL.isPrefixOf parent' path'+toXML obj = do+	(_, text) <- runXmlWriter (writeRoot obj)+	return text++#line 316 "src/introspection.anansi"+writeRoot :: Object -> XmlWriter ()+writeRoot obj@(Object path _ _) = do+	tell "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'"+	tell " 'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>\n"+	writeObject (T.strObjectPath path) obj++#line 324 "src/introspection.anansi"+writeChild :: T.ObjectPath -> Object -> XmlWriter ()+writeChild parentPath obj@(Object path _ _) = write where+	path' = T.strObjectPath path+	parent' = T.strObjectPath parentPath+	relpathM = if TL.isPrefixOf parent' path' 		then Just $ if parent' == "/" 			then TL.drop 1 path' 			else TL.drop (TL.length parent' + 1) path' 		else Nothing-	xmlObject' relpath obj-xmlObject' :: Text -> Object -> Maybe (H.Content a)-xmlObject' path (Object fullPath interfaces children') = do-	children'' <- mapM (xmlObject fullPath) children'-	return $ mkElement "node"-		[mkAttr "name" $ TL.unpack path]-		$ concat-			[ map xmlInterface interfaces-			, children''-			]-xmlInterface :: Interface -> H.Content a-xmlInterface (Interface name methods signals properties) =-	mkElement "interface"-		[mkAttr "name" . TL.unpack . T.strInterfaceName $ name]-		$ concat-			[ map xmlMethod methods-			, map xmlSignal signals-			, map xmlProperty properties-			]-xmlMethod :: Method -> H.Content a-xmlMethod (Method name inParams outParams) = mkElement "method"-	[mkAttr "name" . TL.unpack . T.strMemberName $ name]-	$ concat-		[ map (xmlParameter "in") inParams-		, map (xmlParameter "out") outParams-		]-xmlSignal :: Signal -> H.Content a-xmlSignal (Signal name params) = mkElement "signal"-	[mkAttr "name" . TL.unpack . T.strMemberName $ name]-	$ map (xmlParameter "out") params-xmlParameter :: String -> Parameter -> H.Content a-xmlParameter direction (Parameter name sig) = mkElement "arg"-	[ mkAttr "name" . TL.unpack $ name-	, mkAttr "type" . TL.unpack . T.strSignature $ sig-	, mkAttr "direction" direction-	] []-xmlProperty :: Property -> H.Content a-xmlProperty (Property name sig access) = mkElement "property"-	[ mkAttr "name" . TL.unpack $ name-	, mkAttr "type" . TL.unpack . T.strSignature $ sig-	, mkAttr "access" $ xmlAccess access-	] []-xmlAccess :: [PropertyAccess] -> String-xmlAccess access = readS ++ writeS where+	+	write = case relpathM of+		Just relpath -> writeObject relpath obj+		Nothing -> XmlWriter Nothing++#line 340 "src/introspection.anansi"+writeObject :: Text -> Object -> XmlWriter ()+writeObject path (Object fullPath interfaces children') = writeElement "node"+	[("name", path)] $ do+		mapM_ writeInterface interfaces+		mapM_ (writeChild fullPath) children'++#line 348 "src/introspection.anansi"+writeInterface :: Interface -> XmlWriter ()+writeInterface (Interface name methods signals properties) = writeElement "interface"+	[("name", T.strInterfaceName name)] $ do+		mapM_ writeMethod methods+		mapM_ writeSignal signals+		mapM_ writeProperty properties++#line 357 "src/introspection.anansi"+writeMethod :: Method -> XmlWriter ()+writeMethod (Method name inParams outParams) = writeElement "method"+	[("name", T.strMemberName name)] $ do+		mapM_ (writeParameter "in") inParams+		mapM_ (writeParameter "out") outParams++#line 365 "src/introspection.anansi"+writeSignal :: Signal -> XmlWriter ()+writeSignal (Signal name params) = writeElement "signal"+	[("name", T.strMemberName name)] $ do+		mapM_ (writeParameter "out") params++#line 372 "src/introspection.anansi"+writeParameter :: Text -> Parameter -> XmlWriter ()+writeParameter direction (Parameter name sig) = writeEmptyElement "arg"+	[ ("name", name)+	, ("type", T.strSignature sig)+	, ("direction", direction)+	]++#line 381 "src/introspection.anansi"+writeProperty :: Property -> XmlWriter ()+writeProperty (Property name sig access) = writeEmptyElement "property"+	[ ("name", name)+	, ("type", T.strSignature sig)+	, ("access", strAccess access)+	]++#line 390 "src/introspection.anansi"+strAccess :: [PropertyAccess] -> Text+strAccess access = TL.append readS writeS where 	readS = if elem Read access then "read" else "" 	writeS = if elem Write access then "write" else ""-mkElement :: String -> [H.Attribute] -> [H.Content a] -> H.Content a-mkElement name attrs contents = H.CElem (H.Elem name attrs contents) undefined -mkAttr :: String -> String -> H.Attribute-mkAttr name value = (name, H.AttValue [Left escaped]) where-	raw = H.CString True value ()-	escaped = H.verbatim $ H.xmlEscapeContent H.stdXmlEscaper [raw]+#line 397 "src/introspection.anansi"+writeElement :: Text -> [(Text, Text)] -> XmlWriter () -> XmlWriter ()+writeElement name attrs content = do+	tell "<"+	tell name+	mapM_ writeAttribute attrs+	tell ">"+	content+	tell "</"+	tell name+	tell ">"++#line 410 "src/introspection.anansi"+writeEmptyElement :: Text -> [(Text, Text)] -> XmlWriter ()+writeEmptyElement name attrs = do+	tell "<"+	tell name+	mapM_ writeAttribute attrs+	tell "/>"++#line 419 "src/introspection.anansi"+writeAttribute :: (Text, Text) -> XmlWriter ()+writeAttribute (name, content) = do+	tell " "+	tell name+	tell "='"+	tell (escape content)+	tell "'"++#line 429 "src/introspection.anansi"+escape :: Text -> Text+escape = TL.concatMap escapeChar where+	escapeChar c = case c of+		'&' -> "&amp;"+		'<' -> "&lt;"+		'>' -> "&gt;"+		'"' -> "&quot;"+		'\'' -> "&apos;"+		_ -> TL.singleton c
hs/DBus/MatchRule.hs view
@@ -1,3 +1,7 @@++#line 23 "src/match-rules.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,7 +16,13 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 24 "src/match-rules.anansi"++#line 52 "src/introduction.anansi" {-# LANGUAGE OverloadedStrings #-}++#line 25 "src/match-rules.anansi" module DBus.MatchRule ( 	  MatchRule (..) 	, MessageType (..)@@ -22,8 +32,12 @@ 	, matchAll 	, matches 	) where++#line 56 "src/introduction.anansi" import Data.Text.Lazy (Text) import qualified Data.Text.Lazy as TL++#line 35 "src/match-rules.anansi" import Data.Word (Word8) import Data.Maybe (fromMaybe, mapMaybe) import qualified Data.Set as Set@@ -31,6 +45,8 @@ import qualified DBus.Message as M import qualified DBus.Constants as C import DBus.Util (maybeIndex)++#line 45 "src/match-rules.anansi" -- | A match rule is a set of filters; most filters may have one possible -- value assigned, such as a single message type. The exception are parameter -- filters, which are limited in number only by the server implementation.@@ -45,6 +61,8 @@ 	, matchParameters  :: [ParameterValue] 	} 	deriving (Show)++#line 62 "src/match-rules.anansi" -- | Parameters may match against two types, strings and object paths. It's -- probably an error to have two values for the same parameter. -- @@ -56,6 +74,8 @@ 	= StringValue Word8 Text 	| PathValue Word8 T.ObjectPath 	deriving (Show, Eq)++#line 76 "src/match-rules.anansi" -- | The set of allowed message types to filter on is separate from the set -- supported for sending over the wire. This allows the server to support -- additional types not yet implemented in the library, or vice-versa.@@ -66,8 +86,14 @@ 	| Signal 	| Error 	deriving (Show, Eq)++#line 92 "src/match-rules.anansi"++#line 126 "src/api-docs.anansi" -- | Format a 'MatchRule' as the bus expects to receive in a call to -- @AddMatch@.++#line 93 "src/match-rules.anansi" formatRule :: MatchRule -> Text formatRule rule = TL.intercalate "," filters where 	filters = structureFilters ++ parameterFilters@@ -81,12 +107,16 @@ 		, ("destination", fmap T.strBusName . matchDestination) 		] 	unpack (key, mkValue) = formatFilter' key `fmap` mkValue rule++#line 109 "src/match-rules.anansi" formatParameter :: ParameterValue -> Text formatParameter (StringValue index x) = formatFilter' key x where 	key = "arg" `TL.append` TL.pack (show index) formatParameter (PathValue index x) = formatFilter' key value where 	key = "arg" `TL.append` TL.pack (show index) `TL.append` "path" 	value = T.strObjectPath x++#line 121 "src/match-rules.anansi" formatFilter' :: Text -> Text -> Text formatFilter' key value = TL.concat [key, "='", value, "'"] @@ -95,7 +125,13 @@ formatType MethodReturn = "method_return" formatType Signal       = "signal" formatType Error        = "error"++#line 135 "src/match-rules.anansi"++#line 131 "src/api-docs.anansi" -- | Build a 'M.MethodCall' for adding a match rule to the bus.++#line 136 "src/match-rules.anansi" addMatch :: MatchRule -> M.MethodCall addMatch rule = M.MethodCall 	C.dbusPath@@ -104,7 +140,13 @@ 	(Just C.dbusName) 	Set.empty 	[T.toVariant $ formatRule rule]++#line 150 "src/match-rules.anansi"++#line 135 "src/api-docs.anansi" -- | An empty match rule, which matches everything.++#line 151 "src/match-rules.anansi" matchAll :: MatchRule matchAll = MatchRule 	{ matchType        = Nothing@@ -115,8 +157,14 @@ 	, matchDestination = Nothing 	, matchParameters  = [] 	}++#line 167 "src/match-rules.anansi"++#line 139 "src/api-docs.anansi" -- | Whether a 'M.ReceivedMessage' matches a given rule. This is useful -- for implementing signal handlers.++#line 168 "src/match-rules.anansi" matches :: MatchRule -> M.ReceivedMessage -> Bool matches rule msg = and . mapMaybe ($ rule) $ 	[ fmap      (typeMatches msg) . matchType@@ -127,32 +175,44 @@ 	, fmap      (destMatches msg) . matchDestination 	, Just . parametersMatch msg  . matchParameters 	]++#line 181 "src/match-rules.anansi" typeMatches :: M.ReceivedMessage -> MessageType -> Bool typeMatches (M.ReceivedMethodCall   _ _ _) MethodCall   = True typeMatches (M.ReceivedMethodReturn _ _ _) MethodReturn = True typeMatches (M.ReceivedSignal       _ _ _) Signal       = True typeMatches (M.ReceivedError        _ _ _) Error        = True typeMatches _ _ = False++#line 190 "src/match-rules.anansi" senderMatches :: M.ReceivedMessage -> T.BusName -> Bool senderMatches msg name = M.receivedSender msg == Just name++#line 195 "src/match-rules.anansi" ifaceMatches :: M.ReceivedMessage -> T.InterfaceName -> Bool ifaceMatches (M.ReceivedMethodCall _ _ msg) name = 	Just name == M.methodCallInterface msg ifaceMatches (M.ReceivedSignal _ _ msg) name = 	name == M.signalInterface msg ifaceMatches _ _ = False++#line 204 "src/match-rules.anansi" memberMatches :: M.ReceivedMessage -> T.MemberName -> Bool memberMatches (M.ReceivedMethodCall _ _ msg) name = 	name == M.methodCallMember msg memberMatches (M.ReceivedSignal _ _ msg) name = 	name == M.signalMember msg memberMatches _ _ = False++#line 213 "src/match-rules.anansi" pathMatches :: M.ReceivedMessage -> T.ObjectPath -> Bool pathMatches (M.ReceivedMethodCall _ _ msg) path = 	path == M.methodCallPath msg pathMatches (M.ReceivedSignal _ _ msg) path = 	path == M.signalPath msg pathMatches _ _ = False++#line 222 "src/match-rules.anansi" destMatches :: M.ReceivedMessage -> T.BusName -> Bool destMatches (M.ReceivedMethodCall _ _ msg) name = 	Just name == M.methodCallDestination msg@@ -163,6 +223,8 @@ destMatches (M.ReceivedSignal _ _ msg) name = 	Just name == M.signalDestination msg destMatches _ _ = False++#line 235 "src/match-rules.anansi" parametersMatch :: M.ReceivedMessage -> [ParameterValue] -> Bool parametersMatch _ [] = True parametersMatch msg values = all validParam values where
hs/DBus/Message.hs view
@@ -1,3 +1,7 @@++#line 23 "src/messages.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,24 +16,46 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 24 "src/messages.anansi" module DBus.Message (++#line 51 "src/messages.anansi" 	Message ( messageFlags 	        , messageBody 	        )++#line 69 "src/messages.anansi" 	, Flag (..)++#line 124 "src/messages.anansi" 	, Serial 	, serialValue 	, firstSerial 	, nextSerial++#line 169 "src/messages.anansi" 	, MethodCall (..)++#line 206 "src/messages.anansi" 	, MethodReturn (..)++#line 256 "src/messages.anansi" 	, Error (..) 	, errorMessage++#line 296 "src/messages.anansi" 	, Signal (..)++#line 327 "src/messages.anansi" 	, Unknown (..)++#line 377 "src/messages.anansi" 	, ReceivedMessage (..) 	, receivedSerial 	, receivedSender 	, receivedBody++#line 26 "src/messages.anansi" 	) where import DBus.Message.Internal
hs/DBus/Message/Internal.hs view
@@ -1,3 +1,7 @@++#line 31 "src/messages.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,24 +16,40 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 32 "src/messages.anansi"++#line 52 "src/introduction.anansi" {-# LANGUAGE OverloadedStrings #-}++#line 33 "src/messages.anansi" module DBus.Message.Internal where++#line 56 "src/introduction.anansi" import Data.Text.Lazy (Text) import qualified Data.Text.Lazy as TL++#line 35 "src/messages.anansi" 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)++#line 43 "src/messages.anansi" class Message a where 	messageTypeCode     :: a -> Word8 	messageHeaderFields :: a -> [HeaderField] 	messageFlags        :: a -> S.Set Flag 	messageBody         :: a -> [T.Variant]++#line 62 "src/messages.anansi" data Flag 	= NoReplyExpected 	| NoAutoStart 	deriving (Show, Eq, Ord)++#line 80 "src/messages.anansi" data HeaderField 	= Path        T.ObjectPath 	| Interface   T.InterfaceName@@ -40,8 +60,14 @@ 	| Sender      T.BusName 	| Signature   T.Signature 	deriving (Show, Eq)++#line 98 "src/messages.anansi"++#line 152 "src/api-docs.anansi" -- | A value used to uniquely identify a particular message within a session. -- 'Serial's are 32-bit unsigned integers, and eventually wrap.++#line 99 "src/messages.anansi" newtype Serial = Serial { serialValue :: Word32 } 	deriving (Eq, Ord) @@ -51,13 +77,19 @@ instance T.Variable Serial where 	toVariant (Serial x) = T.toVariant x 	fromVariant = fmap Serial . T.fromVariant++#line 113 "src/messages.anansi" firstSerial :: Serial firstSerial = Serial 1  nextSerial :: Serial -> Serial nextSerial (Serial x) = Serial (x + 1)++#line 138 "src/messages.anansi" maybe' :: (a -> b) -> Maybe a -> [b] maybe' f = maybe [] (\x' -> [f x'])++#line 145 "src/messages.anansi" data MethodCall = MethodCall 	{ methodCallPath        :: T.ObjectPath 	, methodCallMember      :: T.MemberName@@ -79,6 +111,8 @@ 		, maybe' Interface . methodCallInterface $ m 		, maybe' Destination . methodCallDestination $ m 		]++#line 187 "src/messages.anansi" data MethodReturn = MethodReturn 	{ methodReturnSerial      :: Serial 	, methodReturnDestination :: Maybe T.BusName@@ -95,6 +129,8 @@ 		  ] 		, maybe' Destination . methodReturnDestination $ m 		]++#line 221 "src/messages.anansi" data Error = Error 	{ errorName        :: T.ErrorName 	, errorSerial      :: Serial@@ -113,6 +149,8 @@ 		  ] 		, maybe' Destination . errorDestination $ m 		]++#line 246 "src/messages.anansi" errorMessage :: Error -> Text errorMessage msg = fromMaybe "(no error message)" $ do 	field <- maybeIndex (errorBody msg) 0@@ -120,6 +158,8 @@ 	if TL.null text 		then Nothing 		else return text++#line 273 "src/messages.anansi" data Signal = Signal 	{ signalPath        :: T.ObjectPath 	, signalMember      :: T.MemberName@@ -140,15 +180,23 @@ 		  ] 		, maybe' Destination . signalDestination $ m 		]++#line 318 "src/messages.anansi" data Unknown = Unknown 	{ unknownType    :: Word8 	, unknownFlags   :: S.Set Flag 	, unknownBody    :: [T.Variant] 	} 	deriving (Show, Eq)++#line 339 "src/messages.anansi"++#line 157 "src/api-docs.anansi" -- | Not an actual message type, but a wrapper around messages received from -- the bus. Each value contains the message's 'Serial' and possibly the -- origin's 'T.BusName'++#line 340 "src/messages.anansi" data ReceivedMessage 	= ReceivedMethodCall   Serial (Maybe T.BusName) MethodCall 	| ReceivedMethodReturn Serial (Maybe T.BusName) MethodReturn@@ -156,18 +204,24 @@ 	| ReceivedSignal       Serial (Maybe T.BusName) Signal 	| ReceivedUnknown      Serial (Maybe T.BusName) Unknown 	deriving (Show, Eq)++#line 350 "src/messages.anansi" receivedSerial :: ReceivedMessage -> Serial receivedSerial (ReceivedMethodCall   s _ _) = s receivedSerial (ReceivedMethodReturn s _ _) = s receivedSerial (ReceivedError        s _ _) = s receivedSerial (ReceivedSignal       s _ _) = s receivedSerial (ReceivedUnknown      s _ _) = s++#line 359 "src/messages.anansi" 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++#line 368 "src/messages.anansi" receivedBody :: ReceivedMessage -> [T.Variant] receivedBody (ReceivedMethodCall   _ _ x) = messageBody x receivedBody (ReceivedMethodReturn _ _ x) = messageBody x
hs/DBus/NameReservation.hs view
@@ -1,3 +1,7 @@++#line 22 "src/name-reservation.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,6 +16,8 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 23 "src/name-reservation.anansi" {-# LANGUAGE OverloadedStrings #-} module DBus.NameReservation 	( RequestNameFlag (..)@@ -29,17 +35,27 @@ import qualified DBus.Message as M import qualified DBus.Constants as C import DBus.Util (maybeIndex)++#line 43 "src/name-reservation.anansi" data RequestNameFlag 	= AllowReplacement 	| ReplaceExisting 	| DoNotQueue 	deriving (Show)++#line 51 "src/name-reservation.anansi" encodeFlags :: [RequestNameFlag] -> Word32 encodeFlags = foldr (.|.) 0 . map flagValue where 	flagValue AllowReplacement = 0x1 	flagValue ReplaceExisting  = 0x2 	flagValue DoNotQueue       = 0x4++#line 62 "src/name-reservation.anansi"++#line 144 "src/api-docs.anansi" -- | Build a 'M.MethodCall' for requesting a registered bus name.++#line 63 "src/name-reservation.anansi" requestName :: T.BusName -> [RequestNameFlag] -> M.MethodCall requestName name flags = M.MethodCall 	{ M.methodCallPath = C.dbusPath@@ -51,24 +67,36 @@ 		[ T.toVariant name 		, T.toVariant . encodeFlags $ flags] 	}++#line 77 "src/name-reservation.anansi" data RequestNameReply 	= PrimaryOwner 	| InQueue 	| Exists 	| AlreadyOwner 	deriving (Show)++#line 86 "src/name-reservation.anansi" mkRequestNameReply :: M.MethodReturn -> Maybe RequestNameReply mkRequestNameReply msg = 	maybeIndex (M.messageBody msg) 0 >>= 	T.fromVariant >>= 	decodeRequestReply++#line 94 "src/name-reservation.anansi" decodeRequestReply :: Word32 -> Maybe RequestNameReply decodeRequestReply 1 = Just PrimaryOwner decodeRequestReply 2 = Just InQueue decodeRequestReply 3 = Just Exists decodeRequestReply 4 = Just AlreadyOwner decodeRequestReply _ = Nothing++#line 103 "src/name-reservation.anansi"++#line 148 "src/api-docs.anansi" -- | Build a 'M.MethodCall' for releasing a registered bus name.++#line 104 "src/name-reservation.anansi" releaseName :: T.BusName -> M.MethodCall releaseName name = M.MethodCall 	{ M.methodCallPath = C.dbusPath@@ -78,16 +106,22 @@ 	, M.methodCallMember = "ReleaseName" 	, M.methodCallBody = [T.toVariant name] 	}++#line 116 "src/name-reservation.anansi" data ReleaseNameReply 	= Released 	| NonExistent 	| NotOwner 	deriving (Show)++#line 124 "src/name-reservation.anansi" mkReleaseNameReply :: M.MethodReturn -> Maybe ReleaseNameReply mkReleaseNameReply msg = 	maybeIndex (M.messageBody msg) 0 >>= 	T.fromVariant >>= 	decodeReleaseReply++#line 132 "src/name-reservation.anansi" decodeReleaseReply :: Word32 -> Maybe ReleaseNameReply decodeReleaseReply 1 = Just Released decodeReleaseReply 2 = Just NonExistent
− hs/DBus/Types.cpphs
@@ -1,504 +0,0 @@--- 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/>.-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeSynonymInstances #-}-module DBus.Types (-	  -- * Available types-	  Type (..)-	, typeCode-	  -- * Variants-	, Variant-	, Variable (..)-	, variantType-	  -- * Signatures-	, Signature-	, signatureTypes-	, strSignature-	, mkSignature-	, mkSignature_-	  -- * Object paths-	, ObjectPath-	, strObjectPath-	, mkObjectPath-	, mkObjectPath_-	  -- * Arrays-	, Array-	, arrayType-	, arrayItems-	, toArray-	, fromArray-	, arrayFromItems-	, arrayToBytes-	, arrayFromBytes-	  -- * Dictionaries-	, Dictionary-	, dictionaryItems-	, dictionaryKeyType-	, dictionaryValueType-	, toDictionary-	, fromDictionary-	, dictionaryFromItems-	, dictionaryToArray-	, arrayToDictionary-	  -- * Structures-	, Structure (..)-	-- * Names-	  -- ** Bus names-	, BusName-	, strBusName-	, mkBusName-	, mkBusName_-	  -- ** Interface names-	, InterfaceName-	, strInterfaceName-	, mkInterfaceName-	, mkInterfaceName_-	  -- ** Error names-	, ErrorName-	, strErrorName-	, mkErrorName-	, mkErrorName_-	  -- ** Member names-	, MemberName-	, strMemberName-	, mkMemberName-	, mkMemberName_-	) where-import Data.Text.Lazy (Text)-import qualified Data.Text.Lazy as TL-import Data.Word (Word8, Word16, Word32, Word64)-import Data.Int (Int16, Int32, Int64)-import qualified Data.Text as T-import Data.Ord (comparing)-import Text.Parsec ((<|>))-import qualified Text.Parsec as P-import DBus.Util (checkLength, parseMaybe)-import DBus.Util (mkUnsafe)-import qualified Data.String as String-import Data.ByteString.Lazy (ByteString)-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.ByteString as StrictByteString-import Data.List (intercalate)-import Control.Monad (unless)-import Control.Arrow ((***))-import qualified Data.Map as Map-import Control.Monad (forM)-data Type-	= DBusBoolean-	| DBusByte-	| DBusInt16-	| DBusInt32-	| DBusInt64-	| DBusWord16-	| DBusWord32-	| DBusWord64-	| DBusDouble-	| DBusString-	| DBusSignature-	| DBusObjectPath-	| DBusVariant-	| DBusArray Type-	| DBusDictionary Type Type-	| DBusStructure [Type]-	deriving (Show, Eq)--- | \"Atomic\" types are any which can't contain any other types. Only--- atomic types may be used as dictionary keys.-isAtomicType :: Type -> Bool-isAtomicType DBusBoolean    = True-isAtomicType DBusByte       = True-isAtomicType DBusInt16      = True-isAtomicType DBusInt32      = True-isAtomicType DBusInt64      = True-isAtomicType DBusWord16     = True-isAtomicType DBusWord32     = True-isAtomicType DBusWord64     = True-isAtomicType DBusDouble     = True-isAtomicType DBusString     = True-isAtomicType DBusSignature  = True-isAtomicType DBusObjectPath = True-isAtomicType _              = False--- | Every type has an associated type code; a textual representation of--- the type, useful for debugging.-typeCode :: Type -> Text-typeCode DBusBoolean    = "b"-typeCode DBusByte       = "y"-typeCode DBusInt16      = "n"-typeCode DBusInt32      = "i"-typeCode DBusInt64      = "x"-typeCode DBusWord16     = "q"-typeCode DBusWord32     = "u"-typeCode DBusWord64     = "t"-typeCode DBusDouble     = "d"-typeCode DBusString     = "s"-typeCode DBusSignature  = "g"-typeCode DBusObjectPath = "o"-typeCode DBusVariant    = "v"-typeCode (DBusArray t) = TL.cons 'a' $ typeCode t-typeCode (DBusDictionary k v) = TL.concat ["a{", typeCode k, typeCode v, "}"]-typeCode (DBusStructure ts) = TL.concat $-	["("] ++ map typeCode ts ++ [")"]--- | 'Variant's may contain any other built-in D-Bus value. Besides--- representing native @VARIANT@ values, they allow type-safe storage and--- deconstruction of heterogeneous collections.-data Variant-	= VarBoxBool Bool-	| VarBoxWord8 Word8-	| VarBoxInt16 Int16-	| VarBoxInt32 Int32-	| VarBoxInt64 Int64-	| VarBoxWord16 Word16-	| VarBoxWord32 Word32-	| VarBoxWord64 Word64-	| VarBoxDouble Double-	| VarBoxString Text-	| VarBoxSignature Signature-	| VarBoxObjectPath ObjectPath-	| VarBoxVariant Variant-	| VarBoxArray Array-	| VarBoxDictionary Dictionary-	| VarBoxStructure Structure-	deriving (Eq)--class Variable a where-	toVariant :: a -> Variant-	fromVariant :: Variant -> Maybe a-instance Show Variant where-	showsPrec d var = showParen (d > 10) full where-		full = s "Variant " . shows code . s " " . valueStr-		code = typeCode $ variantType var-		s = showString-		valueStr = showsPrecVar 11 var--showsPrecVar :: Int -> Variant -> ShowS-showsPrecVar d var = case var of-	(VarBoxBool x) -> showsPrec d x-	(VarBoxWord8 x) -> showsPrec d x-	(VarBoxInt16 x) -> showsPrec d x-	(VarBoxInt32 x) -> showsPrec d x-	(VarBoxInt64 x) -> showsPrec d x-	(VarBoxWord16 x) -> showsPrec d x-	(VarBoxWord32 x) -> showsPrec d x-	(VarBoxWord64 x) -> showsPrec d x-	(VarBoxDouble x) -> showsPrec d x-	(VarBoxString x) -> showsPrec d x-	(VarBoxSignature x) -> showsPrec d x-	(VarBoxObjectPath x) -> showsPrec d x-	(VarBoxVariant x) -> showsPrec d x-	(VarBoxArray x) -> showsPrec d x-	(VarBoxDictionary x) -> showsPrec d x-	(VarBoxStructure x) -> showsPrec d x--- | Every variant is strongly-typed; that is, the type of its contained--- value is known at all times. This function retrieves that type, so that--- the correct cast can be used to retrieve the value.-variantType :: Variant -> Type-variantType var = case var of-	(VarBoxBool _) -> DBusBoolean-	(VarBoxWord8 _) -> DBusByte-	(VarBoxInt16 _) -> DBusInt16-	(VarBoxInt32 _) -> DBusInt32-	(VarBoxInt64 _) -> DBusInt64-	(VarBoxWord16 _) -> DBusWord16-	(VarBoxWord32 _) -> DBusWord32-	(VarBoxWord64 _) -> DBusWord64-	(VarBoxDouble _) -> DBusDouble-	(VarBoxString _) -> DBusString-	(VarBoxSignature _) -> DBusSignature-	(VarBoxObjectPath _) -> DBusObjectPath-	(VarBoxVariant _) -> DBusVariant-	(VarBoxArray x) -> DBusArray (arrayType x)-	(VarBoxDictionary x) -> let-		keyT = dictionaryKeyType x-		valueT = dictionaryValueType x-		in DBusDictionary keyT valueT-	(VarBoxStructure x) -> let-		Structure items = x-		in DBusStructure (map variantType items)-#define INSTANCE_VARIABLE(TYPE) \-	instance Variable TYPE where \-		{ toVariant = VarBox/**/TYPE \-		; fromVariant (VarBox/**/TYPE x) = Just x \-		; fromVariant _ = Nothing \-		}-INSTANCE_VARIABLE(Variant)-INSTANCE_VARIABLE(Bool)-INSTANCE_VARIABLE(Word8)-INSTANCE_VARIABLE(Int16)-INSTANCE_VARIABLE(Int32)-INSTANCE_VARIABLE(Int64)-INSTANCE_VARIABLE(Word16)-INSTANCE_VARIABLE(Word32)-INSTANCE_VARIABLE(Word64)-INSTANCE_VARIABLE(Double)-instance Variable TL.Text where-	toVariant = VarBoxString-	fromVariant (VarBoxString x) = Just x-	fromVariant _ = Nothing-instance Variable T.Text where-	toVariant = toVariant . TL.fromChunks . (:[])-	fromVariant = fmap (T.concat . TL.toChunks) . fromVariant-instance Variable String where-	toVariant = toVariant . TL.pack-	fromVariant = fmap TL.unpack . fromVariant-INSTANCE_VARIABLE(Signature)-data Signature = Signature { signatureTypes :: [Type] }-	deriving (Eq)--instance Show Signature where-	showsPrec d x = showParen (d > 10) $-		showString "Signature " . shows (strSignature x)-strSignature :: Signature -> Text-strSignature (Signature ts) = TL.concat $ map typeCode ts-instance Ord Signature where-	compare = comparing strSignature-mkSignature :: Text -> Maybe Signature-mkSignature text = parsed where-	just t = Just $ Signature [t]-	fast = case TL.head text of-		'b' -> just DBusBoolean-		'y' -> just DBusByte-		'n' -> just DBusInt16-		'i' -> just DBusInt32-		'x' -> just DBusInt64-		'q' -> just DBusWord16-		'u' -> just DBusWord32-		't' -> just DBusWord64-		'd' -> just DBusDouble-		's' -> just DBusString-		'g' -> just DBusSignature-		'o' -> just DBusObjectPath-		'v' -> just DBusVariant-		_   -> Nothing-	slow = (parseMaybe sigParser =<<) . checkLength 255 . TL.unpack $ text-	sigParser = do-		types <- P.many parseType-		P.eof-		return $ Signature types-	parseType = parseAtom <|> parseContainer-	parseContainer =-		    parseArray-		<|> parseStruct-		<|> (P.char 'v' >> return DBusVariant)-	parseArray = do-		P.char 'a'-		parseDict <|> fmap DBusArray parseType-	parseDict = do-		P.char '{'-		keyType <- parseAtom-		valueType <- parseType-		P.char '}'-		return $ DBusDictionary keyType valueType-	parseStruct = do-		P.char '('-		types <- P.many parseType-		P.char ')'-		return $ DBusStructure types-	parseAtom =-		    (P.char 'b' >> return DBusBoolean)-		<|> (P.char 'y' >> return DBusByte)-		<|> (P.char 'n' >> return DBusInt16)-		<|> (P.char 'i' >> return DBusInt32)-		<|> (P.char 'x' >> return DBusInt64)-		<|> (P.char 'q' >> return DBusWord16)-		<|> (P.char 'u' >> return DBusWord32)-		<|> (P.char 't' >> return DBusWord64)-		<|> (P.char 'd' >> return DBusDouble)-		<|> (P.char 's' >> return DBusString)-		<|> (P.char 'g' >> return DBusSignature)-		<|> (P.char 'o' >> return DBusObjectPath)-	parsed = case TL.length text of-		0 -> Just $ Signature []-		1 -> fast-		_ -> slow-mkSignature_ :: Text -> Signature-mkSignature_ = mkUnsafe "signature" mkSignature--instance String.IsString Signature where-	fromString = mkSignature_ . TL.pack-INSTANCE_VARIABLE(ObjectPath)-newtype ObjectPath = ObjectPath-	{ strObjectPath :: Text-	}-	deriving (Eq, Ord)--instance Show ObjectPath where-	showsPrec d (ObjectPath x) = showParen (d > 10) $-		showString "ObjectPath " . shows x--instance String.IsString ObjectPath where-	fromString = mkObjectPath_ . TL.pack-mkObjectPath :: Text -> Maybe ObjectPath-mkObjectPath s = parseMaybe path' (TL.unpack s) where-	c = P.oneOf $ ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_"-	path = P.char '/' >>= P.optional . P.sepBy (P.many1 c) . P.char-	path' = path >> P.eof >> return (ObjectPath s)--mkObjectPath_ :: Text -> ObjectPath-mkObjectPath_ = mkUnsafe "object path" mkObjectPath-INSTANCE_VARIABLE(Array)-data Array-	= VariantArray Type [Variant]-	| ByteArray ByteString-	deriving (Eq)---- | This is the type contained within the array, not the type of the array--- itself.-arrayType :: Array -> Type-arrayType (VariantArray t _) = t-arrayType (ByteArray _) = DBusByte--arrayItems :: Array -> [Variant]-arrayItems (VariantArray _ xs) = xs-arrayItems (ByteArray xs) = map toVariant $ ByteString.unpack xs-instance Show Array where-	showsPrec d array = showParen (d > 10) $-		s "Array " . showSig . s " [" . s valueString . s "]" where-			s = showString-			showSig = shows . typeCode . arrayType $ array-			showVar var = showsPrecVar 0 var ""-			valueString = intercalate ", " $ map showVar $ arrayItems array-arrayFromItems :: Type -> [Variant] -> Maybe Array-arrayFromItems DBusByte vs = fmap (ByteArray . ByteString.pack) (mapM fromVariant vs)--arrayFromItems t vs = do-	mkSignature (typeCode t)-	if all (\x -> variantType x == t) vs-		then Just $ VariantArray t vs-		else Nothing-toArray :: Variable a => Type -> [a] -> Maybe Array-toArray t = arrayFromItems t . map toVariant--fromArray :: Variable a => Array -> Maybe [a]-fromArray = mapM fromVariant . arrayItems-arrayToBytes :: Array -> Maybe ByteString-arrayToBytes (ByteArray x) = Just x-arrayToBytes _             = Nothing--arrayFromBytes :: ByteString -> Array-arrayFromBytes = ByteArray-instance Variable ByteString where-	toVariant = toVariant . arrayFromBytes-	fromVariant x = fromVariant x >>= arrayToBytes-instance Variable StrictByteString.ByteString where-	toVariant x = toVariant . arrayFromBytes $ ByteString.fromChunks [x]-	fromVariant x = do-		chunks <- ByteString.toChunks `fmap` fromVariant x-		return $ StrictByteString.concat chunks-INSTANCE_VARIABLE(Dictionary)-data Dictionary = Dictionary-	{ dictionaryKeyType   :: Type-	, dictionaryValueType :: Type-	, dictionaryItems     :: [(Variant, Variant)]-	}-	deriving (Eq)-instance Show Dictionary where-	showsPrec d (Dictionary kt vt pairs) = showParen (d > 10) $-		s "Dictionary " . showSig . s " {" . s valueString . s "}" where-			s = showString-			showSig = shows $ TL.append (typeCode kt) (typeCode vt)-			valueString = intercalate ", " $ map showPair pairs-			showPair (k, v) = (showsPrecVar 0 k . showString " -> " . showsPrecVar 0 v) ""-dictionaryFromItems :: Type -> Type -> [(Variant, Variant)] -> Maybe Dictionary-dictionaryFromItems kt vt pairs = do-	unless (isAtomicType kt) Nothing-	mkSignature (typeCode kt)-	mkSignature (typeCode vt)-	-	let sameType (k, v) = variantType k == kt &&-	                      variantType v == vt-	if all sameType pairs-		then Just $ Dictionary kt vt pairs-		else Nothing-toDictionary :: (Variable a, Variable b) => Type -> Type -> Map.Map a b-             -> Maybe Dictionary-toDictionary kt vt = dictionaryFromItems kt vt . pairs where-	pairs = map (toVariant *** toVariant) . Map.toList-fromDictionary :: (Variable a, Ord a, Variable b) => Dictionary-               -> Maybe (Map.Map a b)-fromDictionary (Dictionary _ _ vs) = do-	pairs <- forM vs $ \(k, v) -> do-		k' <- fromVariant k-		v' <- fromVariant v-		return (k', v')-	return $ Map.fromList pairs-dictionaryToArray :: Dictionary -> Array-dictionaryToArray (Dictionary kt vt items) = array where-	Just array = toArray itemType structs-	itemType = DBusStructure [kt, vt]-	structs = [Structure [k, v] | (k, v) <- items]-arrayToDictionary :: Array -> Maybe Dictionary-arrayToDictionary array = do-	let toPair x = do-		struct <- fromVariant x-		case struct of-			Structure [k, v] -> Just (k, v)-			_                -> Nothing-	(kt, vt) <- case arrayType array of-		DBusStructure [kt, vt] -> Just (kt, vt)-		_                      -> Nothing-	pairs <- mapM toPair $ arrayItems array-	dictionaryFromItems kt vt pairs-INSTANCE_VARIABLE(Structure)-data Structure = Structure [Variant]-	deriving (Show, Eq)-#define NAME_TYPE(TYPE, NAME) \-	newtype TYPE = TYPE {str/**/TYPE :: Text} \-		deriving (Eq, Ord); \-		\-	instance Show TYPE where \-		{ showsPrec d (TYPE x) = showParen (d > 10) $ \-			showString "TYPE " . shows x \-		}; \-		\-	instance String.IsString TYPE where \-		{ fromString = mk/**/TYPE/**/_ . TL.pack }; \-		\-	instance Variable TYPE where \-		{ toVariant = toVariant . str/**/TYPE \-		; fromVariant = (mk/**/TYPE =<<) . fromVariant }; \-		                                                \-	mk/**/TYPE/**/_ :: Text -> TYPE; \-	mk/**/TYPE/**/_ = mkUnsafe NAME mk/**/TYPE-NAME_TYPE(BusName, "bus name")--mkBusName :: Text -> Maybe BusName-mkBusName s = checkLength 255 (TL.unpack s) >>= parseMaybe parser where-	c = ['a'..'z'] ++ ['A'..'Z'] ++ "_-"-	c' = c ++ ['0'..'9']-	parser = (unique <|> wellKnown) >> P.eof >> return (BusName s)-	unique = P.char ':' >> elems c'-	wellKnown = elems c-	elems start = elem' start >> P.many1 (P.char '.' >> elem' start)-	elem' start = P.oneOf start >> P.many (P.oneOf c')-NAME_TYPE(InterfaceName, "interface name")--mkInterfaceName :: Text -> Maybe InterfaceName-mkInterfaceName s = checkLength 255 (TL.unpack s) >>= parseMaybe parser where-	c = ['a'..'z'] ++ ['A'..'Z'] ++ "_"-	c' = c ++ ['0'..'9']-	element = P.oneOf c >> P.many (P.oneOf c')-	name = element >> P.many1 (P.char '.' >> element)-	parser = name >> P.eof >> return (InterfaceName s)-NAME_TYPE(ErrorName, "error name")--mkErrorName :: Text -> Maybe ErrorName-mkErrorName = fmap (ErrorName . strInterfaceName) . mkInterfaceName-NAME_TYPE(MemberName, "member name")--mkMemberName :: Text -> Maybe MemberName-mkMemberName s = checkLength 255 (TL.unpack s) >>= parseMaybe parser where-	c = ['a'..'z'] ++ ['A'..'Z'] ++ "_"-	c' = c ++ ['0'..'9']-	name = P.oneOf c >> P.many (P.oneOf c')-	parser = name >> P.eof >> return (MemberName s)
+ hs/DBus/Types.hs view
@@ -0,0 +1,121 @@++#line 23 "src/types.anansi"++#line 30 "src/introduction.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/>.++#line 24 "src/types.anansi"+module DBus.Types (++#line 100 "src/types.anansi"+	  -- * Available types+	  Type (..)+	, typeCode++#line 176 "src/types.anansi"+	  -- * Variants+	, Variant+	, Variable (..)++#line 248 "src/types.anansi"+	, variantType++#line 442 "src/types.anansi"+	  -- * Signatures+	, Signature+	, signatureTypes+	, strSignature++#line 714 "src/types.anansi"+	, mkSignature+	, mkSignature_++#line 789 "src/types.anansi"+	  -- * Object paths+	, ObjectPath+	, strObjectPath+	, mkObjectPath+	, mkObjectPath_++#line 839 "src/types.anansi"+	  -- * Arrays+	, Array+	, arrayType+	, arrayItems++#line 885 "src/types.anansi"+	, toArray+	, fromArray+	, arrayFromItems++#line 903 "src/types.anansi"+	, arrayToBytes+	, arrayFromBytes++#line 967 "src/types.anansi"+	  -- * Dictionaries+	, Dictionary+	, dictionaryItems+	, dictionaryKeyType+	, dictionaryValueType++#line 1044 "src/types.anansi"+	, toDictionary+	, fromDictionary+	, dictionaryFromItems++#line 1116 "src/types.anansi"+	, dictionaryToArray+	, arrayToDictionary++#line 1133 "src/types.anansi"+	  -- * Structures+	, Structure (..)++#line 1174 "src/types.anansi"+	-- * Names++#line 1204 "src/types.anansi"+	  -- ** Bus names+	, BusName+	, strBusName+	, mkBusName+	, mkBusName_++#line 1257 "src/types.anansi"+	  -- ** Interface names+	, InterfaceName+	, strInterfaceName+	, mkInterfaceName+	, mkInterfaceName_++#line 1298 "src/types.anansi"+	  -- ** Error names+	, ErrorName+	, strErrorName+	, mkErrorName+	, mkErrorName_++#line 1334 "src/types.anansi"+	  -- ** Member names+	, MemberName+	, strMemberName+	, mkMemberName+	, mkMemberName_++#line 26 "src/types.anansi"+	) where+import DBus.Types.Internal
+ hs/DBus/Types/Internal.cpphs view
@@ -0,0 +1,706 @@++#line 31 "src/types.anansi"++#line 30 "src/introduction.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/>.++#line 32 "src/types.anansi"++#line 52 "src/introduction.anansi"+{-# LANGUAGE OverloadedStrings #-}++#line 33 "src/types.anansi"++#line 372 "src/types.anansi"+{-# LANGUAGE TypeSynonymInstances #-}++#line 34 "src/types.anansi"+module DBus.Types.Internal where++#line 56 "src/introduction.anansi"+import Data.Text.Lazy (Text)+import qualified Data.Text.Lazy as TL++#line 36 "src/types.anansi"++#line 325 "src/types.anansi"+import Data.Word (Word8, Word16, Word32, Word64)+import Data.Int (Int16, Int32, Int64)++#line 359 "src/types.anansi"+import qualified Data.Text as T++#line 433 "src/types.anansi"+import Data.Ord (comparing)++#line 454 "src/types.anansi"+import Data.Text.Encoding (decodeUtf8)++#line 513 "src/types.anansi"+import qualified Data.ByteString.Unsafe as B+import qualified Foreign as F+import System.IO.Unsafe (unsafePerformIO)++#line 684 "src/types.anansi"+import Data.Text.Lazy.Encoding (encodeUtf8)++#line 697 "src/types.anansi"+import DBus.Util (mkUnsafe)+import qualified Data.String as String++#line 772 "src/types.anansi"+import Text.ParserCombinators.Parsec ((<|>))+import qualified Text.ParserCombinators.Parsec as P+import DBus.Util (checkLength, parseMaybe)++#line 978 "src/types.anansi"+import Data.List (intercalate)++#line 995 "src/types.anansi"+import Control.Monad (unless)++#line 1017 "src/types.anansi"+import Control.Arrow ((***))+import qualified Data.Map as Map++#line 1029 "src/types.anansi"+import Control.Monad (forM)++#line 37 "src/types.anansi"+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as B8+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Char8 as BL8++#line 52 "src/types.anansi"+data Type+	= DBusBoolean+	| DBusByte+	| DBusInt16+	| DBusInt32+	| DBusInt64+	| DBusWord16+	| DBusWord32+	| DBusWord64+	| DBusDouble+	| DBusString+	| DBusSignature+	| DBusObjectPath+	| DBusVariant+	| DBusArray Type+	| DBusDictionary Type Type+	| DBusStructure [Type]+	deriving (Show, Eq)++#line 73 "src/types.anansi"++#line 27 "src/api-docs.anansi"+-- | \"Atomic\" types are any which can't contain any other types. Only+-- atomic types may be used as dictionary keys.++#line 74 "src/types.anansi"+isAtomicType :: Type -> Bool+isAtomicType DBusBoolean    = True+isAtomicType DBusByte       = True+isAtomicType DBusInt16      = True+isAtomicType DBusInt32      = True+isAtomicType DBusInt64      = True+isAtomicType DBusWord16     = True+isAtomicType DBusWord32     = True+isAtomicType DBusWord64     = True+isAtomicType DBusDouble     = True+isAtomicType DBusString     = True+isAtomicType DBusSignature  = True+isAtomicType DBusObjectPath = True+isAtomicType _              = False++#line 94 "src/types.anansi"++#line 32 "src/api-docs.anansi"+-- | Every type has an associated type code; a textual representation of+-- the type, useful for debugging.++#line 95 "src/types.anansi"+typeCode :: Type -> Text++#line 458 "src/types.anansi"+typeCode t = TL.fromChunks [decodeUtf8 $ typeCodeB t]++typeCodeB :: Type -> B.ByteString+typeCodeB DBusBoolean    = "b"+typeCodeB DBusByte       = "y"+typeCodeB DBusInt16      = "n"+typeCodeB DBusInt32      = "i"+typeCodeB DBusInt64      = "x"+typeCodeB DBusWord16     = "q"+typeCodeB DBusWord32     = "u"+typeCodeB DBusWord64     = "t"+typeCodeB DBusDouble     = "d"+typeCodeB DBusString     = "s"+typeCodeB DBusSignature  = "g"+typeCodeB DBusObjectPath = "o"+typeCodeB DBusVariant    = "v"++#line 480 "src/types.anansi"+typeCodeB (DBusArray t) = B8.cons 'a' $ typeCodeB t++#line 487 "src/types.anansi"+typeCodeB (DBusDictionary k v) = B.concat ["a{", typeCodeB k, typeCodeB v, "}"]++#line 495 "src/types.anansi"+typeCodeB (DBusStructure ts) = B.concat $+	["("] ++ map typeCodeB ts ++ [")"]++#line 150 "src/types.anansi"++#line 37 "src/api-docs.anansi"+-- | 'Variant's may contain any other built-in D-Bus value. Besides+-- representing native @VARIANT@ values, they allow type-safe storage and+-- deconstruction of heterogeneous collections.++#line 151 "src/types.anansi"+data Variant+	= VarBoxBool Bool+	| VarBoxWord8 Word8+	| VarBoxInt16 Int16+	| VarBoxInt32 Int32+	| VarBoxInt64 Int64+	| VarBoxWord16 Word16+	| VarBoxWord32 Word32+	| VarBoxWord64 Word64+	| VarBoxDouble Double+	| VarBoxString Text+	| VarBoxSignature Signature+	| VarBoxObjectPath ObjectPath+	| VarBoxVariant Variant+	| VarBoxArray Array+	| VarBoxDictionary Dictionary+	| VarBoxStructure Structure+	deriving (Eq)++class Variable a where+	toVariant :: a -> Variant+	fromVariant :: Variant -> Maybe a++#line 186 "src/types.anansi"+instance Show Variant where+	showsPrec d var = showParen (d > 10) full where+		full = s "Variant " . shows code . s " " . valueStr+		code = typeCode $ variantType var+		s = showString+		valueStr = showsPrecVar 11 var++showsPrecVar :: Int -> Variant -> ShowS+showsPrecVar d var = case var of+	(VarBoxBool x) -> showsPrec d x+	(VarBoxWord8 x) -> showsPrec d x+	(VarBoxInt16 x) -> showsPrec d x+	(VarBoxInt32 x) -> showsPrec d x+	(VarBoxInt64 x) -> showsPrec d x+	(VarBoxWord16 x) -> showsPrec d x+	(VarBoxWord32 x) -> showsPrec d x+	(VarBoxWord64 x) -> showsPrec d x+	(VarBoxDouble x) -> showsPrec d x+	(VarBoxString x) -> showsPrec d x+	(VarBoxSignature x) -> showsPrec d x+	(VarBoxObjectPath x) -> showsPrec d x+	(VarBoxVariant x) -> showsPrec d x+	(VarBoxArray x) -> showsPrec d x+	(VarBoxDictionary x) -> showsPrec d x+	(VarBoxStructure x) -> showsPrec d x++#line 218 "src/types.anansi"++#line 43 "src/api-docs.anansi"+-- | Every variant is strongly-typed; that is, the type of its contained+-- value is known at all times. This function retrieves that type, so that+-- the correct cast can be used to retrieve the value.++#line 219 "src/types.anansi"+variantType :: Variant -> Type+variantType var = case var of+	(VarBoxBool _) -> DBusBoolean+	(VarBoxWord8 _) -> DBusByte+	(VarBoxInt16 _) -> DBusInt16+	(VarBoxInt32 _) -> DBusInt32+	(VarBoxInt64 _) -> DBusInt64+	(VarBoxWord16 _) -> DBusWord16+	(VarBoxWord32 _) -> DBusWord32+	(VarBoxWord64 _) -> DBusWord64+	(VarBoxDouble _) -> DBusDouble+	(VarBoxString _) -> DBusString+	(VarBoxSignature _) -> DBusSignature+	(VarBoxObjectPath _) -> DBusObjectPath+	(VarBoxVariant _) -> DBusVariant+	(VarBoxArray x) -> DBusArray (arrayType x)+	(VarBoxDictionary x) -> let+		keyT = dictionaryKeyType x+		valueT = dictionaryValueType x+		in DBusDictionary keyT valueT+	(VarBoxStructure x) -> let+		Structure items = x+		in DBusStructure (map variantType items)++variantSignature :: Variant -> Maybe Signature+variantSignature = mkBytesSignature . typeCodeB . variantType++#line 255 "src/types.anansi"+#define INSTANCE_VARIABLE(TYPE) \+	instance Variable TYPE where \+		{ toVariant = VarBox/**/TYPE \+		; fromVariant (VarBox/**/TYPE x) = Just x \+		; fromVariant _ = Nothing \+		}++#line 266 "src/types.anansi"+INSTANCE_VARIABLE(Variant)++#line 330 "src/types.anansi"+INSTANCE_VARIABLE(Bool)+INSTANCE_VARIABLE(Word8)+INSTANCE_VARIABLE(Int16)+INSTANCE_VARIABLE(Int32)+INSTANCE_VARIABLE(Int64)+INSTANCE_VARIABLE(Word16)+INSTANCE_VARIABLE(Word32)+INSTANCE_VARIABLE(Word64)+INSTANCE_VARIABLE(Double)++#line 348 "src/types.anansi"+instance Variable TL.Text where+	toVariant = VarBoxString+	fromVariant (VarBoxString x) = Just x+	fromVariant _ = Nothing++#line 363 "src/types.anansi"+instance Variable T.Text where+	toVariant = toVariant . TL.fromChunks . (:[])+	fromVariant = fmap (T.concat . TL.toChunks) . fromVariant++#line 376 "src/types.anansi"+instance Variable String where+	toVariant = toVariant . TL.pack+	fromVariant = fmap TL.unpack . fromVariant++#line 409 "src/types.anansi"+INSTANCE_VARIABLE(Signature)+data Signature = Signature { signatureTypes :: [Type] }+	deriving (Eq)++instance Show Signature where+	showsPrec d x = showParen (d > 10) $+		showString "Signature " . shows (strSignature x)++#line 422 "src/types.anansi"+bytesSignature :: Signature -> B.ByteString+bytesSignature (Signature ts) = B.concat $ map typeCodeB ts++strSignature :: Signature -> Text+strSignature (Signature ts) = TL.concat $ map typeCode ts++#line 437 "src/types.anansi"+instance Ord Signature where+	compare = comparing strSignature++#line 529 "src/types.anansi"+mkBytesSignature :: B.ByteString -> Maybe Signature+mkBytesSignature = unsafePerformIO . flip B.unsafeUseAsCStringLen io where++#line 542 "src/types.anansi"+	parseAtom c yes no = case c of+		0x62 -> yes DBusBoolean+		0x79 -> yes DBusByte+		0x6E -> yes DBusInt16+		0x69 -> yes DBusInt32+		0x78 -> yes DBusInt64+		0x71 -> yes DBusWord16+		0x75 -> yes DBusWord32+		0x74 -> yes DBusWord64+		0x64 -> yes DBusDouble+		0x73 -> yes DBusString+		0x67 -> yes DBusSignature+		0x6F -> yes DBusObjectPath+		_ -> no+	+	fast c = parseAtom c (\t -> Just (Signature [t])) $ case c of+		0x76 -> Just (Signature [DBusVariant])+		_ -> Nothing++#line 532 "src/types.anansi"++#line 563 "src/types.anansi"+	slow :: F.Ptr Word8 -> Int -> IO (Maybe Signature)+	slow buf len = loop [] 0 where+		loop acc ii | ii >= len = return . Just . Signature $ reverse acc+		loop acc ii = do+			c <- F.peekElemOff buf ii+			let next t = loop (t : acc) (ii + 1)+			parseAtom c next $ case c of+				0x76 -> next DBusVariant+				+				-- '('+				0x28 -> do+					mt <- structure buf len (ii + 1)+					case mt of+						Just (ii', t) -> loop (t : acc) ii'+						Nothing -> return Nothing+				+				-- 'a'+				0x61 -> do+					mt <- array buf len (ii + 1)+					case mt of+						Just (ii', t) -> loop (t : acc) ii'+						Nothing -> return Nothing+				+				_ -> return Nothing++#line 590 "src/types.anansi"+	structure :: F.Ptr Word8 -> Int -> Int -> IO (Maybe (Int, Type))+	structure buf len = loop [] where+		loop _ ii | ii >= len = return Nothing+		loop acc ii = do+			c <- F.peekElemOff buf ii+			let next t = loop (t : acc) (ii + 1)+			parseAtom c next $ case c of+				0x76 -> next DBusVariant+				+				-- '('+				0x28 -> do+					mt <- structure buf len (ii + 1)+					case mt of+						Just (ii', t) -> loop (t : acc) ii'+						Nothing -> return Nothing+				+				-- ')'+				0x29 -> return $ Just $ (ii + 1, DBusStructure (reverse acc))+				+				-- 'a'+				0x61 -> do+					mt <- array buf len (ii + 1)+					case mt of+						Just (ii', t) -> loop (t : acc) ii'+						Nothing -> return Nothing+				+				_ -> return Nothing++#line 620 "src/types.anansi"+	array :: F.Ptr Word8 -> Int -> Int -> IO (Maybe (Int, Type))+	array _   len ii | ii >= len = return Nothing+	array buf len ii = do+		c <- F.peekElemOff buf ii+		let next t = return $ Just (ii + 1, DBusArray t)+		parseAtom c next $ case c of+			0x76 -> next DBusVariant+			+			-- '('+			0x28 -> do+				mt <- structure buf len (ii + 1)+				case mt of+					Just (ii', t) -> return $ Just (ii', DBusArray t)+					Nothing -> return Nothing+			+			-- '{'+			0x7B -> dict buf len (ii + 1)+			+			-- 'a'+			0x61 -> do+				mt <- array buf len (ii + 1)+				case mt of+					Just (ii', t) -> return $ Just (ii', DBusArray t)+					Nothing -> return Nothing+			+			_ -> return Nothing++#line 649 "src/types.anansi"+	dict :: F.Ptr Word8 -> Int -> Int -> IO (Maybe (Int, Type))+	dict _   len ii | ii + 1 >= len = return Nothing+	dict buf len ii = do+		c1 <- F.peekElemOff buf ii+		c2 <- F.peekElemOff buf (ii + 1)+		+		let mt1 = parseAtom c1 Just Nothing+		+		let next t = return $ Just (ii + 2, t)+		mt2 <- parseAtom c2 next $ case c2 of+			0x76 -> next DBusVariant+			+			-- '('+			0x28 -> structure buf len (ii + 2)+			+			-- 'a'+			0x61 -> array buf len (ii + 2)+			+			_ -> return Nothing+		+		case mt2 of+			Nothing -> return Nothing+			Just (ii', t2) -> if ii' >= len+				then return Nothing+				else do+					c3 <- F.peekElemOff buf ii'+					return $ do+						if c3 == 0x7D then Just () else Nothing+						t1 <- mt1+						Just (ii' + 1, DBusDictionary t1 t2)++#line 533 "src/types.anansi"+	+	io (cstr, len) = case len of+		0 -> return $ Just $ Signature []+		1 -> fmap fast $ F.peek cstr+		_ | len <= 255 -> slow (F.castPtr cstr) len+		_ -> return Nothing++#line 688 "src/types.anansi"+mkSignature :: Text -> Maybe Signature+mkSignature = mkBytesSignature . B.concat . BL.toChunks . encodeUtf8++#line 702 "src/types.anansi"+mkSignature_ :: Text -> Signature+mkSignature_ = mkUnsafe "signature" mkSignature++instance String.IsString Signature where+	fromString = mkUnsafe "signature" mkBytesSignature . BL8.pack++#line 724 "src/types.anansi"+maybeValidType :: Type -> Maybe ()+maybeValidType t = if B.length (typeCodeB t) > 255+	then Nothing+	else Just ()++#line 745 "src/types.anansi"+INSTANCE_VARIABLE(ObjectPath)+newtype ObjectPath = ObjectPath+	{ strObjectPath :: Text+	}+	deriving (Eq, Ord)++instance Show ObjectPath where+	showsPrec d (ObjectPath x) = showParen (d > 10) $+		showString "ObjectPath " . shows x++instance String.IsString ObjectPath where+	fromString = mkObjectPath_ . TL.pack++#line 778 "src/types.anansi"+mkObjectPath :: Text -> Maybe ObjectPath+mkObjectPath s = parseMaybe path' (TL.unpack s) where+	c = P.oneOf $ ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_"+	path = P.char '/' >>= P.optional . P.sepBy (P.many1 c) . P.char+	path' = path >> P.eof >> return (ObjectPath s)++mkObjectPath_ :: Text -> ObjectPath+mkObjectPath_ = mkUnsafe "object path" mkObjectPath++#line 822 "src/types.anansi"+INSTANCE_VARIABLE(Array)+data Array+	= VariantArray Type [Variant]+	| ByteArray BL.ByteString+	deriving (Eq)+++#line 49 "src/api-docs.anansi"+-- | This is the type contained within the array, not the type of the array+-- itself.++#line 829 "src/types.anansi"+arrayType :: Array -> Type+arrayType (VariantArray t _) = t+arrayType (ByteArray _) = DBusByte++arrayItems :: Array -> [Variant]+arrayItems (VariantArray _ xs) = xs+arrayItems (ByteArray xs) = map toVariant $ BL.unpack xs++#line 849 "src/types.anansi"+instance Show Array where+	showsPrec d array = showParen (d > 10) $+		s "Array " . showSig . s " [" . s valueString . s "]" where+			s = showString+			showSig = shows . typeCode . arrayType $ array+			showVar var = showsPrecVar 0 var ""+			valueString = intercalate ", " $ map showVar $ arrayItems array++#line 863 "src/types.anansi"+arrayFromItems :: Type -> [Variant] -> Maybe Array+arrayFromItems DBusByte vs = fmap (ByteArray . BL.pack) (mapM fromVariant vs)++arrayFromItems t vs = do+	maybeValidType t+	if all (\x -> variantType x == t) vs+		then Just $ VariantArray t vs+		else Nothing++#line 877 "src/types.anansi"+toArray :: Variable a => Type -> [a] -> Maybe Array+toArray t = arrayFromItems t . map toVariant++fromArray :: Variable a => Array -> Maybe [a]+fromArray = mapM fromVariant . arrayItems++#line 894 "src/types.anansi"+arrayToBytes :: Array -> Maybe BL.ByteString+arrayToBytes (ByteArray x) = Just x+arrayToBytes _             = Nothing++arrayFromBytes :: BL.ByteString -> Array+arrayFromBytes = ByteArray++#line 911 "src/types.anansi"+instance Variable BL.ByteString where+	toVariant = toVariant . arrayFromBytes+	fromVariant x = fromVariant x >>= arrayToBytes++#line 917 "src/types.anansi"+instance Variable B.ByteString where+	toVariant x = toVariant . arrayFromBytes $ BL.fromChunks [x]+	fromVariant = fmap (B.concat . BL.toChunks) . fromVariant++#line 956 "src/types.anansi"+INSTANCE_VARIABLE(Dictionary)++data Dictionary = Dictionary+	{ dictionaryKeyType   :: Type+	, dictionaryValueType :: Type+	, dictionaryItems     :: [(Variant, Variant)]+	}+	deriving (Eq)++#line 982 "src/types.anansi"+instance Show Dictionary where+	showsPrec d (Dictionary kt vt pairs) = showParen (d > 10) $+		s "Dictionary " . showSig . s " {" . s valueString . s "}" where+			s = showString+			showSig = shows $ TL.append (typeCode kt) (typeCode vt)+			valueString = intercalate ", " $ map showPair pairs+			showPair (k, v) = (showsPrecVar 0 k . showString " -> " . showsPrecVar 0 v) ""++#line 999 "src/types.anansi"+dictionaryFromItems :: Type -> Type -> [(Variant, Variant)] -> Maybe Dictionary+dictionaryFromItems kt vt pairs = do+	unless (isAtomicType kt) Nothing+	maybeValidType kt+	maybeValidType vt+	+	let sameType (k, v) = variantType k == kt &&+	                      variantType v == vt+	if all sameType pairs+		then Just $ Dictionary kt vt pairs+		else Nothing++#line 1022 "src/types.anansi"+toDictionary :: (Variable a, Variable b) => Type -> Type -> Map.Map a b+             -> Maybe Dictionary+toDictionary kt vt = dictionaryFromItems kt vt . pairs where+	pairs = map (toVariant *** toVariant) . Map.toList++#line 1033 "src/types.anansi"+fromDictionary :: (Variable a, Ord a, Variable b) => Dictionary+               -> Maybe (Map.Map a b)+fromDictionary (Dictionary _ _ vs) = do+	pairs <- forM vs $ \(k, v) -> do+		k' <- fromVariant k+		v' <- fromVariant v+		return (k', v')+	return $ Map.fromList pairs++#line 1093 "src/types.anansi"+dictionaryToArray :: Dictionary -> Array+dictionaryToArray (Dictionary kt vt items) = array where+	Just array = toArray itemType structs+	itemType = DBusStructure [kt, vt]+	structs = [Structure [k, v] | (k, v) <- items]++#line 1101 "src/types.anansi"+arrayToDictionary :: Array -> Maybe Dictionary+arrayToDictionary array = do+	let toPair x = do+		struct <- fromVariant x+		case struct of+			Structure [k, v] -> Just (k, v)+			_                -> Nothing+	(kt, vt) <- case arrayType array of+		DBusStructure [kt, vt] -> Just (kt, vt)+		_                      -> Nothing+	pairs <- mapM toPair $ arrayItems array+	dictionaryFromItems kt vt pairs++#line 1126 "src/types.anansi"+INSTANCE_VARIABLE(Structure)++data Structure = Structure [Variant]+	deriving (Show, Eq)++#line 1153 "src/types.anansi"+#define NAME_TYPE(TYPE, NAME) \+	newtype TYPE = TYPE {str/**/TYPE :: Text} \+		deriving (Eq, Ord); \+		\+	instance Show TYPE where \+		{ showsPrec d (TYPE x) = showParen (d > 10) $ \+			showString "TYPE " . shows x \+		}; \+		\+	instance String.IsString TYPE where \+		{ fromString = mk/**/TYPE/**/_ . TL.pack }; \+		\+	instance Variable TYPE where \+		{ toVariant = toVariant . str/**/TYPE \+		; fromVariant = (mk/**/TYPE =<<) . fromVariant }; \+		                                                \+	mk/**/TYPE/**/_ :: Text -> TYPE; \+	mk/**/TYPE/**/_ = mkUnsafe NAME mk/**/TYPE++#line 1190 "src/types.anansi"+NAME_TYPE(BusName, "bus name")++mkBusName :: Text -> Maybe BusName+mkBusName s = checkLength 255 (TL.unpack s) >>= parseMaybe parser where+	c = ['a'..'z'] ++ ['A'..'Z'] ++ "_-"+	c' = c ++ ['0'..'9']+	parser = (unique <|> wellKnown) >> P.eof >> return (BusName s)+	unique = P.char ':' >> elems c'+	wellKnown = elems c+	elems start = elem' start >> P.many1 (P.char '.' >> elem' start)+	elem' start = P.oneOf start >> P.many (P.oneOf c')++#line 1245 "src/types.anansi"+NAME_TYPE(InterfaceName, "interface name")++mkInterfaceName :: Text -> Maybe InterfaceName+mkInterfaceName s = checkLength 255 (TL.unpack s) >>= parseMaybe parser where+	c = ['a'..'z'] ++ ['A'..'Z'] ++ "_"+	c' = c ++ ['0'..'9']+	element = P.oneOf c >> P.many (P.oneOf c')+	name = element >> P.many1 (P.char '.' >> element)+	parser = name >> P.eof >> return (InterfaceName s)++#line 1291 "src/types.anansi"+NAME_TYPE(ErrorName, "error name")++mkErrorName :: Text -> Maybe ErrorName+mkErrorName = fmap (ErrorName . strInterfaceName) . mkInterfaceName++#line 1323 "src/types.anansi"+NAME_TYPE(MemberName, "member name")++mkMemberName :: Text -> Maybe MemberName+mkMemberName s = checkLength 255 (TL.unpack s) >>= parseMaybe parser where+	c = ['a'..'z'] ++ ['A'..'Z'] ++ "_"+	c' = c ++ ['0'..'9']+	name = P.oneOf c >> P.many (P.oneOf c')+	parser = name >> P.eof >> return (MemberName s)
hs/DBus/UUID.hs view
@@ -1,3 +1,7 @@++#line 22 "src/util.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,13 +16,19 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 23 "src/util.anansi" module DBus.UUID 	( UUID 	, toHex 	, fromHex 	) where++#line 56 "src/introduction.anansi" import Data.Text.Lazy (Text) import qualified Data.Text.Lazy as TL++#line 29 "src/util.anansi"  newtype UUID = UUID Text -- TODO: (Word64, Word64)? 	deriving (Eq)
hs/DBus/Util.hs view
@@ -1,3 +1,7 @@++#line 58 "src/util.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,8 +16,10 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 59 "src/util.anansi" module DBus.Util where-import Text.Parsec (Parsec, parse)+import Text.ParserCombinators.Parsec (Parser, parse) import Data.Char (digitToInt) import Data.List (isPrefixOf) @@ -21,7 +27,7 @@ checkLength length' s | length s <= length' = Just s checkLength _ _ = Nothing -parseMaybe :: Parsec String () a -> String -> Maybe a+parseMaybe :: Parser a -> String -> Maybe a parseMaybe p = either (const Nothing) Just . parse p ""  mkUnsafe :: Show a => String -> (a -> Maybe b) -> a -> b
hs/DBus/Util/MonadError.hs view
@@ -1,3 +1,7 @@++#line 117 "src/util.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,20 +16,32 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.-{-# LANGUAGE TypeFamilies #-}++#line 118 "src/util.anansi" module DBus.Util.MonadError-	( ErrorT (..)-	, throwError+	( ErrorM (..)+	, ErrorT (..)+	, throwErrorM+	, throwErrorT 	) where-import Control.Monad.Trans.Class-import Control.Monad.State-import Control.Monad.Error.Class+newtype ErrorM e a = ErrorM { runErrorM :: Either e a } -newtype ErrorT e m a = ErrorT { runErrorT :: m (Either e a) }+instance Functor (ErrorM e) where+	fmap f m = ErrorM $ case runErrorM m of+		Left err -> Left err+		Right x -> Right $ f x -instance Functor m => Functor (ErrorT e m) where-	fmap f = ErrorT . fmap (fmap f) . runErrorT+instance Monad (ErrorM e) where+	return = ErrorM . Right+	(>>=) m k = case runErrorM m of+		Left err -> ErrorM $ Left err+		Right x -> k x +throwErrorM :: e -> ErrorM e a+throwErrorM = ErrorM . Left++newtype ErrorT e m a = ErrorT { runErrorT :: m (Either e a) }+ instance Monad m => Monad (ErrorT e m) where 	return = ErrorT . return . Right 	(>>=) m k = ErrorT $ do@@ -34,19 +50,5 @@ 			Left l -> return $ Left l 			Right r -> runErrorT $ k r -instance MonadTrans (ErrorT e) where-	lift = ErrorT . liftM Right--instance Monad m => MonadError (ErrorT e m) where-	type ErrorType (ErrorT e m) = e-	throwError = ErrorT . return . Left-	catchError m h = ErrorT $ do-		x <- runErrorT m-		case x of-			Left l -> runErrorT $ h l-			Right r -> return $ Right r--instance MonadState m => MonadState (ErrorT e m) where-	type StateType (ErrorT e m) = StateType m-	get = lift get-	put = lift . put+throwErrorT :: Monad m => e -> ErrorT e m a+throwErrorT = ErrorT . return . Left
hs/DBus/Wire.hs view
@@ -1,3 +1,7 @@++#line 21 "src/wire.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,12 +16,26 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 22 "src/wire.anansi" module DBus.Wire (++#line 54 "src/wire.anansi" 	  Endianness (..)++#line 238 "src/wire.anansi" 	, MarshalError (..)++#line 442 "src/wire.anansi" 	, UnmarshalError (..)++#line 921 "src/wire.anansi" 	, marshalMessage++#line 991 "src/wire.anansi" 	, unmarshalMessage++#line 24 "src/wire.anansi" 	) where import DBus.Wire.Internal import DBus.Wire.Marshal
hs/DBus/Wire/Internal.hs view
@@ -1,3 +1,7 @@++#line 31 "src/wire.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,9 +16,13 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 32 "src/wire.anansi" module DBus.Wire.Internal where import Data.Word (Word8, Word64) import qualified DBus.Types as T++#line 40 "src/wire.anansi" data Endianness = LittleEndian | BigEndian 	deriving (Show, Eq) @@ -26,7 +34,11 @@ decodeEndianness 108 = Just LittleEndian decodeEndianness 66  = Just BigEndian decodeEndianness _   = Nothing++#line 69 "src/wire.anansi" alignment :: T.Type -> Word8++#line 451 "src/wire.anansi" alignment T.DBusByte    = 1 alignment T.DBusWord16  = 2 alignment T.DBusWord32  = 4@@ -35,14 +47,30 @@ alignment T.DBusInt32   = 4 alignment T.DBusInt64   = 8 alignment T.DBusDouble  = 8++#line 548 "src/wire.anansi" alignment T.DBusBoolean = 4++#line 629 "src/wire.anansi" alignment T.DBusString     = 4 alignment T.DBusObjectPath = 4++#line 671 "src/wire.anansi" alignment T.DBusSignature  = 1++#line 687 "src/wire.anansi" alignment (T.DBusArray _) = 4++#line 769 "src/wire.anansi" alignment (T.DBusDictionary _ _) = 4++#line 786 "src/wire.anansi" alignment (T.DBusStructure _) = 8++#line 804 "src/wire.anansi" alignment T.DBusVariant = 1++#line 71 "src/wire.anansi"  padding :: Word64 -> Word8 -> Word64 padding current count = required where
hs/DBus/Wire/Marshal.hs view
@@ -1,3 +1,7 @@++#line 89 "src/wire.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,94 +16,167 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 90 "src/wire.anansi" {-# LANGUAGE TypeFamilies #-} module DBus.Wire.Marshal where++#line 56 "src/introduction.anansi" import Data.Text.Lazy (Text) import qualified Data.Text.Lazy as TL-import qualified Control.Monad.State as State-import qualified DBus.Util.MonadError as E++#line 93 "src/wire.anansi"++#line 105 "src/wire.anansi"+import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as L import qualified Data.Binary.Builder as B++#line 514 "src/wire.anansi" import Data.Binary.Put (runPut) import qualified Data.Binary.IEEE754 as IEEE++#line 599 "src/wire.anansi" import DBus.Wire.Unicode (maybeEncodeUtf8)-import Data.Text.Lazy.Encoding (encodeUtf8)++#line 703 "src/wire.anansi" import qualified DBus.Constants as C++#line 830 "src/wire.anansi" import qualified DBus.Message.Internal as M++#line 845 "src/wire.anansi" import Data.Bits ((.|.)) import qualified Data.Set as Set++#line 94 "src/wire.anansi" import DBus.Wire.Internal import Control.Monad (when) import Data.Maybe (fromJust) import Data.Word (Word8, Word32, Word64)-import Data.Int (Int16, Int32, Int64)+import Data.Int (Int64)  import qualified DBus.Types as T-data MarshalState = MarshalState Endianness B.Builder !Word64-type MarshalM = E.ErrorT MarshalError (State.State MarshalState)+import qualified DBus.Types.Internal as T++#line 111 "src/wire.anansi"+data MarshalState = MarshalState {-# UNPACK #-} !B.Builder {-# UNPACK #-} !Word64++data MarshalR a = MarshalRL MarshalError | MarshalRR a {-# UNPACK #-} !MarshalState+ type Marshal = MarshalM ()+newtype MarshalM a = MarshalM { unMarshalM :: Endianness -> MarshalState -> MarshalR a }++instance Monad MarshalM where+	{-# INLINE return #-}+	return a = MarshalM $ \_ s -> MarshalRR a s+	+	{-# INLINE (>>=) #-}+	m >>= k = MarshalM $ \e s -> case unMarshalM m e s of+		MarshalRL err -> MarshalRL err+		MarshalRR a s' -> unMarshalM (k a) e s'+	+	{-# INLINE (>>) #-}+	m >> k = MarshalM $ \e s -> case unMarshalM m e s of+		MarshalRL err -> MarshalRL err+		MarshalRR _ s' -> unMarshalM k e s'++throwError :: MarshalError -> MarshalM a+throwError err = MarshalM $ \_ _ -> MarshalRL err++{-# INLINE getState #-}+getState :: MarshalM MarshalState+getState = MarshalM $ \_ s -> MarshalRR s s++{-# INLINE putState #-}+putState :: MarshalState -> MarshalM ()+putState s = MarshalM $ \_ _ -> MarshalRR () s++#line 148 "src/wire.anansi" runMarshal :: Marshal -> Endianness -> Either MarshalError L.ByteString-runMarshal m e = case State.runState (E.runErrorT m) initialState of-	(Right _, MarshalState _ builder _) -> Right (B.toLazyByteString builder)-	(Left  x, _) -> Left x-	where initialState = MarshalState e B.empty 0+runMarshal m e = case unMarshalM m e (MarshalState B.empty 0) of+	MarshalRL err -> Left err+	MarshalRR _ (MarshalState builder _) -> Right $ B.toLazyByteString builder++#line 155 "src/wire.anansi" marshal :: T.Variant -> Marshal-marshal v = marshalType (T.variantType v) where-	x :: T.Variable a => a-	x = fromJust . T.fromVariant $ v-	marshalType :: T.Type -> Marshal-	marshalType T.DBusByte   = append $ L.singleton x-	marshalType T.DBusWord16 = marshalBuilder 2 B.putWord16be B.putWord16le x-	marshalType T.DBusWord32 = marshalBuilder 4 B.putWord32be B.putWord32le x-	marshalType T.DBusWord64 = marshalBuilder 8 B.putWord64be B.putWord64le x-	marshalType T.DBusInt16  = marshalBuilder 2 B.putWord16be B.putWord16le $ fromIntegral (x :: Int16)-	marshalType T.DBusInt32  = marshalBuilder 4 B.putWord32be B.putWord32le $ fromIntegral (x :: Int32)-	marshalType T.DBusInt64  = marshalBuilder 8 B.putWord64be B.putWord64le $ fromIntegral (x :: Int64)-	marshalType T.DBusDouble = do-		pad 8-		(MarshalState e _ _) <- State.get-		let put = case e of-			BigEndian -> IEEE.putFloat64be-			LittleEndian -> IEEE.putFloat64le-		let bytes = runPut $ put x-		append bytes-	marshalType T.DBusBoolean = marshalWord32 $ if x then 1 else 0-	marshalType T.DBusString = marshalText x-	marshalType T.DBusObjectPath = marshalText . T.strObjectPath $ x-	marshalType T.DBusSignature = marshalSignature x-	marshalType (T.DBusArray _) = marshalArray x-	marshalType (T.DBusDictionary _ _) = marshalArray (T.dictionaryToArray x)-	marshalType (T.DBusStructure _) = do-		let T.Structure vs = x+marshal v = case v of++#line 483 "src/wire.anansi"+	T.VarBoxWord8  x -> marshalWord8 x+	T.VarBoxWord16 x -> marshalBuilder 2 B.putWord16be B.putWord16le x+	T.VarBoxWord32 x -> marshalWord32 x+	T.VarBoxWord64 x -> marshalBuilder 8 B.putWord64be B.putWord64le x+	T.VarBoxInt16  x -> marshalBuilder 2 B.putWord16be B.putWord16le $ fromIntegral x+	T.VarBoxInt32  x -> marshalBuilder 4 B.putWord32be B.putWord32le $ fromIntegral x+	T.VarBoxInt64  x -> marshalBuilder 8 B.putWord64be B.putWord64le $ fromIntegral x++#line 523 "src/wire.anansi"+	T.VarBoxDouble x -> marshalDouble x++#line 552 "src/wire.anansi"+	T.VarBoxBool x -> marshalWord32 $ if x then 1 else 0++#line 634 "src/wire.anansi"+	T.VarBoxString x -> marshalText x+	T.VarBoxObjectPath x -> marshalText . T.strObjectPath $ x++#line 675 "src/wire.anansi"+	T.VarBoxSignature x -> marshalSignature x++#line 691 "src/wire.anansi"+	T.VarBoxArray x -> marshalArray x++#line 773 "src/wire.anansi"+	T.VarBoxDictionary x -> marshalArray (T.dictionaryToArray x)++#line 790 "src/wire.anansi"+	T.VarBoxStructure (T.Structure vs) -> do 		pad 8 		mapM_ marshal vs-	marshalType T.DBusVariant = do-		let rawSig = T.typeCode . T.variantType $ x-		sig <- case T.mkSignature rawSig of++#line 808 "src/wire.anansi"+	T.VarBoxVariant x -> do+		let textSig = T.typeCode . T.variantType $ x+		sig <- case T.variantSignature x of 			Just x' -> return x'-			Nothing -> E.throwError $ InvalidVariantSignature rawSig+			Nothing -> throwError $ InvalidVariantSignature textSig 		marshalSignature sig 		marshal x-append :: L.ByteString -> Marshal-append bytes = do-	(MarshalState e builder count) <- State.get-	let builder' = B.append builder $ B.fromLazyByteString bytes-	    count' = count + fromIntegral (L.length bytes)-	State.put $ MarshalState e builder' count'++#line 163 "src/wire.anansi"+appendS :: BS.ByteString -> Marshal+appendS bytes = MarshalM $ \_ (MarshalState builder count) -> let+	builder' = B.append builder $ B.fromByteString bytes+	count' = count + fromIntegral (BS.length bytes)+	in MarshalRR () (MarshalState builder' count')++#line 171 "src/wire.anansi"+appendL :: L.ByteString -> Marshal+appendL bytes = MarshalM $ \_ (MarshalState builder count) -> let+	builder' = B.append builder $ B.fromLazyByteString bytes+	count' = count + fromIntegral (L.length bytes)+	in MarshalRR () (MarshalState builder' count')++#line 179 "src/wire.anansi" pad :: Word8 -> Marshal-pad count = do-	(MarshalState _ _ existing) <- State.get-	let padding' = fromIntegral $ padding existing count-	append $ L.replicate padding' 0+pad count = MarshalM $ \e s@(MarshalState _ existing) -> let+	padding' = fromIntegral $ padding existing count+	bytes = BS.replicate padding' 0+	in unMarshalM (appendS bytes) e s++#line 190 "src/wire.anansi" marshalBuilder :: Word8 -> (a -> B.Builder) -> (a -> B.Builder) -> a -> Marshal marshalBuilder size be le x = do 	pad size-	(MarshalState e builder count) <- State.get-	let builder' = B.append builder $ case e of-		BigEndian -> be x-		LittleEndian -> le x-	let count' = count + fromIntegral size-	State.put $ MarshalState e builder' count'+	MarshalM $ \e (MarshalState builder count) -> let+		builder' = B.append builder $ case e of+			BigEndian -> be x+			LittleEndian -> le x+		size' = fromIntegral size+		in MarshalRR () (MarshalState builder' (count + size'))++#line 216 "src/wire.anansi" data MarshalError 	= MessageTooLong Word64 	| ArrayTooLong Word64@@ -119,55 +196,89 @@ 		["Invalid variant signature: ", show x] 	show (InvalidText x) = concat 		["Text cannot be marshaled: ", show x]++#line 465 "src/wire.anansi" marshalWord32 :: Word32 -> Marshal marshalWord32 = marshalBuilder 4 B.putWord32be B.putWord32le++#line 470 "src/wire.anansi"+{-# INLINE marshalWord8 #-}+marshalWord8 :: Word8 -> Marshal+marshalWord8 x = MarshalM $ \_ (MarshalState builder count) -> let+	builder' = B.append builder $ B.singleton x+	in MarshalRR () (MarshalState builder' (count + 1))++#line 527 "src/wire.anansi"+marshalDouble :: Double -> Marshal+marshalDouble x = do+	pad 8+	MarshalM $ \e s -> let+		put = case e of+			BigEndian -> IEEE.putFloat64be+			LittleEndian -> IEEE.putFloat64le+		bytes = runPut $ put x+		in unMarshalM (appendL bytes) e s++#line 603 "src/wire.anansi" marshalText :: Text -> Marshal marshalText x = do 	bytes <- case maybeEncodeUtf8 x of 		Just x' -> return x'-		Nothing -> E.throwError $ InvalidText x+		Nothing -> throwError $ InvalidText x 	when (L.any (== 0) bytes) $-		E.throwError $ InvalidText x+		throwError $ InvalidText x 	marshalWord32 . fromIntegral . L.length $ bytes-	append bytes-	append (L.singleton 0)+	appendL bytes+	marshalWord8 0++#line 651 "src/wire.anansi" marshalSignature :: T.Signature -> Marshal marshalSignature x = do-	let bytes = encodeUtf8 . T.strSignature $ x-	let size = fromIntegral . L.length $ bytes-	append (L.singleton size)-	append bytes-	append (L.singleton 0)+	let bytes = T.bytesSignature x+	let size = fromIntegral . BS.length $ bytes+	marshalWord8 size+	appendS bytes+	marshalWord8 0++#line 707 "src/wire.anansi" marshalArray :: T.Array -> Marshal marshalArray x = do 	(arrayPadding, arrayBytes) <- getArrayBytes (T.arrayType x) x 	let arrayLen = L.length arrayBytes 	when (arrayLen > fromIntegral C.arrayMaximumLength)-		(E.throwError $ ArrayTooLong $ fromIntegral arrayLen)+		(throwError $ ArrayTooLong $ fromIntegral arrayLen) 	marshalWord32 $ fromIntegral arrayLen-	append $ L.replicate arrayPadding 0-	append arrayBytes+	appendL $ L.replicate arrayPadding 0+	appendL arrayBytes++#line 719 "src/wire.anansi" getArrayBytes :: T.Type -> T.Array -> MarshalM (Int64, L.ByteString) getArrayBytes T.DBusByte x = return (0, bytes) where 	Just bytes = T.arrayToBytes x++#line 725 "src/wire.anansi" getArrayBytes itemType x = do 	let vs = T.arrayItems x-	s <- State.get-	(MarshalState _ _ afterLength) <- marshalWord32 0 >> State.get-	(MarshalState e _ afterPadding) <- pad (alignment itemType) >> State.get+	s <- getState+	(MarshalState _ afterLength) <- marshalWord32 0 >> getState+	(MarshalState _ afterPadding) <- pad (alignment itemType) >> getState 	-	State.put $ MarshalState e B.empty afterPadding-	(MarshalState _ itemBuilder _) <- mapM_ marshal vs >> State.get+	putState $ MarshalState B.empty afterPadding+	(MarshalState itemBuilder _) <- mapM_ marshal vs >> getState 	 	let itemBytes = B.toLazyByteString itemBuilder 	    paddingSize = fromIntegral $ afterPadding - afterLength 	-	State.put s+	putState s 	return (paddingSize, itemBytes)++#line 850 "src/wire.anansi" encodeFlags :: Set.Set M.Flag -> Word8 encodeFlags flags = foldr (.|.) 0 $ map flagValue $ Set.toList flags where 	flagValue M.NoReplyExpected = 0x1 	flagValue M.NoAutoStart     = 0x2++#line 868 "src/wire.anansi" encodeField :: M.HeaderField -> T.Structure encodeField (M.Path x)        = encodeField' 1 x encodeField (M.Interface x)   = encodeField' 2 x@@ -183,49 +294,64 @@ 	[ T.toVariant code 	, T.toVariant $ T.toVariant x 	]++#line 925 "src/wire.anansi"++#line 163 "src/api-docs.anansi" -- | Convert a 'M.Message' into a 'L.ByteString'. Although unusual, it is -- possible for marshaling to fail -- if this occurs, an appropriate error -- will be returned instead.++#line 926 "src/wire.anansi" marshalMessage :: M.Message a => Endianness -> M.Serial -> a                -> Either MarshalError L.ByteString marshalMessage e serial msg = runMarshal marshaler e where 	body = M.messageBody msg 	marshaler = do 		sig <- checkBodySig body-		empty <- State.get+		empty <- getState 		mapM_ marshal body-		(MarshalState _ bodyBytesB _) <- State.get-		State.put empty+		(MarshalState bodyBytesB _) <- getState+		putState empty 		marshalEndianness e 		let bodyBytes = B.toLazyByteString bodyBytesB 		marshalHeader msg serial sig 			$ fromIntegral . L.length $ bodyBytes 		pad 8-		append bodyBytes+		appendL bodyBytes 		checkMaximumSize++#line 946 "src/wire.anansi" checkBodySig :: [T.Variant] -> MarshalM T.Signature checkBodySig vs = let-	sigStr = TL.concat . map (T.typeCode . T.variantType) $ vs-	invalid = E.throwError $ InvalidBodySignature sigStr-	in case T.mkSignature sigStr of+	textSig = TL.concat . map (T.typeCode . T.variantType) $ vs+	bytesSig = BS.concat . map (T.typeCodeB . T.variantType) $ vs+	invalid = throwError $ InvalidBodySignature textSig+	in case T.mkBytesSignature bytesSig of 		Just x -> return x 		Nothing -> invalid++#line 957 "src/wire.anansi" marshalHeader :: M.Message a => a -> M.Serial -> T.Signature -> Word32               -> Marshal marshalHeader msg serial bodySig bodyLength = do 	let fields = M.Signature bodySig : M.messageHeaderFields msg-	marshal . T.toVariant . M.messageTypeCode $ msg-	marshal . T.toVariant . encodeFlags . M.messageFlags $ msg-	marshal . T.toVariant $ C.protocolVersion+	marshalWord8 . M.messageTypeCode $ msg+	marshalWord8 . encodeFlags . M.messageFlags $ msg+	marshalWord8 C.protocolVersion 	marshalWord32 bodyLength-	marshal . T.toVariant $ serial+	marshalWord32 . M.serialValue $ serial 	let fieldType = T.DBusStructure [T.DBusByte, T.DBusVariant]-	marshal . T.toVariant . fromJust . T.toArray fieldType+	marshalArray . fromJust . T.toArray fieldType 	        $ map encodeField fields++#line 972 "src/wire.anansi" marshalEndianness :: Endianness -> Marshal marshalEndianness = marshal . T.toVariant . encodeEndianness++#line 977 "src/wire.anansi" checkMaximumSize :: Marshal checkMaximumSize = do-	(MarshalState _ _ messageLength) <- State.get+	(MarshalState _ messageLength) <- getState 	when (messageLength > fromIntegral C.messageMaximumLength)-		(E.throwError $ MessageTooLong $ fromIntegral messageLength)+		(throwError $ MessageTooLong $ fromIntegral messageLength)
hs/DBus/Wire/Unicode.hs view
@@ -1,3 +1,7 @@++#line 574 "src/wire.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,6 +16,8 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 575 "src/wire.anansi" module DBus.Wire.Unicode 	( maybeEncodeUtf8 	, maybeDecodeUtf8) where
hs/DBus/Wire/Unmarshal.hs view
@@ -1,3 +1,7 @@++#line 246 "src/wire.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,39 +16,102 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 247 "src/wire.anansi"++#line 52 "src/introduction.anansi" {-# LANGUAGE OverloadedStrings #-}++#line 248 "src/wire.anansi" {-# LANGUAGE TypeFamilies #-} module DBus.Wire.Unmarshal where++#line 56 "src/introduction.anansi" import Data.Text.Lazy (Text) import qualified Data.Text.Lazy as TL-import qualified Control.Monad.State as State-import Control.Monad.Trans.Class (lift)++#line 251 "src/wire.anansi"++#line 262 "src/wire.anansi"+import Control.Monad (liftM) import qualified DBus.Util.MonadError as E-import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL++#line 364 "src/wire.anansi" import qualified Data.Binary.Get as G++#line 519 "src/wire.anansi" import qualified Data.Binary.IEEE754 as IEEE++#line 616 "src/wire.anansi" import DBus.Wire.Unicode (maybeDecodeUtf8)++#line 834 "src/wire.anansi" import qualified DBus.Message.Internal as M++#line 840 "src/wire.anansi" import Data.Bits ((.&.)) import qualified Data.Set as Set++#line 987 "src/wire.anansi" import qualified DBus.Constants as C-import Control.Monad (when, unless, liftM)++#line 252 "src/wire.anansi"+import Control.Monad (when, unless) import Data.Maybe (fromJust, listToMaybe, fromMaybe) import Data.Word (Word8, Word32, Word64) import Data.Int (Int16, Int32, Int64)  import DBus.Wire.Internal-import qualified DBus.Types as T-data UnmarshalState = UnmarshalState Endianness L.ByteString !Word64-type Unmarshal = E.ErrorT UnmarshalError (State.State UnmarshalState)-runUnmarshal :: Unmarshal a -> Endianness -> L.ByteString -> Either UnmarshalError a-runUnmarshal m e bytes = State.evalState (E.runErrorT m) state where-	state = UnmarshalState e bytes 0+import qualified DBus.Types.Internal as T++#line 272 "src/wire.anansi"+data UnmarshalState = UnmarshalState BL.ByteString {-# UNPACK #-} !Word64++data UnmarshalR a = UnmarshalRL UnmarshalError | UnmarshalRR a {-# UNPACK #-} !UnmarshalState++newtype Unmarshal a = Unmarshal { unUnmarshal :: Endianness -> UnmarshalState -> UnmarshalR a }++instance Monad Unmarshal where+	{-# INLINE return #-}+	return a = Unmarshal $ \_ s -> UnmarshalRR a s+	+	{-# INLINE (>>=) #-}+	m >>= k = Unmarshal $ \e s -> case unUnmarshal m e s of+		UnmarshalRL err -> UnmarshalRL err+		UnmarshalRR a s' -> unUnmarshal (k a) e s'+	+	{-# INLINE (>>) #-}+	m >> k = Unmarshal $ \e s -> case unUnmarshal m e s of+		UnmarshalRL err -> UnmarshalRL err+		UnmarshalRR _ s' -> unUnmarshal k e s'++throwError :: UnmarshalError -> Unmarshal a+throwError err = Unmarshal $ \_ _ -> UnmarshalRL err++{-# INLINE getState #-}+getState :: Unmarshal UnmarshalState+getState = Unmarshal $ \_ s -> UnmarshalRR s s++{-# INLINE putState #-}+putState :: UnmarshalState -> Unmarshal ()+putState s = Unmarshal $ \_ _ -> UnmarshalRR () s++#line 305 "src/wire.anansi"+runUnmarshal :: Unmarshal a -> Endianness -> BL.ByteString -> Either UnmarshalError a+runUnmarshal m e bytes = case unUnmarshal m e (UnmarshalState bytes 0) of+	UnmarshalRL err -> Left err+	UnmarshalRR a _ -> Right a++#line 312 "src/wire.anansi" unmarshal :: T.Signature -> Unmarshal [T.Variant] unmarshal = mapM unmarshalType . T.signatureTypes  unmarshalType :: T.Type -> Unmarshal T.Variant-unmarshalType T.DBusByte = fmap (T.toVariant . L.head) $ consume 1++#line 493 "src/wire.anansi"+unmarshalType T.DBusByte = liftM (T.toVariant . BL.head) $ consume 1 unmarshalType T.DBusWord16 = unmarshalGet' 2 G.getWord16be G.getWord16le unmarshalType T.DBusWord32 = unmarshalGet' 4 G.getWord32be G.getWord32le unmarshalType T.DBusWord64 = unmarshalGet' 8 G.getWord64be G.getWord64le@@ -60,76 +127,104 @@ unmarshalType T.DBusInt64  = do 	x <- unmarshalGet 8 G.getWord64be G.getWord64le 	return . T.toVariant $ (fromIntegral x :: Int64)++#line 539 "src/wire.anansi" unmarshalType T.DBusDouble = unmarshalGet' 8 IEEE.getFloat64be IEEE.getFloat64le++#line 556 "src/wire.anansi" unmarshalType T.DBusBoolean = unmarshalWord32 >>= 	fromMaybeU' "boolean" (\x -> case x of 		0 -> Just False 		1 -> Just True 		_ -> Nothing)-unmarshalType T.DBusString = fmap T.toVariant unmarshalText +#line 639 "src/wire.anansi"+unmarshalType T.DBusString = liftM T.toVariant unmarshalText+ unmarshalType T.DBusObjectPath = unmarshalText >>= 	fromMaybeU' "object path" T.mkObjectPath-unmarshalType T.DBusSignature = fmap T.toVariant unmarshalSignature-unmarshalType (T.DBusArray t) = T.toVariant `fmap` unmarshalArray t++#line 679 "src/wire.anansi"+unmarshalType T.DBusSignature = liftM T.toVariant unmarshalSignature++#line 695 "src/wire.anansi"+unmarshalType (T.DBusArray t) = T.toVariant `liftM` unmarshalArray t++#line 777 "src/wire.anansi" unmarshalType (T.DBusDictionary kt vt) = do 	let pairType = T.DBusStructure [kt, vt] 	array <- unmarshalArray pairType 	fromMaybeU' "dictionary" T.arrayToDictionary array++#line 796 "src/wire.anansi" unmarshalType (T.DBusStructure ts) = do 	skipPadding 8-	fmap (T.toVariant . T.Structure) $ mapM unmarshalType ts+	liftM (T.toVariant . T.Structure) $ mapM unmarshalType ts++#line 818 "src/wire.anansi" unmarshalType T.DBusVariant = do 	let getType sig = case T.signatureTypes sig of 		[t] -> Just t 		_   -> Nothing 	 	t <- fromMaybeU "variant signature" getType =<< unmarshalSignature-	T.toVariant `fmap` unmarshalType t-consume :: Word64 -> Unmarshal L.ByteString-consume count = do-	(UnmarshalState e bytes offset) <- State.get-	let (x, bytes') = L.splitAt (fromIntegral count) bytes-	unless (L.length x == fromIntegral count) $-		E.throwError $ UnexpectedEOF offset-	-	State.put $ UnmarshalState e bytes' (offset + count)-	return x+	T.toVariant `liftM` unmarshalType t++#line 322 "src/wire.anansi"+{-# INLINE consume #-}+consume :: Word64 -> Unmarshal BL.ByteString+consume count = Unmarshal $ \_ (UnmarshalState bytes offset) -> let+	count' = fromIntegral count+	(x, bytes') = BL.splitAt count' bytes+	in if BL.length x == count'+		then UnmarshalRR x (UnmarshalState bytes' (offset + count))+		else UnmarshalRL $ UnexpectedEOF offset++#line 333 "src/wire.anansi" skipPadding :: Word8 -> Unmarshal () skipPadding count = do-	(UnmarshalState _ _ offset) <- State.get+	(UnmarshalState _ offset) <- getState 	bytes <- consume $ padding offset count-	unless (L.all (== 0) bytes) $-		E.throwError $ InvalidPadding offset+	unless (BL.all (== 0) bytes) $+		throwError $ InvalidPadding offset++#line 342 "src/wire.anansi" skipTerminator :: Unmarshal () skipTerminator = do-	(UnmarshalState _ _ offset) <- State.get+	(UnmarshalState _ offset) <- getState 	bytes <- consume 1-	unless (L.all (== 0) bytes) $-		E.throwError $ MissingTerminator offset+	unless (BL.all (== 0) bytes) $+		throwError $ MissingTerminator offset++#line 351 "src/wire.anansi" fromMaybeU :: Show a => Text -> (a -> Maybe b) -> a -> Unmarshal b fromMaybeU label f x = case f x of 	Just x' -> return x'-	Nothing -> E.throwError . Invalid label . TL.pack . show $ x+	Nothing -> throwError . Invalid label . TL.pack . show $ x  fromMaybeU' :: (Show a, T.Variable b) => Text -> (a -> Maybe b) -> a            -> Unmarshal T.Variant fromMaybeU' label f x = do 	x' <- fromMaybeU label f x 	return $ T.toVariant x'++#line 368 "src/wire.anansi" unmarshalGet :: Word8 -> G.Get a -> G.Get a -> Unmarshal a unmarshalGet count be le = do 	skipPadding count-	(UnmarshalState e _ _) <- State.get 	bs <- consume . fromIntegral $ count-	let get' = case e of-		BigEndian -> be-		LittleEndian -> le-	return $ G.runGet get' bs+	+	Unmarshal $ \e s -> let+		get = case e of+			BigEndian -> be+			LittleEndian -> le+		in UnmarshalRR (G.runGet get bs) s  unmarshalGet' :: T.Variable a => Word8 -> G.Get a -> G.Get a               -> Unmarshal T.Variant-unmarshalGet' count be le = T.toVariant `fmap` unmarshalGet count be le+unmarshalGet' count be le = T.toVariant `liftM` unmarshalGet count be le++#line 385 "src/wire.anansi" untilM :: Monad m => m Bool -> m a -> m [a] untilM test comp = do 	done <- test@@ -139,6 +234,8 @@ 			x <- comp 			xs <- untilM test comp 			return $ x:xs++#line 412 "src/wire.anansi" data UnmarshalError 	= UnsupportedProtocolVersion Word8 	| UnexpectedEOF Word64@@ -166,46 +263,60 @@ 	show (MissingTerminator pos) = concat 		["Missing NUL terminator at position ", show pos] 	show ArraySizeMismatch = "Array size mismatch"++#line 478 "src/wire.anansi" unmarshalWord32 :: Unmarshal Word32 unmarshalWord32 = unmarshalGet 4 G.getWord32be G.getWord32le++#line 620 "src/wire.anansi" unmarshalText :: Unmarshal Text unmarshalText = do 	byteCount <- unmarshalWord32 	bytes <- consume . fromIntegral $ byteCount 	skipTerminator 	fromMaybeU "text" maybeDecodeUtf8 bytes++#line 661 "src/wire.anansi" unmarshalSignature :: Unmarshal T.Signature unmarshalSignature = do-	byteCount <- L.head `fmap` consume 1-	bytes <- consume $ fromIntegral byteCount-	sigText <- fromMaybeU "text" maybeDecodeUtf8 bytes+	byteCount <- BL.head `liftM` consume 1+	lazy <- consume $ fromIntegral byteCount 	skipTerminator-	fromMaybeU "signature" T.mkSignature sigText+	let bytes = B.concat $ BL.toChunks lazy+	fromMaybeU "signature" T.mkBytesSignature bytes++#line 744 "src/wire.anansi" unmarshalArray :: T.Type -> Unmarshal T.Array unmarshalArray T.DBusByte = do 	byteCount <- unmarshalWord32-	T.arrayFromBytes `fmap` consume (fromIntegral byteCount)+	T.arrayFromBytes `liftM` consume (fromIntegral byteCount)++#line 751 "src/wire.anansi" unmarshalArray itemType = do 	let getOffset = do-		(UnmarshalState _ _ o) <- State.get+		(UnmarshalState _ o) <- getState 		return o 	byteCount <- unmarshalWord32 	skipPadding (alignment itemType) 	start <- getOffset 	let end = start + fromIntegral byteCount-	vs <- untilM (fmap (>= end) getOffset) (unmarshalType itemType)+	vs <- untilM (liftM (>= end) getOffset) (unmarshalType itemType) 	end' <- getOffset 	when (end' > end) $-		E.throwError ArraySizeMismatch+		throwError ArraySizeMismatch 	fromMaybeU "array" (T.arrayFromItems itemType) vs++#line 857 "src/wire.anansi" decodeFlags :: Word8 -> Set.Set M.Flag decodeFlags word = Set.fromList flags where 	flagSet = [ (0x1, M.NoReplyExpected) 	          , (0x2, M.NoAutoStart) 	          ] 	flags = flagSet >>= \(x, y) -> [y | word .&. x > 0]-decodeField :: Monad m => T.Structure-            -> E.ErrorT UnmarshalError m [M.HeaderField]++#line 886 "src/wire.anansi"+decodeField :: T.Structure+            -> E.ErrorM UnmarshalError [M.HeaderField] decodeField struct = case unpackField struct of 	(1, x) -> decodeField' x M.Path "path" 	(2, x) -> decodeField' x M.Interface "interface"@@ -217,68 +328,106 @@ 	(8, x) -> decodeField' x M.Signature "signature" 	_      -> return [] -decodeField' :: (Monad m, T.Variable a) => T.Variant -> (a -> b) -> Text-             -> E.ErrorT UnmarshalError m [b]+decodeField' :: T.Variable a => T.Variant -> (a -> b) -> Text+             -> E.ErrorM UnmarshalError [b] decodeField' x f label = case T.fromVariant x of 	Just x' -> return [f x']-	Nothing -> E.throwError $ InvalidHeaderField label x+	Nothing -> E.throwErrorM $ InvalidHeaderField label x++#line 907 "src/wire.anansi" unpackField :: T.Structure -> (Word8, T.Variant) unpackField struct = (c', v') where 	T.Structure [c, v] = struct 	c' = fromJust . T.fromVariant $ c 	v' = fromJust . T.fromVariant $ v++#line 995 "src/wire.anansi"++#line 169 "src/api-docs.anansi" -- | Read bytes from a monad until a complete message has been received.-unmarshalMessage :: Monad m => (Word32 -> m L.ByteString)++#line 996 "src/wire.anansi"+unmarshalMessage :: Monad m => (Word32 -> m BL.ByteString)                  -> m (Either UnmarshalError M.ReceivedMessage) unmarshalMessage getBytes' = E.runErrorT $ do-	let getBytes = lift . getBytes'+	let getBytes = E.ErrorT . liftM Right . getBytes' 	++#line 1011 "src/wire.anansi" 	let fixedSig = "yyyyuuu" 	fixedBytes <- getBytes 16-	let messageVersion = L.index fixedBytes 3++#line 1020 "src/wire.anansi"+	let messageVersion = BL.index fixedBytes 3 	when (messageVersion /= C.protocolVersion) $-		E.throwError $ UnsupportedProtocolVersion messageVersion-	let eByte = L.index fixedBytes 0+		E.throwErrorT $ UnsupportedProtocolVersion messageVersion++#line 1028 "src/wire.anansi"+	let eByte = BL.index fixedBytes 0 	endianness <- case decodeEndianness eByte of 		Just x' -> return x'-		Nothing -> E.throwError . Invalid "endianness" . TL.pack . show $ eByte+		Nothing -> E.throwErrorT . Invalid "endianness" . TL.pack . show $ eByte++#line 1038 "src/wire.anansi" 	let unmarshal' x bytes = case runUnmarshal (unmarshal x) endianness bytes of 		Right x' -> return x'-		Left  e  -> E.throwError e+		Left  e  -> E.throwErrorT e 	fixed <- unmarshal' fixedSig fixedBytes 	let typeCode = fromJust . T.fromVariant $ fixed !! 1 	let flags = decodeFlags . fromJust . T.fromVariant $ fixed !! 2 	let bodyLength = fromJust . T.fromVariant $ fixed !! 4 	let serial = fromJust . T.fromVariant $ fixed !! 5++#line 1053 "src/wire.anansi" 	let fieldByteCount = fromJust . T.fromVariant $ fixed !! 6++#line 1002 "src/wire.anansi"++#line 1060 "src/wire.anansi" 	let headerSig  = "yyyyuua(yv)" 	fieldBytes <- getBytes fieldByteCount-	let headerBytes = L.append fixedBytes fieldBytes+	let headerBytes = BL.append fixedBytes fieldBytes 	header <- unmarshal' headerSig headerBytes++#line 1069 "src/wire.anansi" 	let fieldArray = fromJust . T.fromVariant $ header !! 6 	let fieldStructures = fromJust . T.fromArray $ fieldArray-	fields <- concat `liftM` mapM decodeField fieldStructures+	fields <- case E.runErrorM $ concat `liftM` mapM decodeField fieldStructures of+		Left err -> E.throwErrorT err+		Right x -> return x++#line 1003 "src/wire.anansi"++#line 1080 "src/wire.anansi" 	let bodyPadding = padding (fromIntegral fieldByteCount + 16) 8 	getBytes . fromIntegral $ bodyPadding++#line 1091 "src/wire.anansi" 	let bodySig = findBodySignature fields++#line 1097 "src/wire.anansi" 	bodyBytes <- getBytes bodyLength 	body <- unmarshal' bodySig bodyBytes-	y <- case buildReceivedMessage typeCode fields of-		EitherM (Right x) -> return x-		EitherM (Left x) -> E.throwError $ MissingHeaderField x++#line 1004 "src/wire.anansi"++#line 1105 "src/wire.anansi"+	y <- case E.runErrorM $ buildReceivedMessage typeCode fields of+		Right x -> return x+		Left err -> E.throwErrorT $ MissingHeaderField err 	return $ y serial flags body++#line 1085 "src/wire.anansi" findBodySignature :: [M.HeaderField] -> T.Signature findBodySignature fields = fromMaybe "" signature where 	signature = listToMaybe [x | M.Signature x <- fields]-newtype EitherM a b = EitherM (Either a b) -instance Monad (EitherM a) where-	return = EitherM . Right-	(EitherM (Left x)) >>= _ = EitherM (Left x)-	(EitherM (Right x)) >>= k = k x-buildReceivedMessage :: Word8 -> [M.HeaderField] -> EitherM Text+#line 1114 "src/wire.anansi"+buildReceivedMessage :: Word8 -> [M.HeaderField] -> E.ErrorM Text                         (M.Serial -> (Set.Set M.Flag) -> [T.Variant]                          -> M.ReceivedMessage)++#line 1122 "src/wire.anansi" buildReceivedMessage 1 fields = do 	path <- require "path" [x | M.Path x <- fields] 	member <- require "member name" [x | M.Member x <- fields]@@ -288,6 +437,8 @@ 		sender = listToMaybe [x | M.Sender x <- fields] 		msg = M.MethodCall path member iface dest flags body 		in M.ReceivedMethodCall serial sender msg++#line 1136 "src/wire.anansi" buildReceivedMessage 2 fields = do 	replySerial <- require "reply serial" [x | M.ReplySerial x <- fields] 	return $ \serial _ body -> let@@ -295,6 +446,8 @@ 		sender = listToMaybe [x | M.Sender x <- fields] 		msg = M.MethodReturn replySerial dest body 		in M.ReceivedMethodReturn serial sender msg++#line 1148 "src/wire.anansi" buildReceivedMessage 3 fields = do 	name <- require "error name" [x | M.ErrorName x <- fields] 	replySerial <- require "reply serial" [x | M.ReplySerial x <- fields]@@ -303,6 +456,8 @@ 		sender = listToMaybe [x | M.Sender x <- fields] 		msg = M.Error name replySerial dest body 		in M.ReceivedError serial sender msg++#line 1161 "src/wire.anansi" buildReceivedMessage 4 fields = do 	path <- require "path" [x | M.Path x <- fields] 	member <- require "member name" [x | M.Member x <- fields]@@ -312,10 +467,14 @@ 		sender = listToMaybe [x | M.Sender x <- fields] 		msg = M.Signal path member iface dest body 		in M.ReceivedSignal serial sender msg++#line 1175 "src/wire.anansi" buildReceivedMessage typeCode fields = return $ \serial flags body -> let 	sender = listToMaybe [x | M.Sender x <- fields] 	msg = M.Unknown typeCode flags body 	in M.ReceivedUnknown serial sender msg-require :: Text -> [a] -> EitherM Text a++#line 1182 "src/wire.anansi"+require :: Text -> [a] -> E.ErrorM Text a require _     (x:_) = return x-require label _     = EitherM $ Left label+require label _     = E.throwErrorM label
hs/Tests.hs view
@@ -1,3 +1,7 @@++#line 64 "src/introduction.anansi"++#line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin <jmillikin@gmail.com> --  -- This program is free software: you can redistribute it and/or modify@@ -12,8 +16,16 @@ --  -- You should have received a copy of the GNU General Public License -- along with this program.  If not, see <http://www.gnu.org/licenses/>.++#line 65 "src/introduction.anansi"++#line 52 "src/introduction.anansi" {-# LANGUAGE OverloadedStrings #-}++#line 66 "src/introduction.anansi" module Main (tests, main) where++#line 159 "src/util.anansi" import Test.QuickCheck import qualified Test.Framework as F import Test.Framework.Providers.QuickCheck2 (testProperty)@@ -40,8 +52,12 @@ import DBus.Wire.Unmarshal import qualified DBus.Introspection as I +#line 68 "src/introduction.anansi"+ tests :: [F.Test] tests = [ F.testGroup "dummy" []++#line 384 "src/types.anansi" 	, F.testGroup "String" 		[ testProperty "String -> strict Text" 			$ funEq (fromVariant . toVariant) (Just . T.pack)@@ -56,19 +72,27 @@ 		, testProperty "Strict Text <- lazy Text" 			$ funEq (fromVariant . toVariant) (Just . T.pack . TL.unpack) 		]++#line 736 "src/types.anansi" 	, F.testGroup "Signature" 		[ testProperty "Signature identity" 			$ funEq (mkSignature . strSignature) Just 		]++#line 806 "src/types.anansi" 	, F.testGroup "ObjectPath" 		[ testProperty "ObjectPath identity" 			$ funEq (mkObjectPath . strObjectPath) Just 		]++#line 942 "src/types.anansi" 	, F.testGroup "Array" 		[ testProperty "Array identity" 			$ \x -> Just x == arrayFromItems (arrayType x) (arrayItems x) 		, testProperty "Array homogeneity" prop_ArrayHomogeneous 		]++#line 1071 "src/types.anansi" 	, F.testGroup "Dictionary" 		[ testProperty "Dictionary identity" 			$ \x -> Just x == dictionaryFromItems@@ -82,22 +106,32 @@ 		, testProperty "Dictionary <-> Array conversion" 			$ funEq (arrayToDictionary . dictionaryToArray) Just 		]++#line 1232 "src/types.anansi" 	, F.testGroup "BusName" 		[ testProperty "BusName identity" 			$ funEq (mkBusName . strBusName) Just 		]++#line 1279 "src/types.anansi" 	, F.testGroup "InterfaceName" 		[ testProperty "InterfaceName identity" 			$ funEq (mkInterfaceName . strInterfaceName) Just 		]++#line 1311 "src/types.anansi" 	, F.testGroup "ErrorName" 		[ testProperty "ErrorName identity" 			$ funEq (mkErrorName . strErrorName) Just 		]++#line 1354 "src/types.anansi" 	, F.testGroup "MemberName" 		[ testProperty "MemberName identity" 			$ funEq (mkMemberName . strMemberName) Just 		]++#line 1223 "src/wire.anansi" 	, F.testGroup "Wire format" 		[ testProperty "Marshal -> Ummarshal" prop_Unmarshal 		, F.testGroup "Messages"@@ -107,6 +141,8 @@ 			, testProperty "Signals" prop_WireSignal 			] 		]++#line 177 "src/addresses.anansi" 	, F.testGroup "Addresses" 		[ testProperty "Address identity" 			$ \x -> mkAddresses (strAddress x) == Just [x]@@ -137,6 +173,8 @@ 			, test "no param" $ isNothing . mkAddresses $ "a:," 			] 		]++#line 508 "src/introspection.anansi" 	, F.testGroup "Introspection" 		[ testProperty "Generate -> Parse" 			$ \x@(I.Object path _ _) -> let@@ -145,10 +183,14 @@ 			parsed = I.fromXML path xml' 			in isJust xml ==> I.fromXML path xml' == Just x 		]++#line 72 "src/introduction.anansi" 	]  main :: IO () main = F.defaultMain tests++#line 106 "src/types.anansi" instance Arbitrary Type where 	arbitrary = oneof [atomicType, containerType] @@ -179,6 +221,8 @@ 			return $ DBusDictionary kt vt 		2 -> fmap DBusStructure $ halfSized arbitrary 		3 -> return DBusVariant++#line 272 "src/types.anansi" instance Arbitrary Variant where 	arbitrary = arbitrary >>= genVariant @@ -200,14 +244,20 @@ 	(DBusDictionary _ _) -> fmap toVariant (arbitrary :: Gen Dictionary) 	(DBusStructure _)    -> fmap toVariant (arbitrary :: Gen Structure) 	DBusVariant          -> fmap toVariant (arbitrary :: Gen Variant)++#line 731 "src/types.anansi" instance Arbitrary Signature where 	arbitrary = sizedText 255 $ fmap (TL.concat . map typeCode) arbitrary++#line 797 "src/types.anansi" instance Arbitrary ObjectPath where 	arbitrary = fmap (mkObjectPath_ . TL.pack) path' where 		c = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_" 		path = fmap (intercalate "/" . ([] :)) genElements 		path' = frequency [(1, return "/"), (9, path)] 		genElements = atLeast 1 (atLeast 1 (elements c))++#line 926 "src/types.anansi" instance Arbitrary Array where 	arbitrary = do 		t <- atomicType@@ -221,6 +271,8 @@ 	firstType = if null types 		then DBusByte 		else head types++#line 1053 "src/types.anansi" instance Arbitrary Dictionary where 	arbitrary = do 		kt <- atomicType@@ -236,8 +288,12 @@ 	vType = dictionaryValueType x 	correctType (k, v) = variantType k == kType && 	                     variantType v == vType++#line 1138 "src/types.anansi" instance Arbitrary Structure where 	arbitrary = fmap Structure $ halfSized arbitrary++#line 1212 "src/types.anansi" instance Arbitrary BusName where 	arbitrary = sizedText 255 (oneof [unique, wellKnown]) where 		c = ['a'..'z'] ++ ['A'..'Z'] ++ "_-"@@ -255,6 +311,8 @@ 			x <- elements start 			xs <- atLeast 0 (elements c') 			return (x:xs)++#line 1265 "src/types.anansi" instance Arbitrary InterfaceName where 	arbitrary = sizedText 255 genName where 		c = ['a'..'z'] ++ ['A'..'Z'] ++ "_"@@ -266,8 +324,12 @@ 			x <- elements c 			xs <- atLeast 0 (elements c') 			return (x:xs)++#line 1306 "src/types.anansi" instance Arbitrary ErrorName where 	arbitrary = fmap (mkErrorName_ . strInterfaceName) arbitrary++#line 1342 "src/types.anansi" instance Arbitrary MemberName where 	arbitrary = sizedText 255 genName where 		c = ['a'..'z'] ++ ['A'..'Z'] ++ "_"@@ -277,10 +339,16 @@ 			x <- elements c 			xs <- atLeast 0 (elements c') 			return . TL.pack $ (x:xs)++#line 73 "src/messages.anansi" instance Arbitrary Flag where 	arbitrary = elements [NoReplyExpected, NoAutoStart]++#line 131 "src/messages.anansi" instance Arbitrary Serial where 	arbitrary = fmap Serial arbitrary++#line 173 "src/messages.anansi" instance Arbitrary MethodCall where 	arbitrary = do 		path   <- arbitrary@@ -290,12 +358,16 @@ 		flags  <- fmap Set.fromList arbitrary 		Structure body <- arbitrary 		return $ MethodCall path member iface dest flags body++#line 210 "src/messages.anansi" instance Arbitrary MethodReturn where 	arbitrary = do 		serial <- arbitrary 		dest   <- arbitrary 		Structure body <- arbitrary 		return $ MethodReturn serial dest body++#line 261 "src/messages.anansi" instance Arbitrary Error where 	arbitrary = do 		name   <- arbitrary@@ -303,6 +375,8 @@ 		dest   <- arbitrary 		Structure body <- arbitrary 		return $ Error name serial dest body++#line 300 "src/messages.anansi" instance Arbitrary Signal where 	arbitrary = do 		path   <- arbitrary@@ -311,8 +385,12 @@ 		dest   <- arbitrary 		Structure body <- arbitrary 		return $ Signal path member iface dest body++#line 58 "src/wire.anansi" instance Arbitrary Endianness where 	arbitrary = elements [LittleEndian, BigEndian]++#line 1188 "src/wire.anansi" prop_Unmarshal :: Endianness -> Variant -> Property prop_Unmarshal e x = valid ==> unmarshaled == Right [x] where 	sig = mkSignature . typeCode . variantType $ x@@ -345,6 +423,8 @@  prop_WireSignal e serial msg = prop_MarshalMessage e serial msg 	$ ReceivedSignal serial Nothing msg++#line 138 "src/addresses.anansi" instance Arbitrary Address where 	arbitrary = genAddress where 		optional = ['0'..'9'] ++ ['a'..'z'] ++ ['A'..'Z'] ++ "-_/\\*."@@ -381,6 +461,8 @@ 			let addrStr = concat [m, ":", params, extraSemicolon] 			let Just [addr] = mkAddresses $ TL.pack addrStr 			return addr++#line 443 "src/introspection.anansi" subObject :: ObjectPath -> Gen I.Object subObject parentPath = sized $ \n -> resize (min n 4) $ do 	let nonRoot = do@@ -443,6 +525,8 @@ 			[[], [I.Read], [I.Write], 			 [I.Read, I.Write]] 		return $ I.Property (TL.pack name) sig access++#line 187 "src/util.anansi" halfSized :: Gen a -> Gen a halfSized gen = sized $ \n -> if n > 0 then 	resize (n `div` 2) gen@@ -466,9 +550,13 @@  isRight :: Either a b -> Bool isRight = either (const False) (const True)++#line 216 "src/util.anansi" test :: Testable a => F.TestName -> a -> F.Test test name prop = F.plusTestOptions options (testProperty name prop) where 	options = F.TestOptions Nothing (Just 1) Nothing Nothing++#line 225 "src/util.anansi" instance Arbitrary Word8 where 	arbitrary = arbitraryBoundedIntegral 	shrink    = shrinkIntegral
src/addresses.anansi view
@@ -30,8 +30,8 @@ import Data.Char (ord, chr) import qualified Data.Map as M import Text.Printf (printf)-import qualified Text.Parsec as P-import Text.Parsec ((<|>))+import qualified Text.ParserCombinators.Parsec as P+import Text.ParserCombinators.Parsec ((<|>)) import DBus.Util (hexToInt, eitherToMaybe) : 
src/authentication.anansi view
@@ -44,6 +44,7 @@ :d authentication imports import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString.Lazy as ByteString+import Data.ByteString.Lazy.Char8 () import qualified DBus.UUID as UUID : 
src/connections.anansi view
@@ -190,7 +190,7 @@ value must be converted from host order using {\tt Data.Binary}.  :d connection imports-import qualified Text.Parsec as P+import qualified Text.ParserCombinators.Parsec as P import Control.Monad (unless) import Data.Binary.Get (runGet, getWord16host) import Data.Binary.Put (runPut, putWord16be)@@ -202,7 +202,10 @@ 		P.eof 		let value = read chars :: Integer 		unless (value > 0 && value <= 65535) $-			P.parserFail "bad port" >> return ()+			-- Calling 'fail' is acceptable here, because Parsec 2+			-- offers no other error reporting mechanism, and+			-- implements 'fail'.+			fail "bad port" 		let word = fromIntegral value 		return $ runGet getWord16host (runPut (putWord16be word)) :
src/introspection.anansi view
@@ -25,18 +25,18 @@ <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"          "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node name="/org/example/example">-	<interface name="org.example.ExampleInterface">-		<method name="Echo">-			<arg name="text" type="s" direction="in"/>-			<arg type="s" direction="out"/>-		</method>-		<signal name="Echoed">-			<arg type="s"/>-		</signal>-		<property name="EchoCount" type="u" access="read"/>-	</interface>-	<node name="child_a"/>-	<node name="child/b"/>+    <interface name="org.example.ExampleInterface">+        <method name="Echo">+            <arg name="text" type="s" direction="in"/>+            <arg type="s" direction="out"/>+        </method>+        <signal name="Echoed">+            <arg type="s"/>+        </signal>+        <property name="EchoCount" type="u" access="read"/>+    </interface>+    <node name="child_a"/>+    <node name="child/b"/> </node> \end{verbatim} @@ -59,13 +59,6 @@ import qualified DBus.Types as T : -HaXml is used to do the heavy lifting of XML parsing because HXT cannot-be combined with Parsec 3.--:d introspection imports-import qualified Text.XML.HaXml as H-:- \subsection{Data types}  :f DBus/Introspection.hs@@ -93,37 +86,71 @@  \subsection{Parsing XML} -The root {\tt node} is special, in that it's the only {\tt node} which is-not required to have a {\tt name} attribute. If the root has no {\tt name},-its path will default to the path of the introspected object.- If parsing fails, {\tt fromXML} will return {\tt Nothing}. Aside from the elements directly accessed by the parser, no effort is made to check the document's validity because there is no DTD as of yet. -:d introspection imports-import Text.XML.HaXml.Parse (xmlParse')-import DBus.Util (eitherToMaybe)-:- :f DBus/Introspection.hs fromXML :: T.ObjectPath -> Text -> Maybe Object fromXML path text = do-	doc <- eitherToMaybe . xmlParse' "" . TL.unpack $ text-	let (H.Document _ _ root _) = doc+	root <- parseElement text 	parseRoot path root : +LibXML's event-based parsing is used to convert the input into a list of+events. DBus's introspection format doesn't use any character data, so only+the element attributes and nesting are preserved.++:d introspection imports+import qualified Data.XML.Types as X+import qualified Text.XML.LibXML.SAX as SAX+import Control.Monad.ST (runST)+import qualified Data.STRef as ST+import Data.Text.Encoding (encodeUtf8)+:++:f DBus/Introspection.hs+parseElement :: Text -> Maybe X.Element+parseElement text = runST $ do+	stackRef <- ST.newSTRef [([], [])]+	let callbacks = SAX.ParserCallbacks+		{ SAX.parsedBeginElement = \_ attrs -> do+			ST.modifySTRef stackRef ((attrs, []):)+		, SAX.parsedEndElement = \name -> do+			stack <- ST.readSTRef stackRef+			let (attrs, children'):stack' = stack+			let e = X.Element name attrs (map X.NodeElement (reverse children'))+			let (pAttrs, pChildren):stack'' = stack'+			let parent = (pAttrs, e:pChildren)+			ST.writeSTRef stackRef (parent:stack'')+		, SAX.parsedCharacters = \_ -> return ()+		, SAX.parsedComment = \_ -> return ()+		, SAX.parsedProcessingInstruction = \_ -> return ()+		}+	let onError _ = ST.writeSTRef stackRef []+	p <- SAX.newParser callbacks onError Nothing+	mapM_ (SAX.parse p . encodeUtf8) $ TL.toChunks text+	SAX.parseComplete p+	stack <- ST.readSTRef stackRef+	return $ case stack of+		[] -> Nothing+		(_, children'):_ -> Just $ head children'+:++The root {\tt node} is special, in that it's the only {\tt node} which is+not required to have a {\tt name} attribute. If the root has no {\tt name},+its path will default to the path of the introspected object.+ Even though the root object's {\tt name} is optional, if present, it must still be a valid object path.  :f DBus/Introspection.hs-parseRoot :: T.ObjectPath -> H.Element a -> Maybe Object+parseRoot :: T.ObjectPath -> X.Element -> Maybe Object parseRoot defaultPath e = do-	path <- case getAttr "name" e of+	path <- case getattrM "name" e of 		Nothing -> Just defaultPath 		Just x  -> T.mkObjectPath x-	parseObject' path e+	parseObject path e :  Child {\tt nodes} have ``relative'' paths -- that is, their {\tt name}@@ -131,37 +158,37 @@ the root object's path.  :f DBus/Introspection.hs-parseChild :: T.ObjectPath -> H.Element a -> Maybe Object+parseChild :: T.ObjectPath -> X.Element -> Maybe Object parseChild parentPath e = do 	let parentPath' = case T.strObjectPath parentPath of 		"/" -> "/" 		x   -> TL.append x "/"-	pathSegment <- getAttr "name" e+	pathSegment <- getattrM "name" e 	path <- T.mkObjectPath $ TL.append parentPath' pathSegment-	parseObject' path e+	parseObject path e :  Other than the name, both root and non-root {\tt nodes} have identical contents.  They may contain interface definitions, and child {\tt node}s.  :f DBus/Introspection.hs-parseObject' :: T.ObjectPath -> H.Element a -> Maybe Object-parseObject' path e@(H.Elem "node" _ _)  = do-	interfaces <- children parseInterface (H.tag "interface") e-	children' <- children (parseChild path) (H.tag "node") e+parseObject :: T.ObjectPath -> X.Element -> Maybe Object+parseObject path e | X.elementName e == toName "node" = do+	interfaces <- children parseInterface (named "interface") e+	children' <- children (parseChild path) (named "node") e 	return $ Object path interfaces children'-parseObject' _ _ = Nothing+parseObject _ _ = Nothing :  Interfaces may contain methods, signals, and properties.  :f DBus/Introspection.hs-parseInterface :: H.Element a -> Maybe Interface+parseInterface :: X.Element -> Maybe Interface parseInterface e = do-	name <- T.mkInterfaceName =<< getAttr "name" e-	methods <- children parseMethod (H.tag "method") e-	signals <- children parseSignal (H.tag "signal") e-	properties <- children parseProperty (H.tag "property") e+	name <- T.mkInterfaceName =<< getattrM "name" e+	methods <- children parseMethod (named "method") e+	signals <- children parseSignal (named "signal") e+	properties <- children parseProperty (named "property") e 	return $ Interface name methods signals properties : @@ -169,9 +196,9 @@ if no direction is specified.  :f DBus/Introspection.hs-parseMethod :: H.Element a -> Maybe Method+parseMethod :: X.Element -> Maybe Method parseMethod e = do-	name <- T.mkMemberName =<< getAttr "name" e+	name <- T.mkMemberName =<< getattrM "name" e 	paramsIn <- children parseParameter (isParam ["in", ""]) e 	paramsOut <- children parseParameter (isParam ["out"]) e 	return $ Method name paramsIn paramsOut@@ -180,9 +207,9 @@ Signals are similar to methods, except they have no ``in'' parameters.  :f DBus/Introspection.hs-parseSignal :: H.Element a -> Maybe Signal+parseSignal :: X.Element -> Maybe Signal parseSignal e = do-	name <- T.mkMemberName =<< getAttr "name" e+	name <- T.mkMemberName =<< getattrM "name" e 	params <- children parseParameter (isParam ["out", ""]) e 	return $ Signal name params :@@ -190,17 +217,17 @@ A parameter has a free-form name, and a single valid type.  :f DBus/Introspection.hs-parseParameter :: H.Element a -> Maybe Parameter+parseParameter :: X.Element -> Maybe Parameter parseParameter e = do-	let name = getAttr' "name" e+	let name = getattr "name" e 	sig <- parseType e 	return $ Parameter name sig :  :f DBus/Introspection.hs-parseType :: H.Element a -> Maybe T.Signature+parseType :: X.Element -> Maybe T.Signature parseType e = do-	sig <- T.mkSignature =<< getAttr "type" e+	sig <- T.mkSignature =<< getattrM "type" e 	case T.signatureTypes sig of 		[_] -> Just sig 		_   -> Nothing@@ -210,11 +237,11 @@ Each property may be read, written, or both, and has an associated type.  :f DBus/Introspection.hs-parseProperty :: H.Element a -> Maybe Property+parseProperty :: X.Element -> Maybe Property parseProperty e = do-	let name = getAttr' "name" e+	let name = getattr "name" e 	sig <- parseType e-	access <- case getAttr' "access" e of+	access <- case getattr "access" e of 		""          -> Just [] 		"read"      -> Just [Read] 		"write"     -> Just [Write]@@ -223,182 +250,191 @@ 	return $ Property name sig access : -HaXml doesn't seem to have any way to retrieve the ``real'' value of an-attribute, so {\tt attrValue} implements this.- :d introspection imports-import Data.Char (chr)+import Control.Monad ((>=>))+import Data.Maybe (fromMaybe, listToMaybe) :  :f DBus/Introspection.hs-attrValue :: H.AttValue -> Maybe Text-attrValue attr = fmap (TL.pack . concat) $ mapM unescape parts where-	(H.AttValue parts) = attr-	-	unescape (Left x) = Just x-	unescape (Right (H.RefEntity x)) = lookup x namedRefs-	unescape (Right (H.RefChar x)) = Just [chr x]-	-	namedRefs =-		[ ("lt", "<")-		, ("gt", ">")-		, ("amp", "&")-		, ("apos", "'")-		, ("quot", "\"")-		]-:--Some helper functions for dealing with HaXml filters+getattrM :: Text -> X.Element -> Maybe Text+getattrM name = fmap attrText . listToMaybe . attrs where+	attrText = textContent . X.attributeContent+	attrs = X.elementAttributes >=> X.isNamed (toName name)+	textContent cs = TL.concat [t | X.ContentText t <- cs] -:d introspection imports-import Data.Maybe (fromMaybe)-:+getattr :: Text -> X.Element -> Text+getattr = (fromMaybe "" .) . getattrM -:f DBus/Introspection.hs-getAttr :: String -> H.Element a -> Maybe Text-getAttr name (H.Elem _ attrs _) = lookup name attrs >>= attrValue+isParam :: [Text] -> X.Element -> [X.Element]+isParam dirs = named "arg" >=> checkDir where+	checkDir e = [e | getattr "direction" e `elem` dirs] -getAttr' :: String -> H.Element a -> Text-getAttr' = (fromMaybe "" .) . getAttr+children :: Monad m => (X.Element -> m b) -> (X.Element -> [X.Element]) -> X.Element -> m [b]+children f p = mapM f . concatMap p . X.elementChildren -isParam :: [Text] -> H.CFilter a-isParam dirs content = do-	arg@(H.CElem e _) <- H.tag "arg" content-	let direction = getAttr' "direction" e-	[arg | direction `elem` dirs]+named :: X.Named a => Text -> a -> [a]+named = X.isNamed . toName -children :: Monad m => (H.Element a -> m b) -> H.CFilter a -> H.Element a -> m [b]-children f filt (H.Elem _ _ contents) =-	mapM f [x | (H.CElem x _) <- concatMap filt contents]+toName :: Text -> X.Name+toName t = X.Name t Nothing Nothing :  \subsection{Generating XML} -:f DBus/Introspection.hs-dtdPublicID, dtdSystemID :: String-dtdPublicID = "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"-dtdSystemID = "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"-:+Generating XML can fail; if a child object's path is not a sub-path of the+parent, {\tt toXML} will return {\tt Nothing}. -HaXml punts to the {\tt pretty} package for serialising XML.+To simplify XML serialization, a variant of {\tt Writer} is used to combine+text fragments with optional failure. This would be {\tt WriterT Text Maybe},+except I didn't want to drag in a dependency on {\tt transformers} for such+a little thing. -:d introspection imports-import Text.XML.HaXml.Pretty (document)-import Text.PrettyPrint.HughesPJ (render)+:f DBus/Introspection.hs+newtype XmlWriter a = XmlWriter { runXmlWriter :: Maybe (a, Text) }++instance Monad XmlWriter where+	return a = XmlWriter $ Just (a, TL.empty)+	m >>= f = XmlWriter $ do+		(a, w) <- runXmlWriter m+		(b, w') <- runXmlWriter (f a)+		return (b, TL.append w w') : -Generating XML can fail; if a child object's path is not a sub-path of the-parent, {\tt toXML} will return {\tt Nothing}.+:f DBus/Introspection.hs+tell :: Text -> XmlWriter ()+tell t = XmlWriter $ Just ((), t)+:  :f DBus/Introspection.hs toXML :: Object -> Maybe Text-toXML obj = fmap (TL.pack . render . document) doc where-	prolog = H.Prolog Nothing [] (Just doctype) []-	doctype = H.DTD "node" (Just (H.PUBLIC-		(H.PubidLiteral dtdPublicID)-		(H.SystemLiteral dtdSystemID))) []-	doc = do-		root <- xmlRoot obj-		return $ H.Document prolog H.emptyST root []+toXML obj = do+	(_, text) <- runXmlWriter (writeRoot obj)+	return text : -When writing objects to {\tt node}s, the root object must have an absolute-path, and children must have paths relative to their parent.- :f DBus/Introspection.hs-xmlRoot :: Object -> Maybe (H.Element a)-xmlRoot obj@(Object path _ _) = do-	(H.CElem root _) <- xmlObject' (T.strObjectPath path) obj-	return root+writeRoot :: Object -> XmlWriter ()+writeRoot obj@(Object path _ _) = do+	tell "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'"+	tell " 'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>\n"+	writeObject (T.strObjectPath path) obj :  :f DBus/Introspection.hs-xmlObject :: T.ObjectPath -> Object -> Maybe (H.Content a)-xmlObject parentPath obj@(Object path _ _) = do-	let path' = T.strObjectPath path-	    parent' = T.strObjectPath parentPath-	relpath <- if TL.isPrefixOf parent' path'+writeChild :: T.ObjectPath -> Object -> XmlWriter ()+writeChild parentPath obj@(Object path _ _) = write where+	path' = T.strObjectPath path+	parent' = T.strObjectPath parentPath+	relpathM = if TL.isPrefixOf parent' path' 		then Just $ if parent' == "/" 			then TL.drop 1 path' 			else TL.drop (TL.length parent' + 1) path' 		else Nothing-	xmlObject' relpath obj+	+	write = case relpathM of+		Just relpath -> writeObject relpath obj+		Nothing -> XmlWriter Nothing :  :f DBus/Introspection.hs-xmlObject' :: Text -> Object -> Maybe (H.Content a)-xmlObject' path (Object fullPath interfaces children') = do-	children'' <- mapM (xmlObject fullPath) children'-	return $ mkElement "node"-		[mkAttr "name" $ TL.unpack path]-		$ concat-			[ map xmlInterface interfaces-			, children''-			]+writeObject :: Text -> Object -> XmlWriter ()+writeObject path (Object fullPath interfaces children') = writeElement "node"+	[("name", path)] $ do+		mapM_ writeInterface interfaces+		mapM_ (writeChild fullPath) children' :  :f DBus/Introspection.hs-xmlInterface :: Interface -> H.Content a-xmlInterface (Interface name methods signals properties) =-	mkElement "interface"-		[mkAttr "name" . TL.unpack . T.strInterfaceName $ name]-		$ concat-			[ map xmlMethod methods-			, map xmlSignal signals-			, map xmlProperty properties-			]+writeInterface :: Interface -> XmlWriter ()+writeInterface (Interface name methods signals properties) = writeElement "interface"+	[("name", T.strInterfaceName name)] $ do+		mapM_ writeMethod methods+		mapM_ writeSignal signals+		mapM_ writeProperty properties :  :f DBus/Introspection.hs-xmlMethod :: Method -> H.Content a-xmlMethod (Method name inParams outParams) = mkElement "method"-	[mkAttr "name" . TL.unpack . T.strMemberName $ name]-	$ concat-		[ map (xmlParameter "in") inParams-		, map (xmlParameter "out") outParams-		]+writeMethod :: Method -> XmlWriter ()+writeMethod (Method name inParams outParams) = writeElement "method"+	[("name", T.strMemberName name)] $ do+		mapM_ (writeParameter "in") inParams+		mapM_ (writeParameter "out") outParams :  :f DBus/Introspection.hs-xmlSignal :: Signal -> H.Content a-xmlSignal (Signal name params) = mkElement "signal"-	[mkAttr "name" . TL.unpack . T.strMemberName $ name]-	$ map (xmlParameter "out") params+writeSignal :: Signal -> XmlWriter ()+writeSignal (Signal name params) = writeElement "signal"+	[("name", T.strMemberName name)] $ do+		mapM_ (writeParameter "out") params :  :f DBus/Introspection.hs-xmlParameter :: String -> Parameter -> H.Content a-xmlParameter direction (Parameter name sig) = mkElement "arg"-	[ mkAttr "name" . TL.unpack $ name-	, mkAttr "type" . TL.unpack . T.strSignature $ sig-	, mkAttr "direction" direction-	] []+writeParameter :: Text -> Parameter -> XmlWriter ()+writeParameter direction (Parameter name sig) = writeEmptyElement "arg"+	[ ("name", name)+	, ("type", T.strSignature sig)+	, ("direction", direction)+	] :  :f DBus/Introspection.hs-xmlProperty :: Property -> H.Content a-xmlProperty (Property name sig access) = mkElement "property"-	[ mkAttr "name" . TL.unpack $ name-	, mkAttr "type" . TL.unpack . T.strSignature $ sig-	, mkAttr "access" $ xmlAccess access-	] []+writeProperty :: Property -> XmlWriter ()+writeProperty (Property name sig access) = writeEmptyElement "property"+	[ ("name", name)+	, ("type", T.strSignature sig)+	, ("access", strAccess access)+	] :  :f DBus/Introspection.hs-xmlAccess :: [PropertyAccess] -> String-xmlAccess access = readS ++ writeS where+strAccess :: [PropertyAccess] -> Text+strAccess access = TL.append readS writeS where 	readS = if elem Read access then "read" else "" 	writeS = if elem Write access then "write" else "" :  :f DBus/Introspection.hs-mkElement :: String -> [H.Attribute] -> [H.Content a] -> H.Content a-mkElement name attrs contents = H.CElem (H.Elem name attrs contents) undefined+writeElement :: Text -> [(Text, Text)] -> XmlWriter () -> XmlWriter ()+writeElement name attrs content = do+	tell "<"+	tell name+	mapM_ writeAttribute attrs+	tell ">"+	content+	tell "</"+	tell name+	tell ">"+: -mkAttr :: String -> String -> H.Attribute-mkAttr name value = (name, H.AttValue [Left escaped]) where-	raw = H.CString True value ()-	escaped = H.verbatim $ H.xmlEscapeContent H.stdXmlEscaper [raw]+:f DBus/Introspection.hs+writeEmptyElement :: Text -> [(Text, Text)] -> XmlWriter ()+writeEmptyElement name attrs = do+	tell "<"+	tell name+	mapM_ writeAttribute attrs+	tell "/>"+:++:f DBus/Introspection.hs+writeAttribute :: (Text, Text) -> XmlWriter ()+writeAttribute (name, content) = do+	tell " "+	tell name+	tell "='"+	tell (escape content)+	tell "'"+:++:f DBus/Introspection.hs+escape :: Text -> Text+escape = TL.concatMap escapeChar where+	escapeChar c = case c of+		'&' -> "&amp;"+		'<' -> "&lt;"+		'>' -> "&gt;"+		'"' -> "&quot;"+		'\'' -> "&apos;"+		_ -> TL.singleton c :  \subsection{Test support}
src/types.anansi view
@@ -16,17 +16,28 @@ \section{Types}  The {\tt DBus.Types module} defines interfaces for storing, building, and-deconstructing D-Bus values.+deconstructing D-Bus values. Everything is defined in an internal module, and+then exported via the public interface. -:f DBus/Types.cpphs+:f DBus/Types.hs |copyright|-|text extensions|-|type extensions| module DBus.Types ( 	|type exports| 	) where+import DBus.Types.Internal+:++:f DBus/Types/Internal.cpphs+|copyright|+|text extensions|+|type extensions|+module DBus.Types.Internal where |text imports| |type imports|+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as B8+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Char8 as BL8 :  D-Bus types are divided into two categories, ``atomic'' and ``container''@@ -37,7 +48,7 @@  Internally, types are represented using an enumeration. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs data Type 	= DBusBoolean 	| DBusByte@@ -58,7 +69,7 @@ 	deriving (Show, Eq) : -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs |apidoc isAtomicType| isAtomicType :: Type -> Bool isAtomicType DBusBoolean    = True@@ -79,7 +90,7 @@ Each type can be converted to a textual representation, used in ``type signatures'' or for debugging. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs |apidoc typeCode| typeCode :: Type -> Text |type codes|@@ -135,7 +146,7 @@ outside of this module, {\tt Variant}s can only be constructed from pre-defined types. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs |apidoc Variant| data Variant 	= VarBoxBool Bool@@ -171,7 +182,7 @@ be parsed or inspected or anything like that, since the output format might change drastically. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs instance Show Variant where 	showsPrec d var = showParen (d > 10) full where 		full = s "Variant " . shows code . s " " . valueStr@@ -203,7 +214,7 @@ {\tt variantType} is used to retrieve which type is actually stored within a {\tt Variant}. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs |apidoc variantType| variantType :: Variant -> Type variantType var = case var of@@ -228,6 +239,9 @@ 	(VarBoxStructure x) -> let 		Structure items = x 		in DBusStructure (map variantType items)++variantSignature :: Variant -> Maybe Signature+variantSignature = mkBytesSignature . typeCodeB . variantType :  :d type exports@@ -237,7 +251,7 @@ A macro is useful for reducing verbosity in simple {\tt Variable} instances. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs #define INSTANCE_VARIABLE(TYPE) \ 	instance Variable TYPE where \ 		{ toVariant = VarBox/**/TYPE \@@ -248,7 +262,7 @@  Since {\tt Variant}s are D-Bus values themselves, they can be stored in variants. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs INSTANCE_VARIABLE(Variant) : @@ -312,7 +326,7 @@ import Data.Int (Int16, Int32, Int64) : -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs INSTANCE_VARIABLE(Bool) INSTANCE_VARIABLE(Word8) INSTANCE_VARIABLE(Int16)@@ -330,7 +344,7 @@ inefficent. To provide better performance for large strings, packed Unicode strings defined in {\tt Data.Text} are used internally. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs instance Variable TL.Text where 	toVariant = VarBoxString 	fromVariant (VarBoxString x) = Just x@@ -345,7 +359,7 @@ import qualified Data.Text as T : -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs instance Variable T.Text where 	toVariant = toVariant . TL.fromChunks . (:[]) 	fromVariant = fmap (T.concat . TL.toChunks) . fromVariant@@ -358,7 +372,7 @@ {-# LANGUAGE TypeSynonymInstances #-} : -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs instance Variable String where 	toVariant = toVariant . TL.pack 	fromVariant = fmap TL.unpack . fromVariant@@ -391,7 +405,7 @@ one requires using the {\tt mkSignature} function, which will convert a valid D-Bus signature string into a {\tt Signature}. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs INSTANCE_VARIABLE(Signature) data Signature = Signature { signatureTypes :: [Type] } 	deriving (Eq)@@ -404,7 +418,10 @@ Signatures can also be converted back into text, by concatenating the type codes of their contained types. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs+bytesSignature :: Signature -> B.ByteString+bytesSignature (Signature ts) = B.concat $ map typeCodeB ts+ strSignature :: Signature -> Text strSignature (Signature ts) = TL.concat $ map typeCode ts :@@ -416,7 +433,7 @@ import Data.Ord (comparing) : -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs instance Ord Signature where 	compare = comparing strSignature :@@ -433,34 +450,41 @@ For atomic types, the type code is a single letter. Arrays, structures, and dictionary types are multiple characters. +:d type imports+import Data.Text.Encoding (decodeUtf8)+:+ :d type codes-typeCode DBusBoolean    = "b"-typeCode DBusByte       = "y"-typeCode DBusInt16      = "n"-typeCode DBusInt32      = "i"-typeCode DBusInt64      = "x"-typeCode DBusWord16     = "q"-typeCode DBusWord32     = "u"-typeCode DBusWord64     = "t"-typeCode DBusDouble     = "d"-typeCode DBusString     = "s"-typeCode DBusSignature  = "g"-typeCode DBusObjectPath = "o"-typeCode DBusVariant    = "v"+typeCode t = TL.fromChunks [decodeUtf8 $ typeCodeB t]++typeCodeB :: Type -> B.ByteString+typeCodeB DBusBoolean    = "b"+typeCodeB DBusByte       = "y"+typeCodeB DBusInt16      = "n"+typeCodeB DBusInt32      = "i"+typeCodeB DBusInt64      = "x"+typeCodeB DBusWord16     = "q"+typeCodeB DBusWord32     = "u"+typeCodeB DBusWord64     = "t"+typeCodeB DBusDouble     = "d"+typeCodeB DBusString     = "s"+typeCodeB DBusSignature  = "g"+typeCodeB DBusObjectPath = "o"+typeCodeB DBusVariant    = "v" :  An array's type code is ``a'' followed by the type it contains. For example, an array of booleans would have the type string ``ab''.  :d type codes-typeCode (DBusArray t) = TL.cons 'a' $ typeCode t+typeCodeB (DBusArray t) = B8.cons 'a' $ typeCodeB t :  A dictionary's type code is ``a\{$key\_type$ $value\_type$\}''. For example, a dictionary of bytes to booleans would have the type string ``a\{yb\}''.  :d type codes-typeCode (DBusDictionary k v) = TL.concat ["a{", typeCode k, typeCode v, "}"]+typeCodeB (DBusDictionary k v) = B.concat ["a{", typeCodeB k, typeCodeB v, "}"] :  A structure's type code is the concatenation of its contained types,@@ -468,112 +492,201 @@ type code is simply ``()''.  :d type codes-typeCode (DBusStructure ts) = TL.concat $-	["("] ++ map typeCode ts ++ [")"]+typeCodeB (DBusStructure ts) = B.concat $+	["("] ++ map typeCodeB ts ++ [")"] :  \subsubsection{Parsing}  When parsing, additional restrictions apply which are not inherent to the-D-Bus type system. {\tt mkSignature} guarantees that any {\tt Signature} is-valid according to D-Bus rules.+D-Bus type system. The signature parsing functions guarantee that any+{\tt Signature} is valid according to D-Bus rules. -:f DBus/Types.cpphs-mkSignature :: Text -> Maybe Signature-mkSignature text = parsed where-	|fast signature parser|-	|slow signature parser|-	parsed = case TL.length text of-		0 -> Just $ Signature []-		1 -> fast-		_ -> slow+Signature parsing is the most common operation when unmarshaling messages;+therefore, an efficient parsing implementation is essential.++Since all valid signatures as entirely ASCII, it's possible to parse them+using byte-based parsers for better performance. These aren't exported from+the public interface, but are still used within {\tt DBus.Wire} modules.++:d type imports+import qualified Data.ByteString.Unsafe as B+import qualified Foreign as F+import System.IO.Unsafe (unsafePerformIO) : -Since single-type and empty signatures are very common, they are-special-cased for performance.+There are three special cases which can be optimized: -:d fast signature parser-just t = Just $ Signature [t]-fast = case TL.head text of-	'b' -> just DBusBoolean-	'y' -> just DBusByte-	'n' -> just DBusInt16-	'i' -> just DBusInt32-	'x' -> just DBusInt64-	'q' -> just DBusWord16-	'u' -> just DBusWord32-	't' -> just DBusWord64-	'd' -> just DBusDouble-	's' -> just DBusString-	'g' -> just DBusSignature-	'o' -> just DBusObjectPath-	'v' -> just DBusVariant-	_   -> Nothing+\begin{enumerate}+\item Empty signatures occur when messages have no body, and can use a constant+      result.+\item Single-character signatures occur when parsing variants, and can use a+      faster atom-only parser.+\item Signatures larger than 255 characters are invalid.+\end{enumerate}++:f DBus/Types/Internal.cpphs+mkBytesSignature :: B.ByteString -> Maybe Signature+mkBytesSignature = unsafePerformIO . flip B.unsafeUseAsCStringLen io where+	|fast signature parser|+	|slow signature parser|+	+	io (cstr, len) = case len of+		0 -> return $ Just $ Signature []+		1 -> fmap fast $ F.peek cstr+		_ | len <= 255 -> slow (F.castPtr cstr) len+		_ -> return Nothing : -Parsec is used to parse more complex signatures.+:d fast signature parser+parseAtom c yes no = case c of+	0x62 -> yes DBusBoolean+	0x79 -> yes DBusByte+	0x6E -> yes DBusInt16+	0x69 -> yes DBusInt32+	0x78 -> yes DBusInt64+	0x71 -> yes DBusWord16+	0x75 -> yes DBusWord32+	0x74 -> yes DBusWord64+	0x64 -> yes DBusDouble+	0x73 -> yes DBusString+	0x67 -> yes DBusSignature+	0x6F -> yes DBusObjectPath+	_ -> no -:d type imports-import Text.Parsec ((<|>))-import qualified Text.Parsec as P-import DBus.Util (checkLength, parseMaybe)+fast c = parseAtom c (\t -> Just (Signature [t])) $ case c of+	0x76 -> Just (Signature [DBusVariant])+	_ -> Nothing :  :d slow signature parser-slow = (parseMaybe sigParser =<<) . checkLength 255 . TL.unpack $ text-sigParser = do-	types <- P.many parseType-	P.eof-	return $ Signature types+slow :: F.Ptr Word8 -> Int -> IO (Maybe Signature)+slow buf len = loop [] 0 where+	loop acc ii | ii >= len = return . Just . Signature $ reverse acc+	loop acc ii = do+		c <- F.peekElemOff buf ii+		let next t = loop (t : acc) (ii + 1)+		parseAtom c next $ case c of+			0x76 -> next DBusVariant+			+			-- '('+			0x28 -> do+				mt <- structure buf len (ii + 1)+				case mt of+					Just (ii', t) -> loop (t : acc) ii'+					Nothing -> return Nothing+			+			-- 'a'+			0x61 -> do+				mt <- array buf len (ii + 1)+				case mt of+					Just (ii', t) -> loop (t : acc) ii'+					Nothing -> return Nothing+			+			_ -> return Nothing : -Types may be either containers or atoms. Containers can't be dictionary-keys.+:d slow signature parser+structure :: F.Ptr Word8 -> Int -> Int -> IO (Maybe (Int, Type))+structure buf len = loop [] where+	loop _ ii | ii >= len = return Nothing+	loop acc ii = do+		c <- F.peekElemOff buf ii+		let next t = loop (t : acc) (ii + 1)+		parseAtom c next $ case c of+			0x76 -> next DBusVariant+			+			-- '('+			0x28 -> do+				mt <- structure buf len (ii + 1)+				case mt of+					Just (ii', t) -> loop (t : acc) ii'+					Nothing -> return Nothing+			+			-- ')'+			0x29 -> return $ Just $ (ii + 1, DBusStructure (reverse acc))+			+			-- 'a'+			0x61 -> do+				mt <- array buf len (ii + 1)+				case mt of+					Just (ii', t) -> loop (t : acc) ii'+					Nothing -> return Nothing+			+			_ -> return Nothing+:  :d slow signature parser-parseType = parseAtom <|> parseContainer-parseContainer =-	    parseArray-	<|> parseStruct-	<|> (P.char 'v' >> return DBusVariant)+array :: F.Ptr Word8 -> Int -> Int -> IO (Maybe (Int, Type))+array _   len ii | ii >= len = return Nothing+array buf len ii = do+	c <- F.peekElemOff buf ii+	let next t = return $ Just (ii + 1, DBusArray t)+	parseAtom c next $ case c of+		0x76 -> next DBusVariant+		+		-- '('+		0x28 -> do+			mt <- structure buf len (ii + 1)+			case mt of+				Just (ii', t) -> return $ Just (ii', DBusArray t)+				Nothing -> return Nothing+		+		-- '{'+		0x7B -> dict buf len (ii + 1)+		+		-- 'a'+		0x61 -> do+			mt <- array buf len (ii + 1)+			case mt of+				Just (ii', t) -> return $ Just (ii', DBusArray t)+				Nothing -> return Nothing+		+		_ -> return Nothing :  :d slow signature parser-parseArray = do-	P.char 'a'-	parseDict <|> fmap DBusArray parseType-parseDict = do-	P.char '{'-	keyType <- parseAtom-	valueType <- parseType-	P.char '}'-	return $ DBusDictionary keyType valueType+dict :: F.Ptr Word8 -> Int -> Int -> IO (Maybe (Int, Type))+dict _   len ii | ii + 1 >= len = return Nothing+dict buf len ii = do+	c1 <- F.peekElemOff buf ii+	c2 <- F.peekElemOff buf (ii + 1)+	+	let mt1 = parseAtom c1 Just Nothing+	+	let next t = return $ Just (ii + 2, t)+	mt2 <- parseAtom c2 next $ case c2 of+		0x76 -> next DBusVariant+		+		-- '('+		0x28 -> structure buf len (ii + 2)+		+		-- 'a'+		0x61 -> array buf len (ii + 2)+		+		_ -> return Nothing+	+	case mt2 of+		Nothing -> return Nothing+		Just (ii', t2) -> if ii' >= len+			then return Nothing+			else do+				c3 <- F.peekElemOff buf ii'+				return $ do+					if c3 == 0x7D then Just () else Nothing+					t1 <- mt1+					Just (ii' + 1, DBusDictionary t1 t2) : -Structures can contain any types.+The public interface for building signatures is {\tt Text}-based. -:d slow signature parser-parseStruct = do-	P.char '('-	types <- P.many parseType-	P.char ')'-	return $ DBusStructure types+:d type imports+import Data.Text.Lazy.Encoding (encodeUtf8) : -:d slow signature parser-parseAtom =-	    (P.char 'b' >> return DBusBoolean)-	<|> (P.char 'y' >> return DBusByte)-	<|> (P.char 'n' >> return DBusInt16)-	<|> (P.char 'i' >> return DBusInt32)-	<|> (P.char 'x' >> return DBusInt64)-	<|> (P.char 'q' >> return DBusWord16)-	<|> (P.char 'u' >> return DBusWord32)-	<|> (P.char 't' >> return DBusWord64)-	<|> (P.char 'd' >> return DBusDouble)-	<|> (P.char 's' >> return DBusString)-	<|> (P.char 'g' >> return DBusSignature)-	<|> (P.char 'o' >> return DBusObjectPath)+:f DBus/Types/Internal.cpphs+mkSignature :: Text -> Maybe Signature+mkSignature = mkBytesSignature . B.concat . BL.toChunks . encodeUtf8 :  Since many signatures are defined as string literals, it's useful to@@ -585,12 +698,12 @@ import qualified Data.String as String : -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs mkSignature_ :: Text -> Signature mkSignature_ = mkUnsafe "signature" mkSignature  instance String.IsString Signature where-	fromString = mkSignature_ . TL.pack+	fromString = mkUnsafe "signature" mkBytesSignature . BL8.pack :  Most signature-related functions are exposed to clients, except the@@ -602,6 +715,18 @@ , mkSignature_ : +Checking if a type is valid according to signature rules is common, and if+performed through the signature parser, slow. Since such types are already+guaranteed to be structurally valid, the only test needed is whether their+size is within bounds.++:f DBus/Types/Internal.cpphs+maybeValidType :: Type -> Maybe ()+maybeValidType t = if B.length (typeCodeB t) > 255+	then Nothing+	else Just ()+:+ :f Tests.hs instance Arbitrary Signature where 	arbitrary = sizedText 255 $ fmap (TL.concat . map typeCode) arbitrary@@ -616,7 +741,7 @@  \subsection{Object paths} -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs INSTANCE_VARIABLE(ObjectPath) newtype ObjectPath = ObjectPath 	{ strObjectPath :: Text@@ -643,7 +768,13 @@ Element names are separated by {\tt '/'}, and the path may not end in {\tt '/'} unless it is the root path. -:f DBus/Types.cpphs+:d type imports+import Text.ParserCombinators.Parsec ((<|>))+import qualified Text.ParserCombinators.Parsec as P+import DBus.Util (checkLength, parseMaybe)+:++:f DBus/Types/Internal.cpphs mkObjectPath :: Text -> Maybe ObjectPath mkObjectPath s = parseMaybe path' (TL.unpack s) where 	c = P.oneOf $ ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_"@@ -687,16 +818,11 @@ Many D-Bus APIs represent binary data using an array of bytes; therefore, there is a special constructor for {\tt ByteString}-based arrays. -:d type imports-import Data.ByteString.Lazy (ByteString)-import qualified Data.ByteString.Lazy as ByteString-:--:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs INSTANCE_VARIABLE(Array) data Array 	= VariantArray Type [Variant]-	| ByteArray ByteString+	| ByteArray BL.ByteString 	deriving (Eq)  |apidoc arrayType|@@ -706,7 +832,7 @@  arrayItems :: Array -> [Variant] arrayItems (VariantArray _ xs) = xs-arrayItems (ByteArray xs) = map toVariant $ ByteString.unpack xs+arrayItems (ByteArray xs) = map toVariant $ BL.unpack xs :  :d type exports@@ -719,7 +845,7 @@ Like {\tt Variant}, deriving {\tt Show} for {\tt Array} is mostly just useful for debugging. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs instance Show Array where 	showsPrec d array = showParen (d > 10) $ 		s "Array " . showSig . s " [" . s valueString . s "]" where@@ -733,12 +859,12 @@ will be checked for validity. Every item in the array will be checked against the item type, to ensure the array is homogenous. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs arrayFromItems :: Type -> [Variant] -> Maybe Array-arrayFromItems DBusByte vs = fmap (ByteArray . ByteString.pack) (mapM fromVariant vs)+arrayFromItems DBusByte vs = fmap (ByteArray . BL.pack) (mapM fromVariant vs)  arrayFromItems t vs = do-	mkSignature (typeCode t)+	maybeValidType t 	if all (\x -> variantType x == t) vs 		then Just $ VariantArray t vs 		else Nothing@@ -747,7 +873,7 @@ Additionally, for ease of use, an {\tt Array} can be converted directly to/from lists of {\tt Variable} values. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs toArray :: Variable a => Type -> [a] -> Maybe Array toArray t = arrayFromItems t . map toVariant @@ -764,12 +890,12 @@ To provide a more efficient interface for byte array literals, these functions bypass the conversions in {\tt toArray} and {\tt fromArray} -:f DBus/Types.cpphs-arrayToBytes :: Array -> Maybe ByteString+:f DBus/Types/Internal.cpphs+arrayToBytes :: Array -> Maybe BL.ByteString arrayToBytes (ByteArray x) = Just x arrayToBytes _             = Nothing -arrayFromBytes :: ByteString -> Array+arrayFromBytes :: BL.ByteString -> Array arrayFromBytes = ByteArray : @@ -781,22 +907,16 @@ And to simplify inclusion of {\tt ByteString}s in message, instances of {\tt Variable} exist for both strict and lazy {\tt ByteString}. -:f DBus/Types.cpphs-instance Variable ByteString where+:f DBus/Types/Internal.cpphs+instance Variable BL.ByteString where 	toVariant = toVariant . arrayFromBytes 	fromVariant x = fromVariant x >>= arrayToBytes : -:d type imports-import qualified Data.ByteString as StrictByteString-:--:f DBus/Types.cpphs-instance Variable StrictByteString.ByteString where-	toVariant x = toVariant . arrayFromBytes $ ByteString.fromChunks [x]-	fromVariant x = do-		chunks <- ByteString.toChunks `fmap` fromVariant x-		return $ StrictByteString.concat chunks+:f DBus/Types/Internal.cpphs+instance Variable B.ByteString where+	toVariant x = toVariant . arrayFromBytes $ BL.fromChunks [x]+	fromVariant = fmap (B.concat . BL.toChunks) . fromVariant :  When generating random arrays for testing, only atomic values are used.@@ -832,8 +952,9 @@ key type must be atomic. Values may be of any valid D-Bus type. Like {\tt Array}, {\tt Dictionary} stores its contained types. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs INSTANCE_VARIABLE(Dictionary)+ data Dictionary = Dictionary 	{ dictionaryKeyType   :: Type 	, dictionaryValueType :: Type@@ -857,7 +978,7 @@ import Data.List (intercalate) : -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs instance Show Dictionary where 	showsPrec d (Dictionary kt vt pairs) = showParen (d > 10) $ 		s "Dictionary " . showSig . s " {" . s valueString . s "}" where@@ -874,12 +995,12 @@ import Control.Monad (unless) : -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs dictionaryFromItems :: Type -> Type -> [(Variant, Variant)] -> Maybe Dictionary dictionaryFromItems kt vt pairs = do 	unless (isAtomicType kt) Nothing-	mkSignature (typeCode kt)-	mkSignature (typeCode vt)+	maybeValidType kt+	maybeValidType vt 	 	let sameType (k, v) = variantType k == kt && 	                      variantType v == vt@@ -897,7 +1018,7 @@ import qualified Data.Map as Map : -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs toDictionary :: (Variable a, Variable b) => Type -> Type -> Map.Map a b              -> Maybe Dictionary toDictionary kt vt = dictionaryFromItems kt vt . pairs where@@ -908,7 +1029,7 @@ import Control.Monad (forM) : -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs fromDictionary :: (Variable a, Ord a, Variable b) => Dictionary                -> Maybe (Map.Map a b) fromDictionary (Dictionary _ _ vs) = do@@ -968,7 +1089,7 @@ (un)marshaling -- dictionaries can be thought of as arrays of two-element structures, much as a {\tt Map} is a list of pairs. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs dictionaryToArray :: Dictionary -> Array dictionaryToArray (Dictionary kt vt items) = array where 	Just array = toArray itemType structs@@ -976,7 +1097,7 @@ 	structs = [Structure [k, v] | (k, v) <- items] : -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs arrayToDictionary :: Array -> Maybe Dictionary arrayToDictionary array = do 	let toPair x = do@@ -1001,8 +1122,9 @@ A heterogeneous, fixed-length container; equivalent in purpose to a Haskell tuple. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs INSTANCE_VARIABLE(Structure)+ data Structure = Structure [Variant] 	deriving (Show, Eq) :@@ -1027,7 +1149,7 @@ declaration and some helper functions), I define a macro to automate the definitions. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs #define NAME_TYPE(TYPE, NAME) \ 	newtype TYPE = TYPE {str/**/TYPE :: Text} \ 		deriving (Eq, Ord); \@@ -1064,7 +1186,7 @@ element consists of characters from the set {\tt [a-zA-Z0-9\_-]}, and must not start with a digit. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs NAME_TYPE(BusName, "bus name")  mkBusName :: Text -> Maybe BusName@@ -1119,7 +1241,7 @@ element constists of characters from the set {\tt [a-zA-Z0-9\_]}, may not start with a digit, and must have at least one character. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs NAME_TYPE(InterfaceName, "interface name")  mkInterfaceName :: Text -> Maybe InterfaceName@@ -1165,7 +1287,7 @@ Error names have the same format as interface names, so the parser logic can just be re-purposed. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs NAME_TYPE(ErrorName, "error name")  mkErrorName :: Text -> Maybe ErrorName@@ -1197,7 +1319,7 @@ Member names must contain only characters from the set {\tt [a-zA-Z0-9\_]}, may not begin with a digit, and must be at least one character long. -:f DBus/Types.cpphs+:f DBus/Types/Internal.cpphs NAME_TYPE(MemberName, "member name")  mkMemberName :: Text -> Maybe MemberName
src/util.anansi view
@@ -57,7 +57,7 @@ :f DBus/Util.hs |copyright| module DBus.Util where-import Text.Parsec (Parsec, parse)+import Text.ParserCombinators.Parsec (Parser, parse) import Data.Char (digitToInt) import Data.List (isPrefixOf) @@ -65,7 +65,7 @@ checkLength length' s | length s <= length' = Just s checkLength _ _ = Nothing -parseMaybe :: Parsec String () a -> String -> Maybe a+parseMaybe :: Parser a -> String -> Maybe a parseMaybe p = either (const Nothing) Just . parse p ""  mkUnsafe :: Show a => String -> (a -> Maybe b) -> a -> b@@ -115,20 +115,30 @@  :f DBus/Util/MonadError.hs |copyright|-{-# LANGUAGE TypeFamilies #-} module DBus.Util.MonadError-	( ErrorT (..)-	, throwError+	( ErrorM (..)+	, ErrorT (..)+	, throwErrorM+	, throwErrorT 	) where-import Control.Monad.Trans.Class-import Control.Monad.State-import Control.Monad.Error.Class+newtype ErrorM e a = ErrorM { runErrorM :: Either e a } -newtype ErrorT e m a = ErrorT { runErrorT :: m (Either e a) }+instance Functor (ErrorM e) where+	fmap f m = ErrorM $ case runErrorM m of+		Left err -> Left err+		Right x -> Right $ f x -instance Functor m => Functor (ErrorT e m) where-	fmap f = ErrorT . fmap (fmap f) . runErrorT+instance Monad (ErrorM e) where+	return = ErrorM . Right+	(>>=) m k = case runErrorM m of+		Left err -> ErrorM $ Left err+		Right x -> k x +throwErrorM :: e -> ErrorM e a+throwErrorM = ErrorM . Left++newtype ErrorT e m a = ErrorT { runErrorT :: m (Either e a) }+ instance Monad m => Monad (ErrorT e m) where 	return = ErrorT . return . Right 	(>>=) m k = ErrorT $ do@@ -137,22 +147,8 @@ 			Left l -> return $ Left l 			Right r -> runErrorT $ k r -instance MonadTrans (ErrorT e) where-	lift = ErrorT . liftM Right--instance Monad m => MonadError (ErrorT e m) where-	type ErrorType (ErrorT e m) = e-	throwError = ErrorT . return . Left-	catchError m h = ErrorT $ do-		x <- runErrorT m-		case x of-			Left l -> runErrorT $ h l-			Right r -> return $ Right r--instance MonadState m => MonadState (ErrorT e m) where-	type StateType (ErrorT e m) = StateType m-	get = lift get-	put = lift . put+throwErrorT :: Monad m => e -> ErrorT e m a+throwErrorT = ErrorT . return . Left :  \subsection{Extra test support}
src/wire.anansi view
@@ -95,22 +95,50 @@ import Control.Monad (when) import Data.Maybe (fromJust) import Data.Word (Word8, Word32, Word64)-import Data.Int (Int16, Int32, Int64)+import Data.Int (Int64)  import qualified DBus.Types as T+import qualified DBus.Types.Internal as T :  :d marshal imports-import qualified Control.Monad.State as State-import qualified DBus.Util.MonadError as E+import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as L import qualified Data.Binary.Builder as B :  :f DBus/Wire/Marshal.hs-data MarshalState = MarshalState Endianness B.Builder !Word64-type MarshalM = E.ErrorT MarshalError (State.State MarshalState)+data MarshalState = MarshalState {-# UNPACK #-} !B.Builder {-# UNPACK #-} !Word64++data MarshalR a = MarshalRL MarshalError | MarshalRR a {-# UNPACK #-} !MarshalState+ type Marshal = MarshalM ()+newtype MarshalM a = MarshalM { unMarshalM :: Endianness -> MarshalState -> MarshalR a }++instance Monad MarshalM where+	{-# INLINE return #-}+	return a = MarshalM $ \_ s -> MarshalRR a s+	+	{-# INLINE (>>=) #-}+	m >>= k = MarshalM $ \e s -> case unMarshalM m e s of+		MarshalRL err -> MarshalRL err+		MarshalRR a s' -> unMarshalM (k a) e s'+	+	{-# INLINE (>>) #-}+	m >> k = MarshalM $ \e s -> case unMarshalM m e s of+		MarshalRL err -> MarshalRL err+		MarshalRR _ s' -> unMarshalM k e s'++throwError :: MarshalError -> MarshalM a+throwError err = MarshalM $ \_ _ -> MarshalRL err++{-# INLINE getState #-}+getState :: MarshalM MarshalState+getState = MarshalM $ \_ s -> MarshalRR s s++{-# INLINE putState #-}+putState :: MarshalState -> MarshalM ()+putState s = MarshalM $ \_ _ -> MarshalRR () s :  Clients can perform marshaling via {\tt marshal} and {\tt runMarshal},@@ -118,38 +146,41 @@  :f DBus/Wire/Marshal.hs runMarshal :: Marshal -> Endianness -> Either MarshalError L.ByteString-runMarshal m e = case State.runState (E.runErrorT m) initialState of-	(Right _, MarshalState _ builder _) -> Right (B.toLazyByteString builder)-	(Left  x, _) -> Left x-	where initialState = MarshalState e B.empty 0+runMarshal m e = case unMarshalM m e (MarshalState B.empty 0) of+	MarshalRL err -> Left err+	MarshalRR _ (MarshalState builder _) -> Right $ B.toLazyByteString builder :  :f DBus/Wire/Marshal.hs marshal :: T.Variant -> Marshal-marshal v = marshalType (T.variantType v) where-	x :: T.Variable a => a-	x = fromJust . T.fromVariant $ v-	marshalType :: T.Type -> Marshal+marshal v = case v of 	|marshalers| :  TODO: describe these functions  :f DBus/Wire/Marshal.hs-append :: L.ByteString -> Marshal-append bytes = do-	(MarshalState e builder count) <- State.get-	let builder' = B.append builder $ B.fromLazyByteString bytes-	    count' = count + fromIntegral (L.length bytes)-	State.put $ MarshalState e builder' count'+appendS :: BS.ByteString -> Marshal+appendS bytes = MarshalM $ \_ (MarshalState builder count) -> let+	builder' = B.append builder $ B.fromByteString bytes+	count' = count + fromIntegral (BS.length bytes)+	in MarshalRR () (MarshalState builder' count') :  :f DBus/Wire/Marshal.hs+appendL :: L.ByteString -> Marshal+appendL bytes = MarshalM $ \_ (MarshalState builder count) -> let+	builder' = B.append builder $ B.fromLazyByteString bytes+	count' = count + fromIntegral (L.length bytes)+	in MarshalRR () (MarshalState builder' count')+:++:f DBus/Wire/Marshal.hs pad :: Word8 -> Marshal-pad count = do-	(MarshalState _ _ existing) <- State.get-	let padding' = fromIntegral $ padding existing count-	append $ L.replicate padding' 0+pad count = MarshalM $ \e s@(MarshalState _ existing) -> let+	padding' = fromIntegral $ padding existing count+	bytes = BS.replicate padding' 0+	in unMarshalM (appendS bytes) e s :  Most numeric values already have marshalers implemented in the@@ -159,12 +190,12 @@ marshalBuilder :: Word8 -> (a -> B.Builder) -> (a -> B.Builder) -> a -> Marshal marshalBuilder size be le x = do 	pad size-	(MarshalState e builder count) <- State.get-	let builder' = B.append builder $ case e of-		BigEndian -> be x-		LittleEndian -> le x-	let count' = count + fromIntegral size-	State.put $ MarshalState e builder' count'+	MarshalM $ \e (MarshalState builder count) -> let+		builder' = B.append builder $ case e of+			BigEndian -> be x+			LittleEndian -> le x+		size' = fromIntegral size+		in MarshalRR () (MarshalState builder' (count + size')) :  \subsubsection{Errors}@@ -218,31 +249,63 @@ module DBus.Wire.Unmarshal where |text imports| |unmarshal imports|-import Control.Monad (when, unless, liftM)+import Control.Monad (when, unless) import Data.Maybe (fromJust, listToMaybe, fromMaybe) import Data.Word (Word8, Word32, Word64) import Data.Int (Int16, Int32, Int64)  import DBus.Wire.Internal-import qualified DBus.Types as T+import qualified DBus.Types.Internal as T :  :d unmarshal imports-import qualified Control.Monad.State as State-import Control.Monad.Trans.Class (lift)+import Control.Monad (liftM) import qualified DBus.Util.MonadError as E-import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL : +A specialised, state-like monad is used for performance reasons. This is+equivalent to {\tt State.StateT UnmarshalState (E.ErrorM UnmarshalError)}.+ :f DBus/Wire/Unmarshal.hs-data UnmarshalState = UnmarshalState Endianness L.ByteString !Word64-type Unmarshal = E.ErrorT UnmarshalError (State.State UnmarshalState)+data UnmarshalState = UnmarshalState BL.ByteString {-# UNPACK #-} !Word64++data UnmarshalR a = UnmarshalRL UnmarshalError | UnmarshalRR a {-# UNPACK #-} !UnmarshalState++newtype Unmarshal a = Unmarshal { unUnmarshal :: Endianness -> UnmarshalState -> UnmarshalR a }++instance Monad Unmarshal where+	{-# INLINE return #-}+	return a = Unmarshal $ \_ s -> UnmarshalRR a s+	+	{-# INLINE (>>=) #-}+	m >>= k = Unmarshal $ \e s -> case unUnmarshal m e s of+		UnmarshalRL err -> UnmarshalRL err+		UnmarshalRR a s' -> unUnmarshal (k a) e s'+	+	{-# INLINE (>>) #-}+	m >> k = Unmarshal $ \e s -> case unUnmarshal m e s of+		UnmarshalRL err -> UnmarshalRL err+		UnmarshalRR _ s' -> unUnmarshal k e s'++throwError :: UnmarshalError -> Unmarshal a+throwError err = Unmarshal $ \_ _ -> UnmarshalRL err++{-# INLINE getState #-}+getState :: Unmarshal UnmarshalState+getState = Unmarshal $ \_ s -> UnmarshalRR s s++{-# INLINE putState #-}+putState :: UnmarshalState -> Unmarshal ()+putState s = Unmarshal $ \_ _ -> UnmarshalRR () s :  :f DBus/Wire/Unmarshal.hs-runUnmarshal :: Unmarshal a -> Endianness -> L.ByteString -> Either UnmarshalError a-runUnmarshal m e bytes = State.evalState (E.runErrorT m) state where-	state = UnmarshalState e bytes 0+runUnmarshal :: Unmarshal a -> Endianness -> BL.ByteString -> Either UnmarshalError a+runUnmarshal m e bytes = case unUnmarshal m e (UnmarshalState bytes 0) of+	UnmarshalRL err -> Left err+	UnmarshalRR a _ -> Right a :  :f DBus/Wire/Unmarshal.hs@@ -256,40 +319,39 @@ TODO: describe these functions  :f DBus/Wire/Unmarshal.hs-consume :: Word64 -> Unmarshal L.ByteString-consume count = do-	(UnmarshalState e bytes offset) <- State.get-	let (x, bytes') = L.splitAt (fromIntegral count) bytes-	unless (L.length x == fromIntegral count) $-		E.throwError $ UnexpectedEOF offset-	-	State.put $ UnmarshalState e bytes' (offset + count)-	return x+{-# INLINE consume #-}+consume :: Word64 -> Unmarshal BL.ByteString+consume count = Unmarshal $ \_ (UnmarshalState bytes offset) -> let+	count' = fromIntegral count+	(x, bytes') = BL.splitAt count' bytes+	in if BL.length x == count'+		then UnmarshalRR x (UnmarshalState bytes' (offset + count))+		else UnmarshalRL $ UnexpectedEOF offset :  :f DBus/Wire/Unmarshal.hs skipPadding :: Word8 -> Unmarshal () skipPadding count = do-	(UnmarshalState _ _ offset) <- State.get+	(UnmarshalState _ offset) <- getState 	bytes <- consume $ padding offset count-	unless (L.all (== 0) bytes) $-		E.throwError $ InvalidPadding offset+	unless (BL.all (== 0) bytes) $+		throwError $ InvalidPadding offset :  :f DBus/Wire/Unmarshal.hs skipTerminator :: Unmarshal () skipTerminator = do-	(UnmarshalState _ _ offset) <- State.get+	(UnmarshalState _ offset) <- getState 	bytes <- consume 1-	unless (L.all (== 0) bytes) $-		E.throwError $ MissingTerminator offset+	unless (BL.all (== 0) bytes) $+		throwError $ MissingTerminator offset :  :f DBus/Wire/Unmarshal.hs fromMaybeU :: Show a => Text -> (a -> Maybe b) -> a -> Unmarshal b fromMaybeU label f x = case f x of 	Just x' -> return x'-	Nothing -> E.throwError . Invalid label . TL.pack . show $ x+	Nothing -> throwError . Invalid label . TL.pack . show $ x  fromMaybeU' :: (Show a, T.Variable b) => Text -> (a -> Maybe b) -> a            -> Unmarshal T.Variant@@ -306,16 +368,17 @@ unmarshalGet :: Word8 -> G.Get a -> G.Get a -> Unmarshal a unmarshalGet count be le = do 	skipPadding count-	(UnmarshalState e _ _) <- State.get 	bs <- consume . fromIntegral $ count-	let get' = case e of-		BigEndian -> be-		LittleEndian -> le-	return $ G.runGet get' bs+	+	Unmarshal $ \e s -> let+		get = case e of+			BigEndian -> be+			LittleEndian -> le+		in UnmarshalRR (G.runGet get bs) s  unmarshalGet' :: T.Variable a => Word8 -> G.Get a -> G.Get a               -> Unmarshal T.Variant-unmarshalGet' count be le = T.toVariant `fmap` unmarshalGet count be le+unmarshalGet' count be le = T.toVariant `liftM` unmarshalGet count be le :  :f DBus/Wire/Unmarshal.hs@@ -395,31 +458,39 @@ alignment T.DBusDouble  = 8 : -Because {\tt Word32}s are often used for other types, there's-separate functions for handling them.+Because some integral types are often used as components of other values,+there's separate functions for handling them.  :f DBus/Wire/Marshal.hs marshalWord32 :: Word32 -> Marshal marshalWord32 = marshalBuilder 4 B.putWord32be B.putWord32le : +:f DBus/Wire/Marshal.hs+{-# INLINE marshalWord8 #-}+marshalWord8 :: Word8 -> Marshal+marshalWord8 x = MarshalM $ \_ (MarshalState builder count) -> let+	builder' = B.append builder $ B.singleton x+	in MarshalRR () (MarshalState builder' (count + 1))+:+ :f DBus/Wire/Unmarshal.hs unmarshalWord32 :: Unmarshal Word32 unmarshalWord32 = unmarshalGet 4 G.getWord32be G.getWord32le :  :d marshalers-marshalType T.DBusByte   = append $ L.singleton x-marshalType T.DBusWord16 = marshalBuilder 2 B.putWord16be B.putWord16le x-marshalType T.DBusWord32 = marshalBuilder 4 B.putWord32be B.putWord32le x-marshalType T.DBusWord64 = marshalBuilder 8 B.putWord64be B.putWord64le x-marshalType T.DBusInt16  = marshalBuilder 2 B.putWord16be B.putWord16le $ fromIntegral (x :: Int16)-marshalType T.DBusInt32  = marshalBuilder 4 B.putWord32be B.putWord32le $ fromIntegral (x :: Int32)-marshalType T.DBusInt64  = marshalBuilder 8 B.putWord64be B.putWord64le $ fromIntegral (x :: Int64)+T.VarBoxWord8  x -> marshalWord8 x+T.VarBoxWord16 x -> marshalBuilder 2 B.putWord16be B.putWord16le x+T.VarBoxWord32 x -> marshalWord32 x+T.VarBoxWord64 x -> marshalBuilder 8 B.putWord64be B.putWord64le x+T.VarBoxInt16  x -> marshalBuilder 2 B.putWord16be B.putWord16le $ fromIntegral x+T.VarBoxInt32  x -> marshalBuilder 4 B.putWord32be B.putWord32le $ fromIntegral x+T.VarBoxInt64  x -> marshalBuilder 8 B.putWord64be B.putWord64le $ fromIntegral x :  :d unmarshalers-unmarshalType T.DBusByte = fmap (T.toVariant . L.head) $ consume 1+unmarshalType T.DBusByte = liftM (T.toVariant . BL.head) $ consume 1 unmarshalType T.DBusWord16 = unmarshalGet' 2 G.getWord16be G.getWord16le unmarshalType T.DBusWord32 = unmarshalGet' 4 G.getWord32be G.getWord32le unmarshalType T.DBusWord64 = unmarshalGet' 8 G.getWord64be G.getWord64le@@ -449,14 +520,19 @@ :  :d marshalers-marshalType T.DBusDouble = do+T.VarBoxDouble x -> marshalDouble x+:++:f DBus/Wire/Marshal.hs+marshalDouble :: Double -> Marshal+marshalDouble x = do 	pad 8-	(MarshalState e _ _) <- State.get-	let put = case e of-		BigEndian -> IEEE.putFloat64be-		LittleEndian -> IEEE.putFloat64le-	let bytes = runPut $ put x-	append bytes+	MarshalM $ \e s -> let+		put = case e of+			BigEndian -> IEEE.putFloat64be+			LittleEndian -> IEEE.putFloat64le+		bytes = runPut $ put x+		in unMarshalM (appendL bytes) e s :  :d unmarshalers@@ -473,7 +549,7 @@ :  :d marshalers-marshalType T.DBusBoolean = marshalWord32 $ if x then 1 else 0+T.VarBoxBool x -> marshalWord32 $ if x then 1 else 0 :  :d unmarshalers@@ -528,12 +604,12 @@ marshalText x = do 	bytes <- case maybeEncodeUtf8 x of 		Just x' -> return x'-		Nothing -> E.throwError $ InvalidText x+		Nothing -> throwError $ InvalidText x 	when (L.any (== 0) bytes) $-		E.throwError $ InvalidText x+		throwError $ InvalidText x 	marshalWord32 . fromIntegral . L.length $ bytes-	append bytes-	append (L.singleton 0)+	appendL bytes+	marshalWord8 0 :  :d unmarshal imports@@ -555,12 +631,12 @@ :  :d marshalers-marshalType T.DBusString = marshalText x-marshalType T.DBusObjectPath = marshalText . T.strObjectPath $ x+T.VarBoxString x -> marshalText x+T.VarBoxObjectPath x -> marshalText . T.strObjectPath $ x :  :d unmarshalers-unmarshalType T.DBusString = fmap T.toVariant unmarshalText+unmarshalType T.DBusString = liftM T.toVariant unmarshalText  unmarshalType T.DBusObjectPath = unmarshalText >>= 	fromMaybeU' "object path" T.mkObjectPath@@ -571,28 +647,24 @@ Signatures are similar to strings, except their length is limited to 255 characters and is therefore stored as a single byte. -:d marshal imports-import Data.Text.Lazy.Encoding (encodeUtf8)-:- :f DBus/Wire/Marshal.hs marshalSignature :: T.Signature -> Marshal marshalSignature x = do-	let bytes = encodeUtf8 . T.strSignature $ x-	let size = fromIntegral . L.length $ bytes-	append (L.singleton size)-	append bytes-	append (L.singleton 0)+	let bytes = T.bytesSignature x+	let size = fromIntegral . BS.length $ bytes+	marshalWord8 size+	appendS bytes+	marshalWord8 0 :  :f DBus/Wire/Unmarshal.hs unmarshalSignature :: Unmarshal T.Signature unmarshalSignature = do-	byteCount <- L.head `fmap` consume 1-	bytes <- consume $ fromIntegral byteCount-	sigText <- fromMaybeU "text" maybeDecodeUtf8 bytes+	byteCount <- BL.head `liftM` consume 1+	lazy <- consume $ fromIntegral byteCount 	skipTerminator-	fromMaybeU "signature" T.mkSignature sigText+	let bytes = B.concat $ BL.toChunks lazy+	fromMaybeU "signature" T.mkBytesSignature bytes :  :d alignments@@ -600,11 +672,11 @@ :  :d marshalers-marshalType T.DBusSignature = marshalSignature x+T.VarBoxSignature x -> marshalSignature x :  :d unmarshalers-unmarshalType T.DBusSignature = fmap T.toVariant unmarshalSignature+unmarshalType T.DBusSignature = liftM T.toVariant unmarshalSignature :  \subsection{Containers}@@ -616,11 +688,11 @@ :  :d marshalers-marshalType (T.DBusArray _) = marshalArray x+T.VarBoxArray x -> marshalArray x :  :d unmarshalers-unmarshalType (T.DBusArray t) = T.toVariant `fmap` unmarshalArray t+unmarshalType (T.DBusArray t) = T.toVariant `liftM` unmarshalArray t :  Marshaling arrays is complicated, because the array body must be marshaled@@ -637,10 +709,10 @@ 	(arrayPadding, arrayBytes) <- getArrayBytes (T.arrayType x) x 	let arrayLen = L.length arrayBytes 	when (arrayLen > fromIntegral C.arrayMaximumLength)-		(E.throwError $ ArrayTooLong $ fromIntegral arrayLen)+		(throwError $ ArrayTooLong $ fromIntegral arrayLen) 	marshalWord32 $ fromIntegral arrayLen-	append $ L.replicate arrayPadding 0-	append arrayBytes+	appendL $ L.replicate arrayPadding 0+	appendL arrayBytes :  :f DBus/Wire/Marshal.hs@@ -652,17 +724,17 @@ :f DBus/Wire/Marshal.hs getArrayBytes itemType x = do 	let vs = T.arrayItems x-	s <- State.get-	(MarshalState _ _ afterLength) <- marshalWord32 0 >> State.get-	(MarshalState e _ afterPadding) <- pad (alignment itemType) >> State.get+	s <- getState+	(MarshalState _ afterLength) <- marshalWord32 0 >> getState+	(MarshalState _ afterPadding) <- pad (alignment itemType) >> getState 	-	State.put $ MarshalState e B.empty afterPadding-	(MarshalState _ itemBuilder _) <- mapM_ marshal vs >> State.get+	putState $ MarshalState B.empty afterPadding+	(MarshalState itemBuilder _) <- mapM_ marshal vs >> getState 	 	let itemBytes = B.toLazyByteString itemBuilder 	    paddingSize = fromIntegral $ afterPadding - afterLength 	-	State.put s+	putState s 	return (paddingSize, itemBytes) : @@ -672,22 +744,22 @@ unmarshalArray :: T.Type -> Unmarshal T.Array unmarshalArray T.DBusByte = do 	byteCount <- unmarshalWord32-	T.arrayFromBytes `fmap` consume (fromIntegral byteCount)+	T.arrayFromBytes `liftM` consume (fromIntegral byteCount) :  :f DBus/Wire/Unmarshal.hs unmarshalArray itemType = do 	let getOffset = do-		(UnmarshalState _ _ o) <- State.get+		(UnmarshalState _ o) <- getState 		return o 	byteCount <- unmarshalWord32 	skipPadding (alignment itemType) 	start <- getOffset 	let end = start + fromIntegral byteCount-	vs <- untilM (fmap (>= end) getOffset) (unmarshalType itemType)+	vs <- untilM (liftM (>= end) getOffset) (unmarshalType itemType) 	end' <- getOffset 	when (end' > end) $-		E.throwError ArraySizeMismatch+		throwError ArraySizeMismatch 	fromMaybeU "array" (T.arrayFromItems itemType) vs : @@ -698,7 +770,7 @@ :  :d marshalers-marshalType (T.DBusDictionary _ _) = marshalArray (T.dictionaryToArray x)+T.VarBoxDictionary x -> marshalArray (T.dictionaryToArray x) :  :d unmarshalers@@ -715,8 +787,7 @@ :  :d marshalers-marshalType (T.DBusStructure _) = do-	let T.Structure vs = x+T.VarBoxStructure (T.Structure vs) -> do 	pad 8 	mapM_ marshal vs :@@ -724,7 +795,7 @@ :d unmarshalers unmarshalType (T.DBusStructure ts) = do 	skipPadding 8-	fmap (T.toVariant . T.Structure) $ mapM unmarshalType ts+	liftM (T.toVariant . T.Structure) $ mapM unmarshalType ts :  \subsubsection{Variants}@@ -734,11 +805,11 @@ :  :d marshalers-marshalType T.DBusVariant = do-	let rawSig = T.typeCode . T.variantType $ x-	sig <- case T.mkSignature rawSig of+T.VarBoxVariant x -> do+	let textSig = T.typeCode . T.variantType $ x+	sig <- case T.variantSignature x of 		Just x' -> return x'-		Nothing -> E.throwError $ InvalidVariantSignature rawSig+		Nothing -> throwError $ InvalidVariantSignature textSig 	marshalSignature sig 	marshal x :@@ -750,7 +821,7 @@ 		_   -> Nothing 	 	t <- fromMaybeU "variant signature" getType =<< unmarshalSignature-	T.toVariant `fmap` unmarshalType t+	T.toVariant `liftM` unmarshalType t :  \subsection{Messages}@@ -812,8 +883,8 @@ :  :f DBus/Wire/Unmarshal.hs-decodeField :: Monad m => T.Structure-            -> E.ErrorT UnmarshalError m [M.HeaderField]+decodeField :: T.Structure+            -> E.ErrorM UnmarshalError [M.HeaderField] decodeField struct = case unpackField struct of 	(1, x) -> decodeField' x M.Path "path" 	(2, x) -> decodeField' x M.Interface "interface"@@ -825,11 +896,11 @@ 	(8, x) -> decodeField' x M.Signature "signature" 	_      -> return [] -decodeField' :: (Monad m, T.Variable a) => T.Variant -> (a -> b) -> Text-             -> E.ErrorT UnmarshalError m [b]+decodeField' :: T.Variable a => T.Variant -> (a -> b) -> Text+             -> E.ErrorM UnmarshalError [b] decodeField' x f label = case T.fromVariant x of 	Just x' -> return [f x']-	Nothing -> E.throwError $ InvalidHeaderField label x+	Nothing -> E.throwErrorM $ InvalidHeaderField label x :  :f DBus/Wire/Unmarshal.hs@@ -858,25 +929,26 @@ 	body = M.messageBody msg 	marshaler = do 		sig <- checkBodySig body-		empty <- State.get+		empty <- getState 		mapM_ marshal body-		(MarshalState _ bodyBytesB _) <- State.get-		State.put empty+		(MarshalState bodyBytesB _) <- getState+		putState empty 		marshalEndianness e 		let bodyBytes = B.toLazyByteString bodyBytesB 		marshalHeader msg serial sig 			$ fromIntegral . L.length $ bodyBytes 		pad 8-		append bodyBytes+		appendL bodyBytes 		checkMaximumSize :  :f DBus/Wire/Marshal.hs checkBodySig :: [T.Variant] -> MarshalM T.Signature checkBodySig vs = let-	sigStr = TL.concat . map (T.typeCode . T.variantType) $ vs-	invalid = E.throwError $ InvalidBodySignature sigStr-	in case T.mkSignature sigStr of+	textSig = TL.concat . map (T.typeCode . T.variantType) $ vs+	bytesSig = BS.concat . map (T.typeCodeB . T.variantType) $ vs+	invalid = throwError $ InvalidBodySignature textSig+	in case T.mkBytesSignature bytesSig of 		Just x -> return x 		Nothing -> invalid :@@ -886,13 +958,13 @@               -> Marshal marshalHeader msg serial bodySig bodyLength = do 	let fields = M.Signature bodySig : M.messageHeaderFields msg-	marshal . T.toVariant . M.messageTypeCode $ msg-	marshal . T.toVariant . encodeFlags . M.messageFlags $ msg-	marshal . T.toVariant $ C.protocolVersion+	marshalWord8 . M.messageTypeCode $ msg+	marshalWord8 . encodeFlags . M.messageFlags $ msg+	marshalWord8 C.protocolVersion 	marshalWord32 bodyLength-	marshal . T.toVariant $ serial+	marshalWord32 . M.serialValue $ serial 	let fieldType = T.DBusStructure [T.DBusByte, T.DBusVariant]-	marshal . T.toVariant . fromJust . T.toArray fieldType+	marshalArray . fromJust . T.toArray fieldType 	        $ map encodeField fields : @@ -904,9 +976,9 @@ :f DBus/Wire/Marshal.hs checkMaximumSize :: Marshal checkMaximumSize = do-	(MarshalState _ _ messageLength) <- State.get+	(MarshalState _ messageLength) <- getState 	when (messageLength > fromIntegral C.messageMaximumLength)-		(E.throwError $ MessageTooLong $ fromIntegral messageLength)+		(throwError $ MessageTooLong $ fromIntegral messageLength) :  \subsubsection{Unmarshaling}@@ -921,10 +993,10 @@  :f DBus/Wire/Unmarshal.hs |apidoc unmarshalMessage|-unmarshalMessage :: Monad m => (Word32 -> m L.ByteString)+unmarshalMessage :: Monad m => (Word32 -> m BL.ByteString)                  -> m (Either UnmarshalError M.ReceivedMessage) unmarshalMessage getBytes' = E.runErrorT $ do-	let getBytes = lift . getBytes'+	let getBytes = E.ErrorT . liftM Right . getBytes' 	 	|read fixed-length header| 	|read full header|@@ -945,18 +1017,18 @@ parsed.  :d read fixed-length header-let messageVersion = L.index fixedBytes 3+let messageVersion = BL.index fixedBytes 3 when (messageVersion /= C.protocolVersion) $-	E.throwError $ UnsupportedProtocolVersion messageVersion+	E.throwErrorT $ UnsupportedProtocolVersion messageVersion :  Next is the endianness, used for parsing pretty much every other field.  :d read fixed-length header-let eByte = L.index fixedBytes 0+let eByte = BL.index fixedBytes 0 endianness <- case decodeEndianness eByte of 	Just x' -> return x'-	Nothing -> E.throwError . Invalid "endianness" . TL.pack . show $ eByte+	Nothing -> E.throwErrorT . Invalid "endianness" . TL.pack . show $ eByte :  With the endianness out of the way, the rest of the fixed header@@ -965,7 +1037,7 @@ :d read fixed-length header let unmarshal' x bytes = case runUnmarshal (unmarshal x) endianness bytes of 	Right x' -> return x'-	Left  e  -> E.throwError e+	Left  e  -> E.throwErrorT e fixed <- unmarshal' fixedSig fixedBytes let typeCode = fromJust . T.fromVariant $ fixed !! 1 let flags = decodeFlags . fromJust . T.fromVariant $ fixed !! 2@@ -987,7 +1059,7 @@ :d read full header let headerSig  = "yyyyuua(yv)" fieldBytes <- getBytes fieldByteCount-let headerBytes = L.append fixedBytes fieldBytes+let headerBytes = BL.append fixedBytes fieldBytes header <- unmarshal' headerSig headerBytes : @@ -996,7 +1068,9 @@ :d read full header let fieldArray = fromJust . T.fromVariant $ header !! 6 let fieldStructures = fromJust . T.fromArray $ fieldArray-fields <- concat `liftM` mapM decodeField fieldStructures+fields <- case E.runErrorM $ concat `liftM` mapM decodeField fieldStructures of+	Left err -> E.throwErrorT err+	Right x -> return x :  The body is always aligned to 8 bytes, so pull out the padding before@@ -1027,29 +1101,17 @@ Even if the received message was structurally valid, building the {\tt ReceivedMessage} can still fail due to missing header fields. -Slightly ugly; to avoid orphan instances of either {\tt Text} or-{\tt Either}, a newtype is used to turn {\tt Either} into a monad.--:f DBus/Wire/Unmarshal.hs-newtype EitherM a b = EitherM (Either a b)--instance Monad (EitherM a) where-	return = EitherM . Right-	(EitherM (Left x)) >>= _ = EitherM (Left x)-	(EitherM (Right x)) >>= k = k x-:- :d build message-y <- case buildReceivedMessage typeCode fields of-	EitherM (Right x) -> return x-	EitherM (Left x) -> E.throwError $ MissingHeaderField x+y <- case E.runErrorM $ buildReceivedMessage typeCode fields of+	Right x -> return x+	Left err -> E.throwErrorT $ MissingHeaderField err return $ y serial flags body :  This really belongs in the Message section...  :f DBus/Wire/Unmarshal.hs-buildReceivedMessage :: Word8 -> [M.HeaderField] -> EitherM Text+buildReceivedMessage :: Word8 -> [M.HeaderField] -> E.ErrorM Text                         (M.Serial -> (Set.Set M.Flag) -> [T.Variant]                          -> M.ReceivedMessage) :@@ -1117,9 +1179,9 @@ :  :f DBus/Wire/Unmarshal.hs-require :: Text -> [a] -> EitherM Text a+require :: Text -> [a] -> E.ErrorM Text a require _     (x:_) = return x-require label _     = EitherM $ Left label+require label _     = E.throwErrorM label :  :f Tests.hs