packages feed

dbus 0.10.15 → 1.0.0

raw patch · 17 files changed

+1745/−503 lines, 17 filesdep +lensdep +splitdep +template-haskelldep ~basedep ~bytestringdep ~containersPVP ok

version bump matches the API change (PVP)

Dependencies added: lens, split, template-haskell, th-lift

Dependency ranges changed: base, bytestring, containers

API changes (from Hackage documentation)

- DBus.Client: instance DBus.Client.AutoMethod (GHC.Types.IO ())
- DBus.Client: method :: InterfaceName -> MemberName -> Signature -> Signature -> (MethodCall -> IO Reply) -> Method
- DBus.Client: replyError :: ErrorName -> [Variant] -> Reply
- DBus.Client: replyReturn :: [Variant] -> Reply
- DBus.Introspection: directionIn :: Direction
- DBus.Introspection: directionOut :: Direction
- DBus.Introspection: interface :: InterfaceName -> Interface
- DBus.Introspection: interfaceMethods :: Interface -> [Method]
- DBus.Introspection: interfaceName :: Interface -> InterfaceName
- DBus.Introspection: interfaceProperties :: Interface -> [Property]
- DBus.Introspection: interfaceSignals :: Interface -> [Signal]
- DBus.Introspection: method :: MemberName -> Method
- DBus.Introspection: methodArg :: String -> Type -> Direction -> MethodArg
- DBus.Introspection: methodArgDirection :: MethodArg -> Direction
- DBus.Introspection: methodArgName :: MethodArg -> String
- DBus.Introspection: methodArgType :: MethodArg -> Type
- DBus.Introspection: methodArgs :: Method -> [MethodArg]
- DBus.Introspection: methodName :: Method -> MemberName
- DBus.Introspection: object :: ObjectPath -> Object
- DBus.Introspection: objectChildren :: Object -> [Object]
- DBus.Introspection: objectInterfaces :: Object -> [Interface]
- DBus.Introspection: objectPath :: Object -> ObjectPath
- DBus.Introspection: property :: String -> Type -> Property
- DBus.Introspection: propertyName :: Property -> String
- DBus.Introspection: propertyRead :: Property -> Bool
- DBus.Introspection: propertyType :: Property -> Type
- DBus.Introspection: propertyWrite :: Property -> Bool
- DBus.Introspection: signal :: MemberName -> Signal
- DBus.Introspection: signalArg :: String -> Type -> SignalArg
- DBus.Introspection: signalArgName :: SignalArg -> String
- DBus.Introspection: signalArgType :: SignalArg -> Type
- DBus.Introspection: signalArgs :: Signal -> [SignalArg]
- DBus.Introspection: signalName :: Signal -> MemberName
+ DBus.Client: Client :: Socket -> IORef (Map Serial (MVar (Either MethodError MethodReturn))) -> IORef (Map Unique SignalHandler) -> IORef PathInfo -> ThreadId -> [Interface] -> Client
+ DBus.Client: Interface :: InterfaceName -> [Method] -> [Property] -> [Signal] -> Interface
+ DBus.Client: Method :: MemberName -> Signature -> Signature -> (MethodCall -> DBusR Reply) -> Method
+ DBus.Client: PathInfo :: [Interface] -> Map String PathInfo -> PathInfo
+ DBus.Client: Property :: MemberName -> Type -> Maybe (IO Variant) -> Maybe (Variant -> IO ()) -> Property
+ DBus.Client: ReplyError :: ErrorName -> [Variant] -> Reply
+ DBus.Client: ReplyReturn :: [Variant] -> Reply
+ DBus.Client: UnknownReleaseNameReply :: Word32 -> ReleaseNameReply
+ DBus.Client: UnknownRequestNameReply :: Word32 -> RequestNameReply
+ DBus.Client: [_pathChildren] :: PathInfo -> Map String PathInfo
+ DBus.Client: [_pathInterfaces] :: PathInfo -> [Interface]
+ DBus.Client: [clientInterfaces] :: Client -> [Interface]
+ DBus.Client: [clientObjects] :: Client -> IORef PathInfo
+ DBus.Client: [clientPendingCalls] :: Client -> IORef (Map Serial (MVar (Either MethodError MethodReturn)))
+ DBus.Client: [clientSignalHandlers] :: Client -> IORef (Map Unique SignalHandler)
+ DBus.Client: [clientSocket] :: Client -> Socket
+ DBus.Client: [clientThreadID] :: Client -> ThreadId
+ DBus.Client: [inSignature] :: Method -> Signature
+ DBus.Client: [interfaceMethods] :: Interface -> [Method]
+ DBus.Client: [interfaceName] :: Interface -> InterfaceName
+ DBus.Client: [interfaceProperties] :: Interface -> [Property]
+ DBus.Client: [interfaceSignals] :: Interface -> [Signal]
+ DBus.Client: [methodHandler] :: Method -> MethodCall -> DBusR Reply
+ DBus.Client: [methodName] :: Method -> MemberName
+ DBus.Client: [outSignature] :: Method -> Signature
+ DBus.Client: [propertyGetter] :: Property -> Maybe (IO Variant)
+ DBus.Client: [propertyName] :: Property -> MemberName
+ DBus.Client: [propertySetter] :: Property -> Maybe (Variant -> IO ())
+ DBus.Client: [propertyType] :: Property -> Type
+ DBus.Client: autoMethodWithMsg :: (AutoMethod fn) => MemberName -> (MethodCall -> fn) -> Method
+ DBus.Client: autoProperty :: forall v. (IsValue v) => MemberName -> Maybe (IO v) -> Maybe (v -> IO ()) -> Property
+ DBus.Client: buildIntrospectableInterface :: Client -> Interface
+ DBus.Client: buildIntrospectionInterface :: Interface -> Interface
+ DBus.Client: buildIntrospectionMethod :: Method -> Method
+ DBus.Client: buildIntrospectionObject :: [Interface] -> PathInfo -> [String] -> Object
+ DBus.Client: buildIntrospectionProperty :: Property -> Property
+ DBus.Client: buildPropertiesInterface :: Client -> Interface
+ DBus.Client: data ErrorName
+ DBus.Client: data Interface
+ DBus.Client: data PathInfo
+ DBus.Client: data Property
+ DBus.Client: dbusName :: BusName
+ DBus.Client: dbusPath :: ObjectPath
+ DBus.Client: defaultInterface :: Interface
+ DBus.Client: errorFailed :: ErrorName
+ DBus.Client: errorInvalidParameters :: ErrorName
+ DBus.Client: errorUnknownMethod :: ErrorName
+ DBus.Client: findPath :: ObjectPath -> PathInfo -> Maybe PathInfo
+ DBus.Client: getAllProperties :: Client -> MethodCall -> IO (Either MethodError MethodReturn)
+ DBus.Client: getAllPropertiesMap :: Client -> MethodCall -> IO (Either MethodError (Map String Variant))
+ DBus.Client: getProperty :: Client -> MethodCall -> IO (Either MethodError Variant)
+ DBus.Client: getPropertyValue :: IsValue a => Client -> MethodCall -> IO (Either MethodError a)
+ DBus.Client: instance DBus.Internal.Types.IsValue a => DBus.Client.AutoMethod (DBus.Client.DBusR (Data.Either.Either DBus.Client.Reply a))
+ DBus.Client: instance DBus.Internal.Types.IsValue a => DBus.Client.AutoMethod (DBus.Client.DBusR a)
+ DBus.Client: instance DBus.Internal.Types.IsValue a => DBus.Client.AutoMethod (GHC.Types.IO (Data.Either.Either DBus.Client.Reply a))
+ DBus.Client: instance GHC.Classes.Eq DBus.Client.PathInfo
+ DBus.Client: makeMethod :: MemberName -> Signature -> Signature -> (MethodCall -> DBusR Reply) -> Method
+ DBus.Client: pathChildren :: Lens' PathInfo (Map String PathInfo)
+ DBus.Client: pathInterfaces :: Lens' PathInfo [Interface]
+ DBus.Client: pathLens :: Applicative f => ObjectPath -> ((PathInfo -> f PathInfo) -> Maybe PathInfo -> f (Maybe PathInfo)) -> (PathInfo -> f PathInfo) -> PathInfo -> f PathInfo
+ DBus.Client: readOnlyProperty :: (IsValue v) => MemberName -> IO v -> Property
+ DBus.Client: setProperty :: Client -> MethodCall -> Variant -> IO (Either MethodError MethodReturn)
+ DBus.Client: setPropertyValue :: IsValue a => Client -> MethodCall -> a -> IO (Maybe MethodError)
+ DBus.Client: type DBusR a = ReaderT Client IO a
+ DBus.Generation: (?/?) :: ClientBusPathR (a -> IO b) -> a -> ClientBusPathR b
+ DBus.Generation: (??) :: Functor f => f (a -> b) -> a -> f b
+ DBus.Generation: GenerationParams :: Maybe BusName -> Maybe ObjectPath -> InterfaceName -> Bool -> (Type -> Type) -> GenerationParams
+ DBus.Generation: [genBusName] :: GenerationParams -> Maybe BusName
+ DBus.Generation: [genInterfaceName] :: GenerationParams -> InterfaceName
+ DBus.Generation: [genObjectPath] :: GenerationParams -> Maybe ObjectPath
+ DBus.Generation: [genTakeSignalErrorHandler] :: GenerationParams -> Bool
+ DBus.Generation: [getTHType] :: GenerationParams -> Type -> Type
+ DBus.Generation: addArgIf :: Bool -> a -> [a] -> [a]
+ DBus.Generation: addTypeArg :: Type -> Type -> Type
+ DBus.Generation: addTypeArgIf :: Bool -> Type -> Type -> Type
+ DBus.Generation: buildGeneratedSignature :: Bool -> Bool -> Type -> Type
+ DBus.Generation: buildGetTHType :: (Type -> Type) -> (Type -> Type -> Type) -> Type -> Type
+ DBus.Generation: clientArgumentUnpackingError :: [Variant] -> MethodError
+ DBus.Generation: clientArgumentUnpackingMessage :: String
+ DBus.Generation: data GenerationParams
+ DBus.Generation: dbusInvoke :: (Client -> BusName -> ObjectPath -> a) -> ClientBusPathR a
+ DBus.Generation: defaultGenerationParams :: GenerationParams
+ DBus.Generation: defaultGetDictType :: Type -> Type -> Type
+ DBus.Generation: defaultGetTHType :: Type -> Type
+ DBus.Generation: generateClient :: GenerationParams -> Interface -> Q [Dec]
+ DBus.Generation: generateClientMethod :: GenerationParams -> Method -> Q [Dec]
+ DBus.Generation: generateClientProperty :: GenerationParams -> Property -> Q [Dec]
+ DBus.Generation: generateFromFilePath :: GenerationParams -> FilePath -> Q [Dec]
+ DBus.Generation: generateSignal :: GenerationParams -> Signal -> Q [Dec]
+ DBus.Generation: generateSignals :: GenerationParams -> InterfaceName -> [Signal] -> Q [Dec]
+ DBus.Generation: generateSignalsFromInterface :: GenerationParams -> Interface -> Q [Dec]
+ DBus.Generation: getSetMethodCallParams :: Name -> Maybe Name -> Maybe Name -> ExpQ -> ExpQ
+ DBus.Generation: infixl 4 ?/?
+ DBus.Generation: makeFromVariantApp :: Name -> Exp
+ DBus.Generation: makeJustPattern :: Name -> Pat
+ DBus.Generation: makeToVariantApp :: Name -> Exp
+ DBus.Generation: mapOrHead :: (Num a, Eq a) => a -> (t -> b) -> [t] -> ([b] -> b) -> b
+ DBus.Generation: maybeName :: a -> Bool -> Maybe a
+ DBus.Generation: mkFunD :: Name -> [Name] -> Exp -> Dec
+ DBus.Generation: newNameDef :: String -> Q Name
+ DBus.Generation: runGetFirst :: [Maybe a] -> Maybe a
+ DBus.Generation: type ClientBusPathR a = ReaderT (Client, BusName, ObjectPath) IO a
+ DBus.Generation: unitIOType :: Type
+ DBus.Internal.Types: extractFromVariant :: IsValue a => Variant -> Maybe a
+ DBus.Internal.Types: fromElements :: [String] -> ObjectPath
+ DBus.Internal.Types: instance DBus.Internal.Types.IsValue ()
+ DBus.Internal.Types: instance DBus.Internal.Types.IsVariant ()
+ DBus.Internal.Types: instance Language.Haskell.TH.Syntax.Lift DBus.Internal.Types.BusName
+ DBus.Internal.Types: instance Language.Haskell.TH.Syntax.Lift DBus.Internal.Types.InterfaceName
+ DBus.Internal.Types: instance Language.Haskell.TH.Syntax.Lift DBus.Internal.Types.MemberName
+ DBus.Internal.Types: instance Language.Haskell.TH.Syntax.Lift DBus.Internal.Types.ObjectPath
+ DBus.Internal.Types: pathElements :: ObjectPath -> [String]
+ DBus.Introspection: In :: Direction
+ DBus.Introspection: Interface :: InterfaceName -> [Method] -> [Signal] -> [Property] -> Interface
+ DBus.Introspection: Method :: MemberName -> [MethodArg] -> Method
+ DBus.Introspection: MethodArg :: String -> Type -> Direction -> MethodArg
+ DBus.Introspection: Object :: ObjectPath -> [Interface] -> [Object] -> Object
+ DBus.Introspection: Out :: Direction
+ DBus.Introspection: Property :: String -> Type -> Bool -> Bool -> Property
+ DBus.Introspection: Signal :: MemberName -> [SignalArg] -> Signal
+ DBus.Introspection: SignalArg :: String -> Type -> SignalArg
+ DBus.Introspection: [interfaceMethods] :: Interface -> [Method]
+ DBus.Introspection: [interfaceName] :: Interface -> InterfaceName
+ DBus.Introspection: [interfaceProperties] :: Interface -> [Property]
+ DBus.Introspection: [interfaceSignals] :: Interface -> [Signal]
+ DBus.Introspection: [methodArgDirection] :: MethodArg -> Direction
+ DBus.Introspection: [methodArgName] :: MethodArg -> String
+ DBus.Introspection: [methodArgType] :: MethodArg -> Type
+ DBus.Introspection: [methodArgs] :: Method -> [MethodArg]
+ DBus.Introspection: [methodName] :: Method -> MemberName
+ DBus.Introspection: [objectChildren] :: Object -> [Object]
+ DBus.Introspection: [objectInterfaces] :: Object -> [Interface]
+ DBus.Introspection: [objectPath] :: Object -> ObjectPath
+ DBus.Introspection: [propertyName] :: Property -> String
+ DBus.Introspection: [propertyRead] :: Property -> Bool
+ DBus.Introspection: [propertyType] :: Property -> Type
+ DBus.Introspection: [propertyWrite] :: Property -> Bool
+ DBus.Introspection: [signalArgName] :: SignalArg -> String
+ DBus.Introspection: [signalArgType] :: SignalArg -> Type
+ DBus.Introspection: [signalArgs] :: Signal -> [SignalArg]
+ DBus.Introspection: [signalName] :: Signal -> MemberName
+ DBus.TH: addMatch :: Client -> String -> IO (Either MethodError ())
+ DBus.TH: addMatchMethodCall :: MethodCall
+ DBus.TH: emitNameAcquired :: Client -> String -> IO ()
+ DBus.TH: emitNameLost :: Client -> String -> IO ()
+ DBus.TH: emitNameOwnerChanged :: Client -> String -> String -> String -> IO ()
+ DBus.TH: emitPropertiesChanged :: Client -> ObjectPath -> String -> Map String Variant -> [String] -> IO ()
+ DBus.TH: getAdtAuditSessionData :: Client -> String -> IO (Either MethodError ByteString)
+ DBus.TH: getAdtAuditSessionDataMethodCall :: MethodCall
+ DBus.TH: getConnectionCredentials :: Client -> String -> IO (Either MethodError (Map String Variant))
+ DBus.TH: getConnectionCredentialsMethodCall :: MethodCall
+ DBus.TH: getConnectionSELinuxSecurityContext :: Client -> String -> IO (Either MethodError ByteString)
+ DBus.TH: getConnectionSELinuxSecurityContextMethodCall :: MethodCall
+ DBus.TH: getConnectionUnixProcessID :: Client -> String -> IO (Either MethodError Word32)
+ DBus.TH: getConnectionUnixProcessIDMethodCall :: MethodCall
+ DBus.TH: getConnectionUnixUser :: Client -> String -> IO (Either MethodError Word32)
+ DBus.TH: getConnectionUnixUserMethodCall :: MethodCall
+ DBus.TH: getFeatures :: Client -> IO (Either MethodError [String])
+ DBus.TH: getId :: Client -> IO (Either MethodError String)
+ DBus.TH: getIdMethodCall :: MethodCall
+ DBus.TH: getInterfaces :: Client -> IO (Either MethodError [String])
+ DBus.TH: getNameOwner :: Client -> String -> IO (Either MethodError String)
+ DBus.TH: getNameOwnerMethodCall :: MethodCall
+ DBus.TH: hello :: Client -> IO (Either MethodError String)
+ DBus.TH: helloMethodCall :: MethodCall
+ DBus.TH: listActivatableNames :: Client -> IO (Either MethodError [String])
+ DBus.TH: listActivatableNamesMethodCall :: MethodCall
+ DBus.TH: listNames :: Client -> IO (Either MethodError [String])
+ DBus.TH: listNamesMethodCall :: MethodCall
+ DBus.TH: listQueuedOwners :: Client -> String -> IO (Either MethodError [String])
+ DBus.TH: listQueuedOwnersMethodCall :: MethodCall
+ DBus.TH: makeHandlerForNameAcquired :: (Signal -> String -> IO ()) -> Signal -> IO ()
+ DBus.TH: makeHandlerForNameLost :: (Signal -> String -> IO ()) -> Signal -> IO ()
+ DBus.TH: makeHandlerForNameOwnerChanged :: (Signal -> String -> String -> String -> IO ()) -> Signal -> IO ()
+ DBus.TH: makeHandlerForPropertiesChanged :: (Signal -> String -> Map String Variant -> [String] -> IO ()) -> Signal -> IO ()
+ DBus.TH: methodCallForFeatures :: MethodCall
+ DBus.TH: methodCallForInterfaces :: MethodCall
+ DBus.TH: nameHasOwner :: Client -> String -> IO (Either MethodError Bool)
+ DBus.TH: nameHasOwnerMethodCall :: MethodCall
+ DBus.TH: registerForNameAcquired :: Client -> MatchRule -> (Signal -> String -> IO ()) -> IO SignalHandler
+ DBus.TH: registerForNameLost :: Client -> MatchRule -> (Signal -> String -> IO ()) -> IO SignalHandler
+ DBus.TH: registerForNameOwnerChanged :: Client -> MatchRule -> (Signal -> String -> String -> String -> IO ()) -> IO SignalHandler
+ DBus.TH: registerForPropertiesChanged :: Client -> MatchRule -> (Signal -> String -> Map String Variant -> [String] -> IO ()) -> IO SignalHandler
+ DBus.TH: releaseName :: Client -> String -> IO (Either MethodError Word32)
+ DBus.TH: releaseNameMethodCall :: MethodCall
+ DBus.TH: reloadConfig :: Client -> IO (Either MethodError ())
+ DBus.TH: reloadConfigMethodCall :: MethodCall
+ DBus.TH: removeMatch :: Client -> String -> IO (Either MethodError ())
+ DBus.TH: removeMatchMethodCall :: MethodCall
+ DBus.TH: requestName :: Client -> String -> Word32 -> IO (Either MethodError Word32)
+ DBus.TH: requestNameMethodCall :: MethodCall
+ DBus.TH: signalForNameAcquired :: Signal
+ DBus.TH: signalForNameLost :: Signal
+ DBus.TH: signalForNameOwnerChanged :: Signal
+ DBus.TH: signalForPropertiesChanged :: Signal
+ DBus.TH: startServiceByName :: Client -> String -> Word32 -> IO (Either MethodError Word32)
+ DBus.TH: startServiceByNameMethodCall :: MethodCall
+ DBus.TH: updateActivationEnvironment :: Client -> Map String String -> IO (Either MethodError ())
+ DBus.TH: updateActivationEnvironmentMethodCall :: MethodCall
- DBus.Client: autoMethod :: (AutoMethod fn) => InterfaceName -> MemberName -> fn -> Method
+ DBus.Client: autoMethod :: (AutoMethod fn) => MemberName -> fn -> Method
- DBus.Client: export :: Client -> ObjectPath -> [Method] -> IO ()
+ DBus.Client: export :: Client -> ObjectPath -> Interface -> IO ()

Files

dbus.cabal view
@@ -1,5 +1,5 @@ name: dbus-version: 0.10.15+version: 1.0.0 license: GPL-3 license-file: license.txt author: John Millikin <john@john-millikin.com>@@ -69,6 +69,7 @@   examples/export.hs   examples/introspect.hs   examples/list-names.hs+  idlxml/dbus.xml  source-repository head   type: git@@ -84,11 +85,16 @@     , cereal     , containers     , deepseq+    , filepath+    , lens     , libxml-sax     , network     , parsec     , random+    , split+    , template-haskell     , text+    , th-lift     , transformers     , unix     , vector@@ -97,13 +103,15 @@   exposed-modules:     DBus     DBus.Client-    DBus.Introspection-    DBus.Socket-    DBus.Transport+    DBus.Generation     DBus.Internal.Address     DBus.Internal.Message     DBus.Internal.Types     DBus.Internal.Wire+    DBus.Introspection+    DBus.Socket+    DBus.TH+    DBus.Transport  test-suite dbus_tests   type: exitcode-stdio-1.0@@ -141,6 +149,7 @@     DBusTests.BusName     DBusTests.Client     DBusTests.ErrorName+    DBusTests.Generation     DBusTests.Integration     DBusTests.InterfaceName     DBusTests.Introspection@@ -150,6 +159,7 @@     DBusTests.Serialization     DBusTests.Signature     DBusTests.Socket+    DBusTests.TH     DBusTests.Transport     DBusTests.Util     DBusTests.Variant
examples/export.hs view
@@ -45,14 +45,20 @@         exitFailure      -- Export two example objects-    export client "/a"-        [ autoMethod "test.iface_1" "Foo" (onFoo "hello" "a")-        , autoMethod "test.iface_1" "Bar" (onBar "hello" "a")-        ]-    export client "/b"-        [ autoMethod "test.iface_1" "Foo" (onFoo "hello")-        , autoMethod "test.iface_1" "Bar" (onBar "hello")-        ]+    export client "/a" defaultInterface+             { interfaceName = "test.iface_1"+             , interfaceMethods =+               [ autoMethod "Foo" (onFoo "hello" "a")+               , autoMethod "Bar" (onBar "hello" "a")+               ]+             }+    export client "/b" defaultInterface+             { interfaceName = "test.iface_2"+             , interfaceMethods =+               [ autoMethod "Foo" (onFoo "hello")+               , autoMethod "Bar" (onBar "hello")+               ]+             }      putStrLn "Exported objects /a and /b to bus name com.example.exporting" 
examples/introspect.hs view
@@ -77,10 +77,10 @@  printMethodArg :: I.MethodArg -> IO () printMethodArg arg = do-    let dir = case I.methodArgDirection arg of-        d | d == I.directionIn -> "IN "-        d | d == I.directionOut -> "OUT"-        _ -> "   "+    let dir =+          case I.methodArgDirection arg of+            I.In -> "IN "+            I.Out -> "OUT"     putStr ("            [" ++ dir ++ " ")     putStr (show (formatSignature (signature_ [I.methodArgType arg])) ++ "] ")     putStrLn (I.methodArgName arg)
+ idlxml/dbus.xml view
@@ -0,0 +1,90 @@+<node>+  <interface name="org.freedesktop.DBus">+    <method name="Hello">+      <arg direction="out" type="s"/>+    </method>+    <method name="RequestName">+      <arg direction="in" type="s"/>+      <arg direction="in" type="u"/>+      <arg direction="out" type="u"/>+    </method>+    <method name="ReleaseName">+      <arg direction="in" type="s"/>+      <arg direction="out" type="u"/>+    </method>+    <method name="StartServiceByName">+      <arg direction="in" type="s"/>+      <arg direction="in" type="u"/>+      <arg direction="out" type="u"/>+    </method>+    <method name="UpdateActivationEnvironment">+      <arg direction="in" type="a{ss}"/>+    </method>+    <method name="NameHasOwner">+      <arg direction="in" type="s"/>+      <arg direction="out" type="b"/>+    </method>+    <method name="ListNames">+      <arg direction="out" type="as"/>+    </method>+    <method name="ListActivatableNames">+      <arg direction="out" type="as"/>+    </method>+    <method name="AddMatch">+      <arg direction="in" type="s"/>+    </method>+    <method name="RemoveMatch">+      <arg direction="in" type="s"/>+    </method>+    <method name="GetNameOwner">+      <arg direction="in" type="s"/>+      <arg direction="out" type="s"/>+    </method>+    <method name="ListQueuedOwners">+      <arg direction="in" type="s"/>+      <arg direction="out" type="as"/>+    </method>+    <method name="GetConnectionUnixUser">+      <arg direction="in" type="s"/>+      <arg direction="out" type="u"/>+    </method>+    <method name="GetConnectionUnixProcessID">+      <arg direction="in" type="s"/>+      <arg direction="out" type="u"/>+    </method>+    <method name="GetAdtAuditSessionData">+      <arg direction="in" type="s"/>+      <arg direction="out" type="ay"/>+    </method>+    <method name="GetConnectionSELinuxSecurityContext">+      <arg direction="in" type="s"/>+      <arg direction="out" type="ay"/>+    </method>+    <method name="ReloadConfig">+    </method>+    <method name="GetId">+      <arg direction="out" type="s"/>+    </method>+    <method name="GetConnectionCredentials">+      <arg direction="in" type="s"/>+      <arg direction="out" type="a{sv}"/>+    </method>+    <property name="Features" type="as" access="read">+      <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>+    </property>+    <property name="Interfaces" type="as" access="read">+      <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>+    </property>+    <signal name="NameOwnerChanged">+      <arg type="s"/>+      <arg type="s"/>+      <arg type="s"/>+    </signal>+    <signal name="NameLost">+      <arg type="s"/>+    </signal>+    <signal name="NameAcquired">+      <arg type="s"/>+    </signal>+  </interface>+</node>
lib/DBus/Client.hs view
@@ -1,8 +1,16 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverlappingInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE IncoherentInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TupleSections #-}  -- Copyright (C) 2009-2012 John Millikin <john@john-millikin.com> --@@ -55,8 +63,18 @@ module DBus.Client     (     -- * Clients-      Client+      Client(..)+    , DBusR +    -- * Path/Interface storage+    , PathInfo(..)+    , pathInterfaces+    , pathChildren+    , pathLens+    , findPath+    , Interface(..)+    , defaultInterface+     -- * Connecting to a bus     , connect     , connectSystem@@ -68,20 +86,27 @@     , call     , call_     , callNoReply+    , getProperty+    , getPropertyValue+    , setProperty+    , setPropertyValue+    , getAllProperties+    , getAllPropertiesMap+    , buildPropertiesInterface      -- * Receiving method calls     , export     , unexport-    , Method-    , method-    , Reply-    , replyReturn-    , replyError-    , throwError--    -- ** Automatic method signatures+    , Method(..)+    , makeMethod     , AutoMethod     , autoMethod+    , autoMethodWithMsg+    , Property(..)+    , autoProperty+    , readOnlyProperty+    , Reply(..)+    , throwError      -- * Signals     , SignalHandler@@ -101,6 +126,13 @@     , matchMember     , matchPathNamespace +    -- * Introspection+    , buildIntrospectionObject+    , buildIntrospectionInterface+    , buildIntrospectionMethod+    , buildIntrospectionProperty+    , buildIntrospectableInterface+     -- * Name reservation     , requestName     , releaseName@@ -110,8 +142,8 @@     , nameReplaceExisting     , nameDoNotQueue -    , RequestNameReply(NamePrimaryOwner, NameInQueue, NameExists, NameAlreadyOwner)-    , ReleaseNameReply(NameReleased, NameNonExistent, NameNotOwner)+    , RequestNameReply(..)+    , ReleaseNameReply(..)      -- * Client errors     , ClientError@@ -125,27 +157,50 @@     , clientThreadRunner     , defaultClientOptions     , connectWith++    , dbusName+    , dbusPath++    , ErrorName+    , errorFailed+    , errorInvalidParameters+    , errorUnknownMethod     ) where -import           Control.Concurrent-import           Control.Exception (SomeException, throwIO)+import Control.Applicative+import Control.Arrow+import Control.Concurrent import qualified Control.Exception-import           Control.Monad (forever, forM_, when)-import           Data.Bits ((.|.))-import           Data.IORef-import           Data.List (foldl', intercalate, isPrefixOf)-import qualified Data.Map-import           Data.Map (Map)-import           Data.Maybe (catMaybes, listToMaybe)-import           Data.Typeable (Typeable)-import           Data.Unique-import           Data.Word (Word32)-import           Data.Function+import Control.Exception (SomeException, throwIO)+import Control.Lens+import Control.Monad+import Control.Monad.Trans.Class+import Control.Monad.Trans.Reader+import Control.Monad.Trans.Except+import Data.Bits ((.|.))+import Data.Coerce+import Data.Foldable hiding (forM_, and)+import Data.Function+import Data.Functor ((<$>))+import Data.IORef+import Data.List (intercalate, isPrefixOf)+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as M+import Data.Maybe+import Data.Monoid+import Data.String+import qualified Data.Traversable as T+import Data.Typeable (Typeable)+import Data.Unique+import Data.Word (Word32)+import Prelude hiding (foldl, foldr, concat) -import           DBus+import DBus+import DBus.Internal.Message+import qualified DBus.Internal.Types as T import qualified DBus.Introspection as I import qualified DBus.Socket-import           DBus.Transport (TransportOpen, SocketTransport)+import DBus.Transport (TransportOpen, SocketTransport)  data ClientError = ClientError     { clientErrorMessage :: String@@ -164,10 +219,13 @@     { clientSocket :: DBus.Socket.Socket     , clientPendingCalls :: IORef (Map Serial (MVar (Either MethodError MethodReturn)))     , clientSignalHandlers :: IORef (Map Unique SignalHandler)-    , clientObjects :: IORef (Map ObjectPath ObjectInfo)+    , clientObjects :: IORef PathInfo     , clientThreadID :: ThreadId+    , clientInterfaces :: [Interface]     } +type DBusR a = ReaderT Client IO a+ data ClientOptions t = ClientOptions     {     -- | Options for the underlying socket, for advanced use cases. See@@ -183,35 +241,142 @@     --     -- The default implementation is 'forever'.     , clientThreadRunner :: IO () -> IO ()+    -- | A function to build the interfaces that should be present at every+    -- point where there is an object present. The default value builds the+    -- property and introspection interfaces.+    , clientBuildInterfaces :: Client -> [Interface]     } -type Callback = (ReceivedMessage -> IO ())- type FormattedMatchRule = String-data SignalHandler = SignalHandler Unique FormattedMatchRule (IORef Bool) (Signal -> IO ())+data SignalHandler =+  SignalHandler Unique FormattedMatchRule (IORef Bool) (Signal -> IO ()) +data Method = Method+  { methodName :: MemberName+  , inSignature :: Signature+  , outSignature :: Signature+  , methodHandler :: MethodCall -> DBusR Reply+  }++data Property = Property+  { propertyName :: MemberName+  , propertyType :: Type+  , propertyGetter :: Maybe (IO Variant)+  , propertySetter :: Maybe (Variant -> IO ())+  }+ data Reply     = ReplyReturn [Variant]     | ReplyError ErrorName [Variant] --- | Reply to a method call with a successful return, containing the given body.-replyReturn :: [Variant] -> Reply-replyReturn = ReplyReturn+data Interface = Interface+  { interfaceName :: InterfaceName+  , interfaceMethods :: [Method]+  , interfaceProperties :: [Property]+  , interfaceSignals :: [I.Signal]+  } --- | Reply to a method call with an error, containing the given error name and--- body.------ Typically, the first item of the error body is a string with a message--- describing the error.-replyError :: ErrorName -> [Variant] -> Reply-replyError = ReplyError+defaultInterface :: Interface+defaultInterface =+  Interface { interfaceName = ""+            , interfaceMethods = []+            , interfaceProperties = []+            , interfaceSignals = []+            } -data Method = Method InterfaceName MemberName Signature Signature (MethodCall -> IO Reply)+data PathInfo = PathInfo+  { _pathInterfaces :: [Interface]+  , _pathChildren :: Map String PathInfo+  } -type ObjectInfo = Map InterfaceName InterfaceInfo-type InterfaceInfo = Map MemberName MethodInfo-data MethodInfo = MethodInfo Signature Signature Callback+-- NOTE: This instance is needed to make modifyNothingHandler work, but it+-- shouldn't really be used for much else. A more complete implementation can't+-- be provided because PathInfo > Interface > Method conatain functions which+-- can't/don't have an eq instance.+instance Eq PathInfo where+  a == b = null (_pathInterfaces a) &&+           null (_pathInterfaces b) &&+           M.null (_pathChildren a) &&+           M.null (_pathChildren b) +makeLenses ''PathInfo++emptyPathInfo :: PathInfo+emptyPathInfo = PathInfo+  { _pathInterfaces = []+  , _pathChildren = M.empty+  }++traverseElement+  :: Applicative f+  => (a -> Maybe PathInfo -> f (Maybe PathInfo))+  -> String+  -> a+  -> PathInfo+  -> f PathInfo+traverseElement nothingHandler pathElement =+  pathChildren . at pathElement . nothingHandler++lookupNothingHandler+  :: (a -> Const (Data.Monoid.First PathInfo) b)+  -> Maybe a+  -> Const (Data.Monoid.First PathInfo) (Maybe b)+lookupNothingHandler = _Just++modifyNothingHandler ::+  (PathInfo -> Identity PathInfo)+    -> Maybe PathInfo+    -> Identity (Maybe PathInfo)+modifyNothingHandler = non emptyPathInfo++pathLens ::+  Applicative f =>+  ObjectPath+  -> ((PathInfo -> f PathInfo) -> Maybe PathInfo -> f (Maybe PathInfo))+  -> (PathInfo -> f PathInfo)+  -> PathInfo+  -> f PathInfo+pathLens path nothingHandler =+  foldl (\f pathElem -> f . traverseElement nothingHandler pathElem) id $+  T.pathElements path++modifyPathInfoLens+  :: ObjectPath+     -> (PathInfo -> Identity PathInfo) -> PathInfo -> Identity PathInfo+modifyPathInfoLens path = pathLens path modifyNothingHandler++modifyPathInterfacesLens+  :: ObjectPath+     -> ([Interface] -> Identity [Interface])+     -> PathInfo+     -> Identity PathInfo+modifyPathInterfacesLens path = modifyPathInfoLens path . pathInterfaces++addInterface :: ObjectPath -> Interface -> PathInfo -> PathInfo+addInterface path interface =+  over (modifyPathInterfacesLens path) (interface :)++findPath :: ObjectPath -> PathInfo -> Maybe PathInfo+findPath path = preview (pathLens path lookupNothingHandler)++findByGetterAndName ::+  (Coercible a2 a1, Eq a1, Foldable t) =>+  t a3 -> (a3 -> a2) -> a1 -> Maybe a3+findByGetterAndName options getter name =+  find ((== name) . coerce . getter) options++findInterface :: [Interface] -> InterfaceName -> PathInfo -> Maybe Interface+findInterface alwaysPresent (T.InterfaceName name) info =+  findByGetterAndName (_pathInterfaces info ++ alwaysPresent) interfaceName name++findMethod :: MemberName -> Interface -> Maybe Method+findMethod (T.MemberName name) interface =+  findByGetterAndName (interfaceMethods interface) methodName name++findProperty :: MemberName -> Interface -> Maybe Property+findProperty (T.MemberName name) interface =+  findByGetterAndName (interfaceProperties interface) propertyName name+ -- | 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@@@ -264,9 +429,9 @@ connectWith opts addr = do     sock <- DBus.Socket.openWith (clientSocketOptions opts) addr -    pendingCalls <- newIORef Data.Map.empty-    signalHandlers <- newIORef Data.Map.empty-    objects <- newIORef Data.Map.empty+    pendingCalls <- newIORef M.empty+    signalHandlers <- newIORef M.empty+    objects <- newIORef $ PathInfo [] M.empty      let threadRunner = clientThreadRunner opts @@ -281,23 +446,112 @@             , clientSignalHandlers = signalHandlers             , clientObjects = objects             , clientThreadID = threadID+            , clientInterfaces = clientBuildInterfaces opts client             }     putMVar clientMVar client -    export client "/" [introspectRoot client]-     callNoReply client (methodCall dbusPath dbusInterface "Hello")         { methodCallDestination = Just dbusName         }      return client +makeErrorReply :: ErrorName -> Reply+makeErrorReply errorName = ReplyError errorName []++buildPropertiesInterface :: Client -> Interface+buildPropertiesInterface client =+  let alwaysPresent = clientInterfaces client+      getPropertyObjF propertyInterfaceName memberName path info =+        findInterfaceAtPath alwaysPresent info path+        (Just $ fromString propertyInterfaceName) >>=+        (maybeToEither errorUnknownMethod . findProperty (fromString memberName))+      getPropertyObj propertyInterfaceName memberName path =+        getPropertyObjF propertyInterfaceName memberName path <$>+                        readIORef (clientObjects client)+      callGet MethodCall { methodCallPath = path }+              propertyInterfaceName memberName =+        left makeErrorReply <$>+        runExceptT (do+          property <- ExceptT $ getPropertyObj propertyInterfaceName+                      memberName path+          ExceptT $ sequenceA $ maybeToEither errorNotAuthorized $+                  propertyGetter property)+      callSet MethodCall { methodCallPath = path }+              propertyInterfaceName memberName value =+        left makeErrorReply <$>+        runExceptT (do+          property <- ExceptT $ getPropertyObj propertyInterfaceName memberName path+          setter <- ExceptT $ return $ maybeToEither errorNotAuthorized $+                    propertySetter property+          lift $ setter value)+      callGetAll MethodCall { methodCallPath = path } propertyInterfaceName =+        left makeErrorReply <$>+        runExceptT (do+          info <- lift $ readIORef (clientObjects client)+          propertyInterface <-+            ExceptT $ return $ findInterfaceAtPath alwaysPresent info path $+                    Just $ fromString propertyInterfaceName+          let properties = interfaceProperties propertyInterface+              nameGetters :: [IO (String, Variant)]+              nameGetters = [ (coerce name,) <$> getter |+                              Property { propertyName = name+                                       , propertyGetter = Just getter+                                       } <- properties]+          lift $ M.fromList <$> T.sequenceA nameGetters)+  in+    defaultInterface+    { interfaceName = propertiesInterfaceName+    , interfaceMethods =+      [ autoMethodWithMsg "Get" callGet+      , autoMethodWithMsg "GetAll" callGetAll+      , autoMethodWithMsg "Set" callSet+      ]+    , interfaceSignals =+      [ I.Signal+        { I.signalName = "PropertiesChanged"+        , I.signalArgs =+          [ I.SignalArg+            { I.signalArgName = "interface_name"+            , I.signalArgType = T.TypeString+            }+          , I.SignalArg+            { I.signalArgName = "changed_properties"+            , I.signalArgType = T.TypeDictionary T.TypeString T.TypeVariant+            }+          , I.SignalArg+            { I.signalArgName = "invalidated_properties"+            , I.signalArgType = T.TypeArray T.TypeString+            }+          ]+        }+      ]+    }++buildIntrospectableInterface :: Client -> Interface+buildIntrospectableInterface client =+  defaultInterface+  { interfaceName = introspectableInterfaceName+  , interfaceMethods = [ autoMethodWithMsg "Introspect" callIntrospect ]+  } where+  callIntrospect MethodCall { methodCallPath = path } = do+    info <- readIORef (clientObjects client)+    return $ left makeErrorReply $ do+      targetInfo <- maybeToEither errorUnknownObject $ findPath path info+      -- TODO: We should probably return a better error here:+      maybeToEither errorUnknownObject $ I.formatXML $+                    buildIntrospectionObject defaultInterfaces+                    targetInfo (T.pathElements path)+  defaultInterfaces = map buildIntrospectionInterface $ clientInterfaces client+ -- | Default client options. Uses the built-in Socket-based transport, which -- supports the @tcp:@ and @unix:@ methods. defaultClientOptions :: ClientOptions SocketTransport defaultClientOptions = ClientOptions     { clientSocketOptions = DBus.Socket.defaultSocketOptions     , clientThreadRunner = forever+    , clientBuildInterfaces =+      \client -> map ($ client) [buildPropertiesInterface, buildIntrospectableInterface]     }  -- | Stop a 'Client''s callback thread and close its underlying socket.@@ -308,13 +562,14 @@  disconnect' :: Client -> IO () disconnect' client = do-    pendingCalls <- atomicModifyIORef (clientPendingCalls client) (\p -> (Data.Map.empty, p))-    forM_ (Data.Map.toList pendingCalls) $ \(k, v) -> do+    pendingCalls <- atomicModifyIORef (clientPendingCalls client) (\p -> (M.empty, p))+    forM_ (M.toList pendingCalls) $ \(k, v) ->         putMVar v (Left (methodError k errorDisconnected)) -    atomicWriteIORef (clientSignalHandlers client) Data.Map.empty-    atomicWriteIORef (clientObjects client) Data.Map.empty+    atomicWriteIORef (clientSignalHandlers client) M.empty +    atomicWriteIORef (clientObjects client) emptyPathInfo+     DBus.Socket.close (clientSocket client)  mainLoop :: Client -> IO ()@@ -330,22 +585,34 @@      dispatch client msg ++-- Dispatch+ dispatch :: Client -> ReceivedMessage -> IO () dispatch client = go where     go (ReceivedMethodReturn _ msg) = dispatchReply (methodReturnSerial msg) (Right msg)     go (ReceivedMethodError _ msg) = dispatchReply (methodErrorSerial msg) (Left msg)     go (ReceivedSignal _ msg) = do         handlers <- readIORef (clientSignalHandlers client)-        forM_ (Data.Map.toAscList handlers) (\(_, SignalHandler _ _ _ h) -> forkIO (h msg) >> return ())-    go received@(ReceivedMethodCall serial msg) = do-        objects <- readIORef (clientObjects client)+        forM_ (M.toAscList handlers) (\(_, SignalHandler _ _ _ h) -> forkIO $ void $ h msg)+    go (ReceivedMethodCall serial msg) = do+        pathInfo <- readIORef (clientObjects client)         let sender = methodCallSender msg-        _ <- forkIO $ case findMethod objects msg of-            Right io -> io received+            sendResult reply =+              case reply of+                ReplyReturn vs -> send_ client (methodReturn serial)+                                  { methodReturnDestination = sender+                                  , methodReturnBody = vs+                                  } (\_ -> return ())+                ReplyError name vs -> send_ client (methodError serial name)+                                      { methodErrorDestination = sender+                                      , methodErrorBody = vs+                                      } (\_ -> return ())+        _ <- forkIO $ case findMethodForCall (clientInterfaces client) pathInfo msg of+            Right Method { methodHandler = handler } ->+              runReaderT (handler msg) client >>= sendResult             Left errName -> send_ client-                (methodError serial errName)-                    { methodErrorDestination = sender-                    }+                (methodError serial errName) { methodErrorDestination = sender }                 (\_ -> return ())         return ()     go _ = return ()@@ -353,13 +620,37 @@     dispatchReply serial result = do         pending <- atomicModifyIORef             (clientPendingCalls client)-            (\p -> case Data.Map.lookup serial p of+            (\p -> case M.lookup serial p of                 Nothing -> (p, Nothing)-                Just mvar -> (Data.Map.delete serial p, Just mvar))+                Just mvar -> (M.delete serial p, Just mvar))         case pending of             Just mvar -> putMVar mvar result             Nothing -> return () +findInterfaceAtPath+  :: [Interface]+  -> PathInfo+  -> ObjectPath+  -> Maybe InterfaceName+  -> Either ErrorName Interface+findInterfaceAtPath defaultInterfaces info path name =+  maybeToEither errorUnknownObject (findPath path info) >>=+  (maybeToEither errorUnknownInterface .+                 maybe (const Nothing) (findInterface defaultInterfaces) name)++findMethodForCall ::+  [Interface] -> PathInfo -> MethodCall -> Either ErrorName Method+findMethodForCall defaultInterfaces info+                  MethodCall { methodCallInterface = interface+                             , methodCallMember = member+                             , methodCallPath = path+                             } =+  findInterfaceAtPath defaultInterfaces info path interface >>=+  (maybeToEither errorUnknownMethod . findMethod member)+++-- Request name+ data RequestNameFlag     = AllowReplacement     | ReplaceExisting@@ -422,7 +713,7 @@     deriving (Eq, Show)  encodeFlags :: [RequestNameFlag] -> Word32-encodeFlags = foldr (.|.) 0 . map flagValue where+encodeFlags = foldr ((.|.) . flagValue) 0  where     flagValue AllowReplacement = 0x1     flagValue ReplaceExisting  = 0x2     flagValue DoNotQueue       = 0x4@@ -499,6 +790,9 @@         3 -> NameNotOwner         _ -> UnknownReleaseNameReply code ++-- Requests+ send_ :: Message msg => Client -> msg -> (Serial -> IO a) -> IO a send_ client msg io = do     result <- Control.Exception.try (DBus.Socket.send (clientSocket client) msg io)@@ -521,14 +815,14 @@             }     mvar <- newEmptyMVar     let ref = clientPendingCalls client-    serial <- send_ client safeMsg (\serial -> atomicModifyIORef ref (\p -> (Data.Map.insert serial mvar p, serial)))+    serial <- send_ client safeMsg (\serial -> atomicModifyIORef ref (\p -> (M.insert serial mvar p, serial)))      -- At this point, we wait for the reply to arrive. The user may cancel     -- a pending call by sending this thread an exception via something     -- like 'timeout'; in that case, we want to clean up the pending call.     Control.Exception.onException         (takeMVar mvar)-        (atomicModifyIORef_ ref (Data.Map.delete serial))+        (atomicModifyIORef_ ref (M.delete serial))  -- | Send a method call to the bus, and wait for the response. --@@ -558,6 +852,84 @@             }     send_ client safeMsg (\_ -> return ()) +orDefaultInterface :: Maybe InterfaceName -> InterfaceName+orDefaultInterface = fromMaybe "org.freedesktop.DBus"++dummyMethodError :: MethodError+dummyMethodError =+  MethodError { methodErrorName = errorName_ "org.ClientTypeMismatch"+              , methodErrorSerial = T.Serial 1+              , methodErrorSender = Nothing+              , methodErrorDestination = Nothing+              , methodErrorBody = []+              }++unpackVariant :: IsValue a => MethodCall -> Variant -> Either MethodError a+unpackVariant MethodCall { methodCallSender = sender } variant =+  maybeToEither dummyMethodError { methodErrorBody =+                                     [variant, toVariant $ show $ variantType variant]+                                 , methodErrorSender = sender+                                 } $ fromVariant variant++-- | Retrieve a property using the method call parameters that were provided.+--+-- Throws a 'ClientError' if the property request couldn't be sent.+getProperty :: Client -> MethodCall -> IO (Either MethodError Variant)+getProperty client+            msg@MethodCall { methodCallInterface = interface+                           , methodCallMember = member+                           } =+  (>>= (unpackVariant msg . head . methodReturnBody)) <$>+    call client msg { methodCallInterface = Just propertiesInterfaceName+                    , methodCallMember = getMemberName+                    , methodCallBody = [ toVariant (coerce (orDefaultInterface interface) :: String)+                                       , toVariant (coerce member :: String)+                                       ]+                    }++getPropertyValue :: IsValue a => Client -> MethodCall -> IO (Either MethodError a)+getPropertyValue client msg =+  (>>= unpackVariant msg) <$> getProperty client msg++setProperty :: Client -> MethodCall -> Variant -> IO (Either MethodError MethodReturn)+setProperty client+            msg@MethodCall { methodCallInterface = interface+                           , methodCallMember = member+                           } value =+  call client msg { methodCallInterface = Just propertiesInterfaceName+                  , methodCallMember = setMemberName+                  , methodCallBody =+                    [ toVariant (coerce (orDefaultInterface interface) :: String)+                    , toVariant (coerce member :: String)+                    , value+                    ]+                  }++setPropertyValue+  :: IsValue a+  => Client -> MethodCall -> a -> IO (Maybe MethodError)+setPropertyValue client msg v = eitherToMaybe <$> setProperty client msg (toVariant v)+  where eitherToMaybe (Left a) = Just a+        eitherToMaybe (Right _) = Nothing++getAllProperties :: Client -> MethodCall -> IO (Either MethodError MethodReturn)+getAllProperties client+               msg@MethodCall { methodCallInterface = interface } =+  call client msg { methodCallInterface = Just propertiesInterfaceName+                  , methodCallMember = getAllMemberName+                  , methodCallBody = [toVariant (coerce (orDefaultInterface interface) :: String)]+                  }++getAllPropertiesMap :: Client -> MethodCall -> IO (Either MethodError (M.Map String Variant))+getAllPropertiesMap client msg =+  -- NOTE: We should never hit the error case here really unless the client+  -- returns the wrong type of object.+  (>>= (maybeToEither dummyMethodError . fromVariant . head . methodReturnBody))+  <$> getAllProperties client msg+++-- Signals+ -- | Request that the bus forward signals matching the given rule to this -- client, and process them in a callback. --@@ -578,7 +950,7 @@     registered <- newIORef True     let handler = SignalHandler handlerId formatted registered (\msg -> when (checkMatchRule rule msg) (io msg)) -    atomicModifyIORef (clientSignalHandlers client) (\hs -> (Data.Map.insert handlerId handler hs, ()))+    atomicModifyIORef (clientSignalHandlers client) (\hs -> (M.insert handlerId handler hs, ()))     _ <- call_ client (methodCall dbusPath dbusInterface "AddMatch")         { methodCallDestination = Just dbusName         , methodCallBody = [toVariant formatted]@@ -592,7 +964,7 @@ removeMatch client (SignalHandler handlerId formatted registered _) = do     shouldUnregister <- atomicModifyIORef registered (\wasRegistered -> (False, wasRegistered))     when shouldUnregister $ do-        atomicModifyIORef (clientSignalHandlers client) (\hs -> (Data.Map.delete handlerId hs, ()))+        atomicModifyIORef (clientSignalHandlers client) (\hs -> (M.delete handlerId hs, ()))         _ <- call_ client (methodCall dbusPath dbusInterface "RemoveMatch")             { methodCallDestination = Just dbusName             , methodCallBody = [toVariant formatted]@@ -601,7 +973,7 @@  -- | Equivalent to 'addMatch', but does not return the added 'SignalHandler'. listen :: Client -> MatchRule -> (Signal -> IO ()) -> IO ()-listen client rule io = addMatch client rule io >> return ()+listen client rule io = void $ addMatch client rule io {-# DEPRECATED listen "Prefer DBus.Client.addMatch in new code." #-}  -- | Emit the signal on the bus.@@ -700,149 +1072,11 @@            -> IO a throwError name message extra = Control.Exception.throwIO (MethodExc name (toVariant message : extra)) --- | Define a method handler, which will accept method calls with the given--- interface and member name.------ Note that the input and output parameter signatures are used for--- introspection, but are not checked when executing a method.------ See 'autoMethod' for an easier way to export functions with simple--- parameter and return types.-method :: InterfaceName-       -> MemberName-       -> Signature -- ^ Input parameter signature-       -> Signature -- ^ Output parameter signature-       -> (MethodCall -> IO Reply)-       -> Method-method iface name inSig outSig io = Method iface name inSig outSig-    (\msg -> Control.Exception.catch-        (Control.Exception.catch-            (io msg)-            (\(MethodExc name' vs') -> return (ReplyError name' vs')))-        (\exc -> return (ReplyError errorFailed-            [toVariant (show (exc :: SomeException))])))---- | Export the given functions under the given 'ObjectPath' and--- 'InterfaceName'.------ Use 'autoMethod' to construct a 'Method' from a function that accepts and--- returns simple types.------ Use 'method' to construct a 'Method' from a function that handles parameter--- conversion manually.------ @---ping :: MethodCall -> IO 'Reply'---ping _ = replyReturn []------sayHello :: String -> IO String---sayHello name = return (\"Hello \" ++ name ++ \"!\")------export client \"/hello_world\"---    [ 'method' \"com.example.HelloWorld\" \"Ping\" ping---    , 'autoMethod' \"com.example.HelloWorld\" \"Hello\" sayHello---    ]--- @-export :: Client -> ObjectPath -> [Method] -> IO ()-export client path methods = atomicModifyIORef (clientObjects client) addObject where-    addObject objs = (Data.Map.insert path info objs, ())--    info = foldl' addMethod Data.Map.empty (defaultIntrospect : methods)-    addMethod m (Method iface name inSig outSig cb) = Data.Map.insertWith'-        Data.Map.union iface-        (Data.Map.fromList [(name, MethodInfo inSig outSig (wrapCB cb))]) m--    wrapCB cb (ReceivedMethodCall serial msg) = do-        reply <- cb msg-        let sender = methodCallSender msg-        case reply of-            ReplyReturn vs -> send_ client (methodReturn serial)-                { methodReturnDestination = sender-                , methodReturnBody = vs-                } (\_ -> return ())-            ReplyError name vs -> send_ client (methodError serial name)-                { methodErrorDestination = sender-                , methodErrorBody = vs-                } (\_ -> return ())-    wrapCB _ _ = return ()--    defaultIntrospect = methodIntrospect $ do-        objects <- readIORef (clientObjects client)-        let Just obj = Data.Map.lookup path objects-        return (introspect path obj)---- | Revokes the export of the given 'ObjectPath'. This will remove all--- interfaces and methods associated with the path.-unexport :: Client -> ObjectPath -> IO ()-unexport client path = atomicModifyIORef (clientObjects client) deleteObject where-    deleteObject objs = (Data.Map.delete path objs, ())--findMethod :: Map ObjectPath ObjectInfo -> MethodCall -> Either ErrorName Callback-findMethod objects msg = case Data.Map.lookup (methodCallPath msg) objects of-    Nothing -> Left errorUnknownObject-    Just obj -> case methodCallInterface msg of-        Nothing -> let-            members = do-                iface <- Data.Map.elems obj-                case Data.Map.lookup (methodCallMember msg) iface of-                    Just member -> [member]-                    Nothing -> []-            in case members of-                [MethodInfo _ _ io] -> Right io-                _ -> Left errorUnknownMethod-        Just ifaceName -> case Data.Map.lookup ifaceName obj of-            Nothing -> Left errorUnknownInterface-            Just iface -> case Data.Map.lookup (methodCallMember msg) iface of-                Just (MethodInfo _ _ io) -> Right io-                _ -> Left errorUnknownMethod--introspectRoot :: Client -> Method-introspectRoot client = methodIntrospect $ do-    objects <- readIORef (clientObjects client)-    let paths = filter (/= "/") (Data.Map.keys objects)-    return (I.object "/")-        { I.objectInterfaces =-            [ (I.interface interfaceIntrospectable)-                { I.interfaceMethods =-                    [ (I.method "Introspect")-                        { I.methodArgs =-                            [ I.methodArg "" TypeString I.directionOut-                            ]-                        }-                    ]-                }-            ]-        , I.objectChildren = [I.object p | p <- paths]-        }--methodIntrospect :: IO I.Object -> Method-methodIntrospect get = method interfaceIntrospectable "Introspect" "" "s" $-    \msg -> case methodCallBody msg of-        [] -> do-            obj <- get-            let Just xml = I.formatXML obj-            return (replyReturn [toVariant xml])-        _ -> return (replyError errorInvalidParameters [])--introspect :: ObjectPath -> ObjectInfo -> I.Object-introspect path obj = (I.object path) { I.objectInterfaces = interfaces } where-    interfaces = map introspectIface (Data.Map.toList obj)--    introspectIface (name, iface) = (I.interface name)-        { I.interfaceMethods = concatMap introspectMethod (Data.Map.toList iface)-        }--    args inSig outSig =-        map (introspectArg I.directionIn) (signatureTypes inSig) ++-        map (introspectArg I.directionOut) (signatureTypes outSig)--    introspectMethod (name, MethodInfo inSig outSig _) =-        [ (I.method name)-            { I.methodArgs = args inSig outSig-            }-        ]++-- Method construction -    introspectArg dir t = I.methodArg "" t dir+returnInvalidParameters :: Monad m => m Reply+returnInvalidParameters = return $ ReplyError errorInvalidParameters []  -- | Used to automatically generate method signatures for introspection -- documents. To support automatic signatures, a method's parameters and@@ -859,31 +1093,46 @@ -- converted to a list of return values. class AutoMethod a where     funTypes :: a -> ([Type], [Type])-    apply :: a -> [Variant] -> Maybe (IO [Variant])--instance AutoMethod (IO ()) where-    funTypes _ = ([], [])+    apply :: a -> [Variant] -> DBusR Reply -    apply io [] = Just (io >> return [])-    apply _ _ = Nothing+handleTopLevelReturn :: IsVariant a => a -> [Variant]+handleTopLevelReturn value =+  case toVariant value of+    T.Variant (T.ValueStructure xs) -> fmap T.Variant xs+    v -> [v]  instance IsValue a => AutoMethod (IO a) where-    funTypes io = cased where-        cased = ([], case ioT io undefined of-            (_, t) -> case t of-                TypeStructure ts -> ts-                _ -> [t])+  funTypes io = funTypes (lift io :: DBusR a)+  apply io = apply (lift io :: DBusR a) -        ioT :: IsValue a => IO a -> a -> (a, Type)-        ioT _ a = (a, typeOf a)+instance IsValue a => AutoMethod (DBusR a) where+    funTypes _ = ([], outTypes) where+      aType :: Type+      aType = typeOf (undefined :: a)+      outTypes =+        case aType of+          TypeStructure ts -> ts+          _ -> [aType] -    apply io [] = Just (do-        var <- fmap toVariant io-        case fromVariant var of-            Just struct -> return (structureItems struct)-            Nothing -> return [var])-    apply _ _ = Nothing+    apply io [] = ReplyReturn . handleTopLevelReturn <$> io+    apply _ _ = returnInvalidParameters +instance IsValue a => AutoMethod (IO (Either Reply a)) where+  funTypes io = funTypes (lift io :: DBusR (Either Reply a))+  apply io = apply (lift io :: DBusR (Either Reply a))++instance IsValue a => AutoMethod (DBusR (Either Reply a)) where+    funTypes _ = ([], outTypes) where+      aType :: Type+      aType = typeOf (undefined :: a)+      outTypes =+        case aType of+          TypeStructure ts -> ts+          _ -> [aType]++    apply io [] = either id (ReplyReturn . handleTopLevelReturn) <$> io+    apply _ _ = returnInvalidParameters+ instance (IsValue a, AutoMethod fn) => AutoMethod (a -> fn) where     funTypes fn = cased where         cased = case valueT undefined of@@ -893,10 +1142,10 @@         valueT :: IsValue a => a -> (a, Type)         valueT a = (a, typeOf a) -    apply _ [] = Nothing+    apply _ [] = returnInvalidParameters     apply fn (v:vs) = case fromVariant v of         Just v' -> apply (fn v') vs-        Nothing -> Nothing+        Nothing -> returnInvalidParameters  -- | Prepare a Haskell function for export, automatically detecting the -- function's type signature.@@ -904,28 +1153,157 @@ -- See 'AutoMethod' for details on the limitations of this function. -- -- See 'method' for exporting functions with user-defined types.-autoMethod :: (AutoMethod fn) => InterfaceName -> MemberName -> fn -> Method-autoMethod iface name fun = DBus.Client.method iface name inSig outSig io where-    (typesIn, typesOut) = funTypes fun-    inSig = case signature typesIn of-        Just sig -> sig-        Nothing -> invalid "input"-    outSig = case signature typesOut of-        Just sig -> sig-        Nothing -> invalid "output"-    io msg = case apply fun (methodCallBody msg) of-        Nothing -> return (ReplyError errorInvalidParameters [])-        Just io' -> fmap ReplyReturn io'+autoMethod :: (AutoMethod fn) => MemberName -> fn -> Method+autoMethod name fun = autoMethodWithMsg name $ const fun +autoMethodWithMsg :: (AutoMethod fn) => MemberName -> (MethodCall -> fn) -> Method+autoMethodWithMsg name fun = makeMethod name inSig outSig io where+    (typesIn, typesOut) = funTypes (fun undefined)+    inSig = fromMaybe (invalid "input") $ signature typesIn+    outSig = fromMaybe (invalid "output") $ signature typesOut+    io msg = apply (fun msg) (methodCallBody msg)+     invalid label = error (concat         [ "Method "-        , formatInterfaceName iface         , "."         , formatMemberName name         , " has an invalid "         , label         , " signature."]) +autoProperty+  :: forall v. (IsValue v)+  => MemberName -> Maybe (IO v) -> Maybe (v -> IO ()) -> Property+autoProperty name mgetter msetter =+  Property name propType (fmap toVariant <$> mgetter) (variantSetter <$> msetter)+    where propType = typeOf (undefined :: v)+          variantSetter setter =+            let newFun variant = maybe (return ()) setter (fromVariant variant)+            in newFun++readOnlyProperty :: (IsValue v) => MemberName -> IO v -> Property+readOnlyProperty name getter = autoProperty name (Just getter) Nothing++-- | Define a method handler, which will accept method calls with the given+-- interface and member name.+--+-- Note that the input and output parameter signatures are used for+-- introspection, but are not checked when executing a method.+--+-- See 'autoMethod' for an easier way to export functions with simple+-- parameter and return types.+makeMethod+  :: MemberName+  -> Signature -- ^ Input parameter signature+  -> Signature -- ^ Output parameter signature+  -> (MethodCall -> DBusR Reply)+  -> Method+makeMethod name inSig outSig io = Method name inSig outSig+    (\msg -> do+       fromReader <- ask+       lift $ Control.Exception.catch+        (Control.Exception.catch+            (runReaderT (io msg) fromReader)+            (\(MethodExc name' vs') -> return (ReplyError name' vs')))+        (\exc -> return (ReplyError errorFailed+            [toVariant (show (exc :: SomeException))])))++-- | Export the given 'Interface' at the given 'ObjectPath'+--+-- Use 'autoMethod' to construct a 'Method' from a function that accepts and+-- returns simple types.+--+-- Use 'method' to construct a 'Method' from a function that handles parameter+-- conversion manually.+--+-- @+--ping :: MethodCall -> IO 'Reply'+--ping _ = ReplyReturn []+--+--sayHello :: String -> IO String+--sayHello name = return (\"Hello \" ++ name ++ \"!\")+--+-- export client \"/hello_world\"+--   defaultInterface { interfaceName = \"com.example.HelloWorld\"+--                    , interfaceMethods =+--                      [ 'method' \"com.example.HelloWorld\" \"Ping\" ping+--                      , 'autoMethod' \"com.example.HelloWorld\" \"Hello\" sayHello+--                      ]+--                    }+-- @+export :: Client -> ObjectPath -> Interface -> IO ()+export client path interface =+  atomicModifyIORef_ (clientObjects client) $ addInterface path interface++-- | Revokes the export of the given 'ObjectPath'. This will remove all+-- interfaces and methods associated with the path.+unexport :: Client -> ObjectPath -> IO ()+unexport client path = atomicModifyIORef_ (clientObjects client) clear+  where clear = over (modifyPathInterfacesLens path) $ const []+++-- Introspection++buildIntrospectionObject :: [I.Interface] -> PathInfo -> [String] -> I.Object+buildIntrospectionObject defaultInterfaces+                         PathInfo+                         { _pathInterfaces = interfaces+                         , _pathChildren = infoChildren+                         } elems =+  I.Object+     { I.objectPath = T.fromElements elems+     , I.objectInterfaces =+       (if null interfaces then [] else defaultInterfaces) +++       map buildIntrospectionInterface interfaces+     -- TODO: Eventually we should support not outputting everything if there is+     -- a lot of stuff.+     , I.objectChildren = M.elems $ M.mapWithKey recurseFromString infoChildren+     }+    where recurseFromString stringNode nodeInfo =+            buildIntrospectionObject defaultInterfaces nodeInfo $ elems ++ [stringNode]++buildIntrospectionInterface :: Interface -> I.Interface+buildIntrospectionInterface Interface+  { interfaceName = name+  , interfaceMethods = methods+  , interfaceProperties = properties+  , interfaceSignals = signals+  } =+  I.Interface+   { I.interfaceName = name+   , I.interfaceMethods = map buildIntrospectionMethod methods+   , I.interfaceProperties = map buildIntrospectionProperty properties+   , I.interfaceSignals = signals+   }++buildIntrospectionProperty :: Property -> I.Property+buildIntrospectionProperty (Property memberName ptype getter setter) =+  I.Property { I.propertyName = coerce memberName+             , I.propertyType = ptype+             , I.propertyRead = isJust getter+             , I.propertyWrite = isJust setter+             }++buildIntrospectionMethod :: Method -> I.Method+buildIntrospectionMethod Method+  { methodName = name+  , inSignature = inSig+  , outSignature = outSig+  } = I.Method+    { I.methodName = name+    , I.methodArgs = zipWith makeMethodArg ['a'..'z'] $ inTuples ++ outTuples+    }+  where inTuples = map (, I.In) $ coerce inSig+        outTuples = map (, I.Out) $ coerce outSig+        makeMethodArg nameChar (t, dir) =+          I.MethodArg { I.methodArgName = [nameChar]+                      , I.methodArgType = t+                      , I.methodArgDirection = dir+                      }+++-- Constants+ errorFailed :: ErrorName errorFailed = errorName_ "org.freedesktop.DBus.Error.Failed" @@ -944,6 +1322,9 @@ errorInvalidParameters :: ErrorName errorInvalidParameters = errorName_ "org.freedesktop.DBus.Error.InvalidParameters" +errorNotAuthorized :: ErrorName+errorNotAuthorized = errorName_ "org.freedesktop.DBus.Error.NotAuthorized"+ dbusName :: BusName dbusName = busName_ "org.freedesktop.DBus" @@ -953,13 +1334,31 @@ dbusInterface :: InterfaceName dbusInterface = interfaceName_ "org.freedesktop.DBus" -interfaceIntrospectable :: InterfaceName-interfaceIntrospectable = interfaceName_ "org.freedesktop.DBus.Introspectable"+introspectableInterfaceName :: InterfaceName+introspectableInterfaceName = interfaceName_ "org.freedesktop.DBus.Introspectable" +propertiesInterfaceName :: InterfaceName+propertiesInterfaceName = fromString "org.freedesktop.DBus.Properties"++getAllMemberName :: MemberName+getAllMemberName = fromString "GetAll"++getMemberName :: MemberName+getMemberName = fromString "Get"++setMemberName :: MemberName+setMemberName = fromString "Set"+++-- Miscellaneous++maybeToEither :: b -> Maybe a -> Either b a+maybeToEither = flip maybe Right . Left+ atomicModifyIORef_ :: IORef a -> (a -> a) -> IO ()-atomicModifyIORef_ ref fn = atomicModifyIORef ref (\x -> (fn x, ()))+atomicModifyIORef_ ref fn = atomicModifyIORef ref (fn &&& const ())  #if !MIN_VERSION_base(4,6,0) atomicWriteIORef :: IORef a -> a -> IO ()-atomicWriteIORef ref x = atomicModifyIORef ref (\_ -> (x, ()))+atomicWriteIORef ref x = atomicModifyIORef ref $ const x &&& const () #endif
+ lib/DBus/Generation.hs view
@@ -0,0 +1,544 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}+module DBus.Generation where++import           Control.Monad.Trans.Class+import           Control.Monad.Trans.Reader+import           DBus.Client as C+import qualified DBus.Internal.Message as M+import qualified DBus.Internal.Types as T+import qualified DBus.Introspection as I+import qualified Data.ByteString as BS+import qualified Data.Char as Char+import           Data.Coerce+import           Data.Functor ((<$>))+import           Data.Int+import           Data.List+import qualified Data.Map as Map+import           Data.Maybe+import           Data.Monoid+import           Data.String+import           Data.Traversable+import           Data.Word+import           Language.Haskell.TH+import           Prelude hiding (mapM)+import           System.IO.Unsafe+import           System.Posix.Types (Fd(..))++type ClientBusPathR a = ReaderT (Client, T.BusName, T.ObjectPath) IO a++dbusInvoke :: (Client -> T.BusName -> T.ObjectPath -> a) -> ClientBusPathR a+dbusInvoke fn = (\(c, b, p) -> fn c b p) <$> ask++-- Use these operators together with dbusInvoke to invoke functions of the form+-- Client -> T.BusName -> T.ObjectPath+infixl 4 ??+(??) :: Functor f => f (a -> b) -> a -> f b+fab ?? a = fmap ($ a) fab+{-# INLINE (??) #-}++infixl 4 ?/?+(?/?) :: ClientBusPathR (a -> IO b) -> a -> ClientBusPathR b+soFar ?/? arg = do+  returnValue <- fmap ($ arg) soFar+  lift returnValue++data GenerationParams = GenerationParams+  { genBusName :: Maybe T.BusName+  , genObjectPath :: Maybe T.ObjectPath+  , genInterfaceName :: T.InterfaceName+  , genTakeSignalErrorHandler :: Bool+  , getTHType :: T.Type -> Type+  }++defaultGetDictType :: Type -> Type -> Type+defaultGetDictType k =+  AppT (AppT (ConT ''Map.Map) k)++defaultGetTHType :: T.Type -> Type+defaultGetTHType = buildGetTHType (AppT ListT) defaultGetDictType++buildGetTHType ::+  (Type -> Type) -> (Type -> Type -> Type) -> T.Type -> Type+buildGetTHType arrayTypeBuilder dictTypeBuilder = fn+  where fn t =+          case t of+            -- Because of a quirk in how we unmarshal things, we currently HAVE+            -- to decorde arrays of Word8 in this way.+            T.TypeArray T.TypeWord8 -> ConT ''BS.ByteString+            T.TypeBoolean -> ConT ''Bool+            T.TypeWord8 -> ConT ''Word8+            T.TypeWord16 -> ConT ''Word16+            T.TypeWord32 -> ConT ''Word32+            T.TypeWord64 -> ConT ''Word64+            T.TypeInt16 -> ConT ''Int16+            T.TypeInt32 -> ConT ''Int32+            T.TypeInt64 -> ConT ''Int64+            T.TypeDouble -> ConT ''Double+            T.TypeUnixFd -> ConT ''Fd+            T.TypeString -> ConT ''String+            T.TypeSignature -> ConT ''T.Signature+            T.TypeObjectPath -> ConT ''T.ObjectPath+            T.TypeVariant -> ConT ''T.Variant+            T.TypeArray arrayType -> arrayTypeBuilder $ fn arrayType+            T.TypeDictionary k v -> dictTypeBuilder (fn k) (fn v)+            T.TypeStructure ts -> foldl AppT (TupleT $ length ts) $ map fn ts++newNameDef :: String -> Q Name+newNameDef n =+  case n of+    "" -> newName "arg"+    "data" -> newName "arg"+    _ -> newName n++defaultGenerationParams :: GenerationParams+defaultGenerationParams =+  GenerationParams+  { genBusName = Nothing+  , genInterfaceName = fromString ""+  , getTHType = defaultGetTHType+  , genObjectPath = Nothing+  , genTakeSignalErrorHandler = False+  }++addTypeArg :: Type -> Type -> Type+addTypeArg argT = AppT (AppT ArrowT argT)++addTypeArgIf :: Bool -> Type -> Type -> Type+addTypeArgIf condition theType = if condition then addTypeArg theType else id++unitIOType :: Type+unitIOType = AppT (ConT ''IO) (TupleT 0)++addArgIf :: Bool -> a -> [a] -> [a]+addArgIf condition name = if condition then (name:) else id++mkFunD :: Name -> [Name] -> Exp -> Dec+mkFunD name argNames body =+  FunD name [Clause (map VarP argNames) (NormalB body) []]++generateClient :: GenerationParams -> I.Interface -> Q [Dec]+generateClient params+               I.Interface{ I.interfaceName = name+                          , I.interfaceProperties = properties+                          , I.interfaceMethods = methods+                          } =+  let params' = params { genInterfaceName = coerce name } in+  fmap concat <$> sequenceA $+                  map (generateClientMethod params') methods+                  +++                  map (generateClientProperty params') properties++maybeName :: a -> Bool -> Maybe a+maybeName name condition = if condition then Just name else Nothing++makeToVariantApp :: Name -> Exp+makeToVariantApp name = AppE (VarE 'T.toVariant) $ VarE name++makeFromVariantApp :: Name -> Exp+makeFromVariantApp name = AppE (VarE 'T.fromVariant) $ VarE name++makeJustPattern :: Name -> Pat+makeJustPattern name = ConP 'Just [VarP name]++mapOrHead ::+  (Num a, Eq a) => a -> (t -> b) -> [t] -> ([b] -> b) -> b+mapOrHead outputLength fn names cons =+  case outputLength of+    1 -> fn $ head names+    _ -> cons $ map fn names++runGetFirst :: [Maybe a] -> Maybe a+runGetFirst options = getFirst $  mconcat $ map First options++buildGeneratedSignature :: Bool -> Bool -> Type -> Type+buildGeneratedSignature takeBusArg takeObjectPathArg =+  addTypeArg (ConT ''C.Client) . addTypeArgIf takeBusArg (ConT ''T.BusName) .+  addTypeArgIf takeObjectPathArg (ConT ''T.ObjectPath)++getSetMethodCallParams ::+  Name -> Maybe Name -> Maybe Name -> ExpQ -> ExpQ+getSetMethodCallParams methodCallN mBusN mObjectPathN variantsE =+  case (mBusN, mObjectPathN) of+    (Just busN, Just objectPathN) -> [|+                       $( varE methodCallN )+                          { M.methodCallDestination = Just $( varE busN )+                          , M.methodCallPath = $( varE objectPathN )+                          , M.methodCallBody = $( variantsE )+                          }+                     |]+    (Just busN, Nothing) -> [|+                        $( varE methodCallN )+                          { M.methodCallDestination = Just $( varE busN )+                          , M.methodCallBody = $( variantsE )+                          }+                      |]+    (Nothing, Just objectPathN) -> [|+                        $( varE methodCallN )+                          { M.methodCallPath = $( varE objectPathN )+                          , M.methodCallBody = $( variantsE )+                          }+                      |]+    (Nothing, Nothing) -> [|+                         $( varE methodCallN ) { M.methodCallBody = $( variantsE ) }+                      |]++clientArgumentUnpackingMessage :: String+clientArgumentUnpackingMessage =+  "The client method could not unpack the message that was received."++clientArgumentUnpackingError :: [T.Variant] -> M.MethodError+clientArgumentUnpackingError variants =+  M.MethodError+  { M.methodErrorName = C.errorFailed+  , M.methodErrorSerial = T.Serial 0+  , M.methodErrorSender = Nothing+  , M.methodErrorDestination = Nothing+  , M.methodErrorBody = T.toVariant clientArgumentUnpackingMessage : variants+  }++generateClientMethod :: GenerationParams -> I.Method -> Q [Dec]+generateClientMethod GenerationParams+                       { getTHType = getArgType+                       , genInterfaceName = methodInterface+                       , genObjectPath = objectPathM+                       , genBusName = busNameM+                       }+                     I.Method+                       { I.methodArgs = args+                       , I.methodName = methodNameMN+                       } =+  do+    let (inputArgs, outputArgs) = partition ((== I.In) . I.methodArgDirection) args+        outputLength = length outputArgs+        buildArgNames = mapM (newNameDef . I.methodArgName) inputArgs+        buildOutputNames = mapM (newNameDef . I.methodArgName) outputArgs+        takeBusArg = isNothing busNameM+        takeObjectPathArg = isNothing objectPathM+        functionNameFirst:functionNameRest = coerce methodNameMN+        functionName = Char.toLower functionNameFirst:functionNameRest+        functionN = mkName $ Char.toLower functionNameFirst:functionNameRest+        methodCallDefN = mkName $ functionName ++ "MethodCall"+        defObjectPath = fromMaybe (fromString "/") objectPathM+    clientN <- newName "client"+    busN <- newName "busName"+    objectPathN <- newName "objectPath"+    methodCallN <- newName "methodCall"+    callResultN <- newName "callResult"+    replySuccessN <- newName "replySuccess"+    methodArgNames <- buildArgNames+    fromVariantOutputNames <- buildOutputNames+    finalOutputNames <- buildOutputNames+    let variantListExp = map makeToVariantApp methodArgNames+        mapOrHead' = mapOrHead outputLength+        fromVariantExp = mapOrHead' makeFromVariantApp fromVariantOutputNames TupE+        finalResultTuple = mapOrHead' VarE finalOutputNames TupE+        maybeExtractionPattern = mapOrHead' makeJustPattern finalOutputNames TupP+        getMethodCallDefDec = [d|+               $( varP methodCallDefN ) =+                 M.MethodCall { M.methodCallPath = defObjectPath+                              , M.methodCallInterface = Just methodInterface+                              , M.methodCallMember = methodNameMN+                              , M.methodCallDestination = busNameM+                              , M.methodCallSender = Nothing+                              , M.methodCallReplyExpected = True+                              , M.methodCallAutoStart = True+                              , M.methodCallBody = []+                              }+                 |]+        setMethodCallParamsE = getSetMethodCallParams methodCallDefN+                               (maybeName busN takeBusArg)+                               (maybeName objectPathN takeObjectPathArg)+                               (return $ ListE variantListExp)+        handleReplySuccess =+          if outputLength == 0+          then+            [| Right () |]+          else+            [|+               case M.methodReturnBody $( varE replySuccessN ) of+                     $( return $ ListP $ map VarP fromVariantOutputNames ) ->+                       case $( return fromVariantExp ) of+                         $( return maybeExtractionPattern ) -> Right $( return finalResultTuple )+                         _ -> Left $ clientArgumentUnpackingError $+                              M.methodReturnBody $( varE replySuccessN )+                     _ -> Left $ clientArgumentUnpackingError $+                          M.methodReturnBody $( varE replySuccessN )+             |]+        getFunctionBody = [|+             do+               let $( varP methodCallN ) = $( setMethodCallParamsE )+               $( varP callResultN ) <- call $( return $ VarE clientN ) $( varE methodCallN )+               return $ case $( varE callResultN ) of+                 Right $( return rightPattern  ) -> $( handleReplySuccess )+                 Left e -> Left e+               |]+                    where rightPattern = if outputLength == 0+                                         then WildP+                                         else VarP replySuccessN+    functionBody <- getFunctionBody+    methodCallDef <- getMethodCallDefDec+    let methodSignature = foldr addInArg fullOutputSignature inputArgs+        addInArg arg = addTypeArg $ getArgType $ I.methodArgType arg+        fullOutputSignature = AppT (ConT ''IO) $+                              AppT (AppT (ConT ''Either)+                                         (ConT ''M.MethodError))+                              outputSignature+        outputSignature =+          case outputLength of+            1 -> getArgType $ I.methodArgType $ head outputArgs+            _ -> foldl addOutArg (TupleT outputLength) outputArgs+        addOutArg target arg = AppT target $ getArgType $ I.methodArgType arg+        fullSignature = buildGeneratedSignature takeBusArg takeObjectPathArg methodSignature+        fullArgNames =+          clientN:addArgIf takeBusArg busN+                   (addArgIf takeObjectPathArg objectPathN methodArgNames)+        definitionDec = SigD functionN fullSignature+        function = mkFunD functionN fullArgNames functionBody+        methodCallSignature = SigD methodCallDefN $ ConT ''M.MethodCall+    return $ methodCallSignature:methodCallDef ++ [definitionDec, function]++generateClientProperty :: GenerationParams -> I.Property -> Q [Dec]+generateClientProperty GenerationParams+                         { getTHType = getArgType+                         , genInterfaceName = propertyInterface+                         , genObjectPath = objectPathM+                         , genBusName = busNameM+                         }+                       I.Property+                         { I.propertyName = name+                         , I.propertyType = propType+                         , I.propertyRead = readable+                         , I.propertyWrite = writable+                         } =+  do+    clientN <- newName "client"+    busN <- newName "busName"+    objectPathN <- newName "objectPath"+    methodCallN <- newName "methodCall"+    argN <- newName "arg"+    let takeBusArg = isNothing busNameM+        takeObjectPathArg = isNothing objectPathM+        defObjectPath = fromMaybe (fromString "/") objectPathM+        methodCallDefN = mkName $ "methodCallFor" ++ name+        getMethodCallDefDec = [d|+               $( varP methodCallDefN ) =+                 M.MethodCall { M.methodCallPath = defObjectPath+                              , M.methodCallInterface = Just propertyInterface+                              , M.methodCallMember = fromString name+                              , M.methodCallDestination = busNameM+                              , M.methodCallSender = Nothing+                              , M.methodCallReplyExpected = True+                              , M.methodCallAutoStart = True+                              , M.methodCallBody = []+                              }+                 |]+        setMethodCallParamsE = getSetMethodCallParams methodCallDefN+                                   (maybeName busN takeBusArg)+                                   (maybeName objectPathN takeObjectPathArg)+                                   (return $ ListE [])+        makeGetterBody = [|+          do+            let $( varP methodCallN ) = $( setMethodCallParamsE )+            getPropertyValue $( return $ VarE clientN )+                             $( varE methodCallN )+          |]+        makeSetterBody = [|+          do+            let $( varP methodCallN ) = $( setMethodCallParamsE )+            setPropertyValue $( varE clientN ) $( varE methodCallN ) $( varE argN )+          |]+    methodCallDefs <- getMethodCallDefDec+    getterBody <- makeGetterBody+    setterBody <- makeSetterBody+    let buildSignature = buildGeneratedSignature takeBusArg takeObjectPathArg+        getterSigType =+          buildSignature $ AppT (ConT ''IO) $+                         AppT (AppT (ConT ''Either)+                                      (ConT ''M.MethodError)) $ getArgType propType+        setterSigType = buildSignature $+                        AppT (ConT ''IO) $ AppT (ConT ''Maybe) (ConT ''M.MethodError)+        buildArgs rest = clientN:addArgIf takeBusArg busN+                         (addArgIf takeObjectPathArg objectPathN rest)+        getterArgNames = buildArgs []+        setterArgNames = buildArgs [argN]+        propertyString = coerce name+        getterName = mkName $ "get" ++ propertyString+        setterName = mkName $ "set" ++ propertyString+        getterFunction = mkFunD getterName getterArgNames getterBody+        setterFunction = mkFunD setterName setterArgNames setterBody+        getterSignature = SigD getterName getterSigType+        setterSignature = SigD setterName setterSigType+        getterDefs = if readable then [getterSignature, getterFunction] else []+        setterDefs = if writable then [setterSignature, setterFunction] else []+        methodCallSignature = SigD methodCallDefN $ ConT ''M.MethodCall+    return $ methodCallSignature:methodCallDefs ++ getterDefs ++ setterDefs++generateSignalsFromInterface :: GenerationParams -> I.Interface -> Q [Dec]+generateSignalsFromInterface params+                             I.Interface{ I.interfaceName = name+                                        , I.interfaceSignals = signals+                                        } = generateSignals params name signals++generateSignals :: GenerationParams -> T.InterfaceName -> [I.Signal] -> Q [Dec]+generateSignals params name signals =+  fmap concat <$> sequenceA $+                map (generateSignal params { genInterfaceName = coerce name })+                    signals++generateSignal :: GenerationParams -> I.Signal -> Q [Dec]+generateSignal GenerationParams+                 { getTHType = getArgType+                 , genInterfaceName = signalInterface+                 , genObjectPath = objectPathM+                 , genBusName = busNameM+                 , genTakeSignalErrorHandler = takeErrorHandler+                 }+               I.Signal+                 { I.signalName = name+                 , I.signalArgs = args+                 } =+  do+    let buildArgNames = mapM (newNameDef . I.signalArgName) args++    argNames <- buildArgNames+    fromVariantOutputNames <- buildArgNames+    toHandlerOutputNames <- buildArgNames+    objectPathN <- newName "objectPath"+    variantsN <- newName "variants"+    signalN <- newName "signal"+    receivedSignalN <- newName "signal"+    clientN <- newName "client"+    handlerArgN <- newName "handlerArg"+    errorHandlerN <- newName "errorHandler"+    matchRuleN <- newName "matchRule"+    matchRuleArgN <- newName "matchRuleArg"++    let variantListExp = map makeToVariantApp argNames+        signalString = coerce name+        signalDefN = mkName $ "signalFor" ++ signalString+        takeObjectPathArg = isNothing objectPathM+        defObjectPath = fromMaybe (fromString "/") objectPathM+        argCount = length argNames+        getSignalDefDec = [d|+          $( varP signalDefN ) =+            M.Signal { M.signalPath = defObjectPath+                     , M.signalInterface = signalInterface+                     , M.signalMember = name+                     , M.signalDestination = Nothing+                     , M.signalSender = Nothing+                     , M.signalBody = []+                     }+                 |]+    let mapOrHead' = mapOrHead argCount+        fromVariantExp = mapOrHead' makeFromVariantApp fromVariantOutputNames TupE+        maybeExtractionPattern = mapOrHead' makeJustPattern toHandlerOutputNames TupP+        applyToName toApply n = AppE toApply $ VarE n+        finalApplication = foldl applyToName (VarE handlerArgN)+                           (receivedSignalN:toHandlerOutputNames)+        makeHandlerN = mkName $ "makeHandlerFor" ++ signalString+        makeHandlerCall =+          if takeErrorHandler+          then AppE base (VarE errorHandlerN)+          else base+            where base = AppE (VarE makeHandlerN) (VarE handlerArgN)+        getSetSignal  =+          if takeObjectPathArg+          then [|+                  $( varE signalDefN )+                     { M.signalPath = $( varE objectPathN )+                     , M.signalBody = $( varE variantsN )+                     }+                 |]+          else [| $( varE signalDefN )+                  { M.signalBody = $( varE variantsN ) }+                |]+        getEmitBody = [|+          let $( varP variantsN ) = $( return $ ListE variantListExp )+              $( varP signalN ) = $( getSetSignal )+          in+            emit $( varE clientN ) $( varE signalN )+          |]+        getErrorHandler =+          if takeErrorHandler then+            [| $( varE errorHandlerN  ) $( varE receivedSignalN )|]+          else [| return () |]+        getMakeHandlerBody =+          if argCount == 0+          then+            [| $( return finalApplication ) |]+          else+            [|+               case M.signalBody $( varE receivedSignalN ) of+                 $( return $ ListP $ map VarP fromVariantOutputNames ) ->+                   case $( return fromVariantExp ) of+                     $( return maybeExtractionPattern ) -> $( return finalApplication )+                     _ -> $( getErrorHandler )+                 _ -> $( getErrorHandler )+                   |]+        getRegisterBody = [|+          let $( varP matchRuleN ) = $( varE matchRuleArgN )+                                       { C.matchInterface = Just signalInterface+                                       , C.matchMember = Just name+                                       , C.matchSender =+                                         runGetFirst+                                         [ C.matchSender $( varE matchRuleArgN )+                                         , busNameM+                                         ]+                                       , C.matchPath =+                                         runGetFirst+                                         [ C.matchPath $( varE matchRuleArgN )+                                         , objectPathM+                                         ]+                                       }+          in+            C.addMatch $( varE clientN ) $( varE matchRuleN ) $ $( return makeHandlerCall )+            |]+    registerBody <- getRegisterBody+    makeHandlerBody <- getMakeHandlerBody+    signalDef <- getSignalDefDec+    emitBody <- getEmitBody+    let methodSignature = foldr addInArg unitIOType args+        addInArg arg = addTypeArg $ getArgType $ I.signalArgType arg+        fullArgNames = clientN:addArgIf takeObjectPathArg objectPathN argNames+        -- Never take bus arg because it is set automatically anyway+        fullSignature =+            buildGeneratedSignature False takeObjectPathArg methodSignature+        functionN = mkName $ "emit" ++ signalString+        emitSignature = SigD functionN fullSignature+        emitFunction = mkFunD functionN fullArgNames emitBody+        handlerType = addTypeArg (ConT ''M.Signal) methodSignature+        errorHandlerType = addTypeArg (ConT ''M.Signal) unitIOType+        registerN = mkName $ "registerFor" ++ signalString+        registerArgs = clientN:matchRuleArgN:handlerArgN:+                       addArgIf takeErrorHandler errorHandlerN []+        registerFunction = mkFunD registerN registerArgs registerBody+        registerType =+          addTypeArg (ConT ''C.Client) $+          addTypeArg (ConT ''C.MatchRule) $+          addTypeArg handlerType $+          addTypeArgIf takeErrorHandler (addTypeArg (ConT ''M.Signal) unitIOType) $+          AppT (ConT ''IO) (ConT ''C.SignalHandler)+        registerSignature = SigD registerN registerType+        makeHandlerArgs =+          handlerArgN:addArgIf takeErrorHandler errorHandlerN [receivedSignalN]+        makeHandlerFunction = mkFunD makeHandlerN makeHandlerArgs makeHandlerBody+        makeHandlerType = addTypeArg handlerType $+                          addTypeArgIf takeErrorHandler errorHandlerType $+                          addTypeArg (ConT ''M.Signal) unitIOType+        makeHandlerSignature = SigD makeHandlerN makeHandlerType+        signalSignature = SigD signalDefN (ConT ''M.Signal)+    return $ signalSignature:+           signalDef ++ [ emitSignature, emitFunction+                        , makeHandlerSignature, makeHandlerFunction+                        , registerSignature, registerFunction+                        ]++generateFromFilePath :: GenerationParams -> FilePath -> Q [Dec]+generateFromFilePath generationParams filepath =+  let obj = unsafePerformIO $+            head . maybeToList . I.parseXML "/" <$> readFile filepath+      interface = head $ I.objectInterfaces obj+      signals = generateSignalsFromInterface generationParams interface+      client = generateClient generationParams interface+  in fmap (++) signals <*> client
lib/DBus/Internal/Types.hs view
@@ -4,6 +4,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE IncoherentInstances #-}+{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeSynonymInstances #-}  -- Copyright (C) 2009-2012 John Millikin <john@john-millikin.com>@@ -23,32 +24,35 @@  module DBus.Internal.Types where -import Control.DeepSeq-import Control.Exception (Exception, handle, throwIO)-import Control.Monad (liftM, when, (>=>))-import Data.ByteString (ByteString)-import Data.Char (ord)-import Data.Int-import Data.List (intercalate)-import Data.Map (Map)-import Data.Text (Text)-import Data.Typeable (Typeable)-import Data.Vector (Vector)-import Data.Word-import GHC.Generics-import System.IO.Unsafe (unsafePerformIO)-import System.Posix.Types (Fd)-import Text.ParserCombinators.Parsec ((<|>), oneOf)+import           Control.DeepSeq+import           Control.Exception (Exception, handle, throwIO)+import           Control.Monad (liftM, when, (>=>)) import qualified Data.ByteString+import           Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as Char8 import qualified Data.ByteString.Lazy import qualified Data.ByteString.Unsafe+import           Data.Char (ord)+import           Data.Coerce+import           Data.Int+import           Data.List (intercalate)+import           Data.List.Split (splitOn) import qualified Data.Map+import           Data.Map (Map) import qualified Data.String import qualified Data.Text+import           Data.Text (Text) import qualified Data.Text.Lazy+import           Data.Typeable (Typeable) import qualified Data.Vector+import           Data.Vector (Vector)+import           Data.Word import qualified Foreign+import           GHC.Generics+import qualified Language.Haskell.TH.Lift as THL+import           System.IO.Unsafe (unsafePerformIO)+import           System.Posix.Types (Fd)+import           Text.ParserCombinators.Parsec ((<|>), oneOf) import qualified Text.ParserCombinators.Parsec as Parsec  data Type@@ -320,6 +324,10 @@                             return (ii' + 1, TypeDictionary t1 t2)                         else throwIO SigParseError +extractFromVariant :: IsValue a => Variant -> Maybe a+extractFromVariant (Variant (ValueVariant v)) = extractFromVariant v+extractFromVariant v = fromVariant v+ class IsVariant a where     toVariant :: a -> Variant     fromVariant :: Variant -> Maybe a@@ -602,6 +610,17 @@     toVariant = Variant . toValue     fromVariant (Variant val) = fromValue val +instance IsValue () where+  typeOf _ = TypeStructure []+  toValue _ = ValueStructure []+  fromValue (ValueStructure []) = return ()+  fromValue _ = Nothing++instance IsVariant () where+  toVariant () = Variant (ValueStructure [])+  fromVariant (Variant (ValueStructure [])) = Just ()+  fromVariant _ = Nothing+ instance (IsValue a1, IsValue a2) => IsValue (a1, a2) where     typeOf ~(a1, a2) = TypeStructure [typeOf a1, typeOf a2]     toValue (a1, a2) = ValueStructure [toValue a1, toValue a2]@@ -635,6 +654,12 @@ newtype ObjectPath = ObjectPath String     deriving (Eq, Ord, Show, NFData) +pathElements :: ObjectPath -> [String]+pathElements = filter (not . null) . splitOn "/" . coerce++fromElements :: [String] -> ObjectPath+fromElements elems = objectPath_ $ '/':intercalate "/" elems+ formatObjectPath :: ObjectPath -> String formatObjectPath (ObjectPath s) = s @@ -1344,3 +1369,5 @@ maybeParseString parser s = case Parsec.parse parser "" s of     Left _ -> Nothing     Right a -> Just a++THL.deriveLiftMany [''BusName, ''ObjectPath, ''InterfaceName, ''MemberName]
lib/DBus/Introspection.hs view
@@ -20,58 +20,14 @@     -- * XML conversion       parseXML     , formatXML--    -- * Objects-    , Object-    , object-    , objectPath-    , objectInterfaces-    , objectChildren--    -- * Interfaces-    , Interface-    , interface-    , interfaceName-    , interfaceMethods-    , interfaceSignals-    , interfaceProperties--    -- * Methods-    , Method-    , method-    , methodName-    , methodArgs--    -- ** Method arguments-    , MethodArg-    , methodArg-    , methodArgName-    , methodArgType-    , methodArgDirection--    , Direction-    , directionIn-    , directionOut--    -- * Signals-    , Signal-    , signal-    , signalName-    , signalArgs--    -- ** Signal arguments-    , SignalArg-    , signalArg-    , signalArgName-    , signalArgType--    -- * Properties-    , Property-    , property-    , propertyName-    , propertyType-    , propertyRead-    , propertyWrite+    , Object(..)+    , Interface(..)+    , Method(..)+    , MethodArg(..)+    , Direction(..)+    , Signal(..)+    , SignalArg(..)+    , Property(..)     ) where  import qualified Control.Applicative@@ -94,9 +50,6 @@     }     deriving (Show, Eq) -object :: T.ObjectPath -> Object-object path = Object path [] []- data Interface = Interface     { interfaceName :: T.InterfaceName     , interfaceMethods :: [Method]@@ -105,18 +58,12 @@     }     deriving (Show, Eq) -interface :: T.InterfaceName -> Interface-interface name = Interface name [] [] []- data Method = Method     { methodName :: T.MemberName     , methodArgs :: [MethodArg]     }     deriving (Show, Eq) -method :: T.MemberName -> Method-method name = Method name []- data MethodArg = MethodArg     { methodArgName :: String     , methodArgType :: T.Type@@ -124,36 +71,21 @@     }     deriving (Show, Eq) -methodArg :: String -> T.Type -> Direction -> MethodArg-methodArg = MethodArg- data Direction = In | Out     deriving (Show, Eq) -directionIn :: Direction-directionIn = In--directionOut :: Direction-directionOut = Out- data Signal = Signal     { signalName :: T.MemberName     , signalArgs :: [SignalArg]     }     deriving (Show, Eq) -signal :: T.MemberName -> Signal-signal name = Signal name []- data SignalArg = SignalArg     { signalArgName :: String     , signalArgType :: T.Type     }     deriving (Show, Eq) -signalArg :: String -> T.Type -> SignalArg-signalArg = SignalArg- data Property = Property     { propertyName :: String     , propertyType :: T.Type@@ -162,9 +94,6 @@     }     deriving (Show, Eq) -property :: String -> T.Type -> Property-property name t = Property name t False False- parseXML :: T.ObjectPath -> String -> Maybe Object parseXML path xml = do     root <- parseElement (Data.Text.pack xml)@@ -343,12 +272,12 @@  writeMethod :: Method -> XmlWriter () writeMethod (Method name args) = writeElement "method"-    [("name", T.formatMemberName name)] $ do+    [("name", T.formatMemberName name)] $         mapM_ writeMethodArg args  writeSignal :: Signal -> XmlWriter () writeSignal (Signal name args) = writeElement "signal"-    [("name", T.formatMemberName name)] $ do+    [("name", T.formatMemberName name)] $         mapM_ writeSignalArg args  formatType :: T.Type -> XmlWriter String@@ -364,7 +293,7 @@     let dirAttr = case dir of             In -> "in"             Out -> "out"-    writeEmptyElement "arg" $+    writeEmptyElement "arg"         [ ("name", name)         , ("type", typeStr)         , ("direction", dirAttr)@@ -373,7 +302,7 @@ writeSignalArg :: SignalArg -> XmlWriter () writeSignalArg (SignalArg name t) = do     typeStr <- formatType t-    writeEmptyElement "arg" $+    writeEmptyElement "arg"         [ ("name", name)         , ("type", typeStr)         ]
+ lib/DBus/TH.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}+module DBus.TH where++import           DBus.Client+import           DBus.Generation+import           System.FilePath+++generateSignalsFromInterface defaultGenerationParams $+                             buildIntrospectionInterface $+                             buildPropertiesInterface undefined++generateFromFilePath defaultGenerationParams { genBusName = Just dbusName+                                             , genObjectPath = Just dbusPath+                                             } $ "idlxml" </> "dbus.xml"
tests/DBusTests/Client.hs view
@@ -18,6 +18,7 @@ import Control.Concurrent import Control.Exception (try) import Data.Word+import Data.Int import Test.Tasty import Test.Tasty.HUnit import qualified Data.Map as Map@@ -25,14 +26,26 @@ import DBus import qualified DBus.Client import qualified DBus.Socket+import DBus.Introspection+import DBus.Internal.Types  import DBusTests.Util+import qualified DBusTests.TH as TH+import qualified DBusTests.Generation as G +doExport :: DBus.Client.Client -> String -> String -> [DBus.Client.Method] -> IO ()+doExport client path name methods =+  DBus.Client.export client (objectPath_ path) DBus.Client.defaultInterface+        { DBus.Client.interfaceMethods = methods+        , DBus.Client.interfaceName = interfaceName_ name+        }+ test_Client :: TestTree test_Client = testGroup "Client" $     [ test_RequestName     , test_ReleaseName     , test_Call+    , test_CallWithGeneration     , test_CallNoReply     , test_AddMatch     , test_AutoMethod@@ -277,6 +290,31 @@          reply @?= methodReturn (methodReturnSerial reply) +test_CallWithGeneration :: TestTree+test_CallWithGeneration = withConnectedClient $ \res -> testCase "callGeneration" $ do+    (sock, client) <- res+    let string = "test"+        busName = busName_ "org.freeDesktop.DBus"+        int = 32 :: Int32+        path = objectPath_ "/a/b/c"+        returnValue = Map.fromList [(string, int)]++    DBus.Client.export client path G.testInterface++    do+        response <- stubMethodCall sock+            (TH.sampleMethod1 client busName path string int)+            (methodCall path (DBus.Client.interfaceName G.testInterface) $+                        memberName_ "SampleMethod1")+             { methodCallDestination = Just busName+             , methodCallBody = [toVariant string, toVariant int]+             }+            (\x -> (methodReturn x)+                   { methodReturnBody = [toVariant returnValue]})+        reply <- requireRight response++        reply @?= returnValue+ test_CallNoReply :: TestTree test_CallNoReply = withConnectedClient $ \res -> testCase "callNoReply" $ do     (sock, client) <- res@@ -346,9 +384,9 @@      let methodPair = (\x y -> return (x, y)) :: String -> String -> IO (String, String) -    DBus.Client.export client (objectPath_ "/")-        [ DBus.Client.autoMethod (interfaceName_ "com.example.Foo") (memberName_ "Max") methodMax-        , DBus.Client.autoMethod (interfaceName_ "com.example.Foo") (memberName_ "Pair") methodPair+    doExport client "/" "com.example.Foo"+        [ DBus.Client.autoMethod (memberName_ "Max") methodMax+        , DBus.Client.autoMethod (memberName_ "Pair") methodPair         ]      -- valid call to com.example.Foo.Max@@ -371,62 +409,207 @@         response @?= Left (methodError serial (errorName_ "org.freedesktop.DBus.Error.InvalidParameters"))  test_ExportIntrospection :: TestTree-test_ExportIntrospection = withConnectedClient $ \res -> testCase "exportIntrospection" $ do-    (sock, client) <- res-    DBus.Client.export client (objectPath_ "/foo")-        [ DBus.Client.autoMethod (interfaceName_ "com.example.Foo") (memberName_ "Method1")-          (undefined :: String -> IO ())-        , DBus.Client.autoMethod (interfaceName_ "com.example.Foo") (memberName_ "Method2")-          (undefined :: String -> IO String)-        , DBus.Client.autoMethod (interfaceName_ "com.example.Foo") (memberName_ "Method3")-          (undefined :: String -> IO (String, String))-        ]--    let introspect path = do-        (_, response) <- callClientMethod sock path "org.freedesktop.DBus.Introspectable" "Introspect" []-        ret <- requireRight response-        let body = methodReturnBody ret--        length body @?= 1-        let Just xml = fromVariant (head body)-        return xml--    root <- introspect "/"-    root @?=-        "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN' 'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>\n\-        \<node name='/'>\-            \<interface name='org.freedesktop.DBus.Introspectable'>\-                \<method name='Introspect'>\-                    \<arg name='' type='s' direction='out'/>\-                \</method>\-            \</interface>\-            \<node name='foo'></node>\-        \</node>"--    foo <- introspect "/foo"-    foo @?=-        "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN' 'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>\n\-        \<node name='/foo'>\-            \<interface name='com.example.Foo'>\-                \<method name='Method1'>\-                    \<arg name='' type='s' direction='in'/>\-                \</method>\-                \<method name='Method2'>\-                    \<arg name='' type='s' direction='in'/>\-                    \<arg name='' type='s' direction='out'/>\-                \</method>\-                \<method name='Method3'>\-                    \<arg name='' type='s' direction='in'/>\-                    \<arg name='' type='s' direction='out'/>\-                    \<arg name='' type='s' direction='out'/>\-                \</method>\-            \</interface>\-            \<interface name='org.freedesktop.DBus.Introspectable'>\-                \<method name='Introspect'>\-                    \<arg name='' type='s' direction='out'/>\-                \</method>\-            \</interface>\-        \</node>"+test_ExportIntrospection =+  withConnectedClient $ \res ->+    testCase "exportIntrospection" $ do+      (sock, client) <- res+      let interface =+            DBus.Client.defaultInterface+            { DBus.Client.interfaceMethods =+                [ DBus.Client.autoMethod+                    (memberName_ "Method1")+                    (undefined :: String -> IO ())+                , DBus.Client.autoMethod+                    (memberName_ "Method2")+                    (undefined :: String -> IO String)+                , DBus.Client.autoMethod+                    (memberName_ "Method3")+                    (undefined :: String -> IO (String, String))+                ]+            , DBus.Client.interfaceName = interfaceName_ "com.example.Echo"+            }+          expectedIntrospectionInterface =+            Object+            { objectPath = ObjectPath "/foo"+            , objectInterfaces =+                [ Interface+                  { interfaceName =+                      InterfaceName "org.freedesktop.DBus.Properties"+                  , interfaceMethods =+                      [ Method+                        { methodName = MemberName "Get"+                        , methodArgs =+                            [ MethodArg+                              { methodArgName = "a"+                              , methodArgType = TypeString+                              , methodArgDirection = In+                              }+                            , MethodArg+                              { methodArgName = "b"+                              , methodArgType = TypeString+                              , methodArgDirection = In+                              }+                            , MethodArg+                              { methodArgName = "c"+                              , methodArgType = TypeVariant+                              , methodArgDirection = Out+                              }+                            ]+                        }+                      , Method+                        { methodName = MemberName "GetAll"+                        , methodArgs =+                            [ MethodArg+                              { methodArgName = "a"+                              , methodArgType = TypeString+                              , methodArgDirection = In+                              }+                            , MethodArg+                              { methodArgName = "b"+                              , methodArgType = TypeDictionary TypeString TypeVariant+                              , methodArgDirection = Out+                              }+                            ]+                        }+                      , Method+                        { methodName = MemberName "Set"+                        , methodArgs =+                            [ MethodArg+                              { methodArgName = "a"+                              , methodArgType = TypeString+                              , methodArgDirection = In+                              }+                            , MethodArg+                              { methodArgName = "b"+                              , methodArgType = TypeString+                              , methodArgDirection = In+                              }+                            , MethodArg+                              { methodArgName = "c"+                              , methodArgType = TypeVariant+                              , methodArgDirection = In+                              }+                            ]+                        }+                      ]+                  , interfaceSignals =+                      [ Signal+                        { signalName = MemberName "PropertiesChanged"+                        , signalArgs =+                            [ SignalArg+                              { signalArgName = "interface_name"+                              , signalArgType = TypeString+                              }+                            , SignalArg+                              { signalArgName = "changed_properties"+                              , signalArgType = TypeDictionary TypeString TypeVariant+                              }+                            , SignalArg+                              { signalArgName = "invalidated_properties"+                              , signalArgType = TypeArray TypeString+                              }+                            ]+                        }+                      ]+                  , interfaceProperties = []+                  }+                , Interface+                  { interfaceName =+                      InterfaceName "org.freedesktop.DBus.Introspectable"+                  , interfaceMethods =+                      [ Method+                        { methodName = MemberName "Introspect"+                        , methodArgs =+                            [ MethodArg+                              { methodArgName = "a"+                              , methodArgType = TypeString+                              , methodArgDirection = Out+                              }+                            ]+                        }+                      ]+                  , interfaceSignals = []+                  , interfaceProperties = []+                  }+                , Interface+                  { interfaceName = InterfaceName "com.example.Echo"+                  , interfaceMethods =+                      [ Method+                        { methodName = MemberName "Method1"+                        , methodArgs =+                            [ MethodArg+                              { methodArgName = "a"+                              , methodArgType = TypeString+                              , methodArgDirection = In+                              }+                            ]+                        }+                      , Method+                        { methodName = MemberName "Method2"+                        , methodArgs =+                            [ MethodArg+                              { methodArgName = "a"+                              , methodArgType = TypeString+                              , methodArgDirection = In+                              }+                            , MethodArg+                              { methodArgName = "b"+                              , methodArgType = TypeString+                              , methodArgDirection = Out+                              }+                            ]+                        }+                      , Method+                        { methodName = MemberName "Method3"+                        , methodArgs =+                            [ MethodArg+                              { methodArgName = "a"+                              , methodArgType = TypeString+                              , methodArgDirection = In+                              }+                            , MethodArg+                              { methodArgName = "b"+                              , methodArgType = TypeString+                              , methodArgDirection = Out+                              }+                            , MethodArg+                              { methodArgName = "c"+                              , methodArgType = TypeString+                              , methodArgDirection = Out+                              }+                            ]+                        }+                      ]+                  , interfaceSignals = []+                  , interfaceProperties = []+                  }+                ]+            , objectChildren = []+            }+      DBus.Client.export client (objectPath_ "/foo") interface+      let introspect path = do+            (_, response) <-+              callClientMethod+                sock+                path+                "org.freedesktop.DBus.Introspectable"+                "Introspect"+                []+            ret <- requireRight response+            let body = methodReturnBody ret+            length body @?= 1+            let Just xml = fromVariant (head body)+            return $ parseXML (objectPath_ "/") xml+      root <- introspect "/"+      root @?=+        Just+          (Object+           { objectPath = ObjectPath "/"+           , objectInterfaces = []+           , objectChildren = [ expectedIntrospectionInterface ]+           })+      foo <- introspect "/foo"+      foo @?= Just expectedIntrospectionInterface  startDummyBus :: IO (Address, MVar DBus.Socket.Socket) startDummyBus = do
+ tests/DBusTests/Generation.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE OverloadedStrings #-}++module DBusTests.Generation where++import           DBus.Client+import qualified DBus.Internal.Types as T+import qualified DBus.Introspection as I+import           Data.Int+import           Data.Map as M++sampleMethod1 :: String -> Int32 -> IO (M.Map String Int32)+sampleMethod1 a b = return $ M.insert a b M.empty++serviceArg :: I.SignalArg+serviceArg = I.SignalArg { I.signalArgName = "service"+                         , I.signalArgType = T.TypeString+                         }++testSignals :: [I.Signal]+testSignals = [ I.Signal { I.signalName = "StatusNotifierItemRegistered"+                         , I.signalArgs = [serviceArg]+                         }+              ]++testInterface :: Interface+testInterface =+  defaultInterface { interfaceMethods =+                       [autoMethod "SampleMethod1" sampleMethod1]+                   , interfaceName = "org.TestInterface"+                   , interfaceSignals = testSignals+                   }++testIntrospectionInterface :: I.Interface+testIntrospectionInterface = buildIntrospectionInterface testInterface
tests/DBusTests/Integration.hs view
@@ -59,11 +59,18 @@     clientB <- connect addr      export clientA "/"-        [ method "com.example.Echo" "Echo" (signature_ [TypeString]) (signature_ []) (-            \msg -> if map variantType (methodCallBody msg) == [TypeString]-                then return (replyReturn (methodCallBody msg))-                else return (replyError "com.example.Error" [toVariant ("bad body: " ++ show (methodCallBody msg))]))-        ]+           defaultInterface+           { interfaceName = "com.example.Echo"+           , interfaceMethods =+               [ Method "Echo" (signature_ [TypeString]) (signature_ []) (+                 \msg -> if map variantType (methodCallBody msg) == [TypeString]+                         then return (ReplyReturn (methodCallBody msg))+                         else+                           return $ ReplyError+                                    "com.example.Error"+                                    [toVariant ("bad body: " ++ show (methodCallBody msg))])+               ]+           }      -- TODO: get bus address of clientA with a function     let busAddrA = ":1.0"
tests/DBusTests/Introspection.hs view
@@ -23,7 +23,7 @@ import Test.Tasty.QuickCheck  import DBus-import qualified DBus.Introspection as Introspection+import qualified DBus.Introspection as I  import DBusTests.InterfaceName () import DBusTests.MemberName ()@@ -41,27 +41,30 @@  test_XmlPassthrough :: TestTree test_XmlPassthrough = testProperty "xml-passthrough" $ \obj -> let-    path = Introspection.objectPath obj-    Just xml = Introspection.formatXML obj-    in Introspection.parseXML path xml == Just obj+    path = I.objectPath obj+    Just xml = I.formatXML obj+    in I.parseXML path xml == Just obj +buildEmptyObject :: String -> I.Object+buildEmptyObject name = I.Object (objectPath_ name) [] []+ test_XmlParse :: TestTree test_XmlParse = testCase "xml-parse" $ do     -- root object path can be inferred-    Introspection.parseXML (objectPath_ "/") "<node><node name='foo'/></node>"-        @?= Just (Introspection.object (objectPath_ "/"))-            { Introspection.objectChildren =-                [ Introspection.object (objectPath_ "/foo")+    I.parseXML (objectPath_ "/") "<node><node name='foo'/></node>"+        @?= Just (buildEmptyObject "/")+            { I.objectChildren =+                [ buildEmptyObject "/foo"                 ]             }  test_XmlParseFailed :: TestTree test_XmlParseFailed = testCase "xml-parse-failed" $ do-    Nothing @=? Introspection.parseXML (objectPath_ "/") "<invalid>"-    Nothing @=? Introspection.parseXML (objectPath_ "/") "<invalid/>"+    Nothing @=? I.parseXML (objectPath_ "/") "<invalid>"+    Nothing @=? I.parseXML (objectPath_ "/") "<invalid/>"      -- invalid property access-    Nothing @=? Introspection.parseXML (objectPath_ "/")+    Nothing @=? I.parseXML (objectPath_ "/")         "<node>\         \  <interface name='com.example.Foo'>\         \    <property type='s' access='invalid'>\@@ -70,7 +73,7 @@         \</node>"      -- invalid parameter type-    Nothing @=? Introspection.parseXML (objectPath_ "/")+    Nothing @=? I.parseXML (objectPath_ "/")         "<node>\         \  <interface name='com.example.Foo'>\         \    <method name='Foo'>\@@ -82,22 +85,17 @@ test_XmlWriteFailed :: TestTree test_XmlWriteFailed = testCase "xml-write-failed" $ do     -- child's object path isn't under parent's-    Nothing @=? Introspection.formatXML (Introspection.object (objectPath_ "/foo"))-        { Introspection.objectChildren =-            [ Introspection.object (objectPath_ "/bar")-            ]+    Nothing @=? I.formatXML (buildEmptyObject "/foo")+        { I.objectChildren =+            [ buildEmptyObject "/bar" ]         }      -- invalid type-    Nothing @=? Introspection.formatXML (Introspection.object (objectPath_ "/foo"))-        { Introspection.objectInterfaces =-            [ (Introspection.interface (interfaceName_ "/bar"))-                { Introspection.interfaceProperties =-                    [ Introspection.property "prop" (TypeDictionary TypeVariant TypeVariant)-                    ]-                }-            ]-        }+    Nothing @=? I.formatXML+            ((buildEmptyObject "/foo")+            { I.objectInterfaces =+                [ I.Interface (interfaceName_ "/bar") [] []+                                [ I.Property "prop" (TypeDictionary TypeVariant TypeVariant) True True ]]})  instance Arbitrary Type where     arbitrary = oneof [atom, container] where@@ -122,10 +120,10 @@             , liftM TypeStructure (listOf1 (halfSized arbitrary))             ] -instance Arbitrary Introspection.Object where+instance Arbitrary I.Object where     arbitrary = arbitrary >>= subObject -subObject :: ObjectPath -> Gen Introspection.Object+subObject :: ObjectPath -> Gen I.Object subObject parentPath = sized $ \n -> resize (min n 4) $ do     let nonRoot = do         x <- resize 10 arbitrary@@ -140,62 +138,53 @@     let path = objectPath_ path'     ifaces <- arbitrary     children <- halfSized (listOf (subObject path))-    return (Introspection.object path)-        { Introspection.objectInterfaces = ifaces-        , Introspection.objectChildren = children-        }+    return $ I.Object path ifaces children -instance Arbitrary Introspection.Interface where+instance Arbitrary I.Interface where     arbitrary = do         name <- arbitrary         methods <- arbitrary         signals <- arbitrary         properties <- arbitrary-        return (Introspection.interface name)-            { Introspection.interfaceMethods = methods-            , Introspection.interfaceSignals = signals-            , Introspection.interfaceProperties = properties-            }+        return $ I.Interface name methods signals properties -instance Arbitrary Introspection.Method where+instance Arbitrary I.Method where     arbitrary = do         name <- arbitrary         args <- arbitrary-        return (Introspection.method name)-            { Introspection.methodArgs = args-            }+        return $ (I.Method name args) -instance Arbitrary Introspection.Signal where+instance Arbitrary I.Signal where     arbitrary = do         name <- arbitrary         args <- arbitrary-        return (Introspection.signal name)-            { Introspection.signalArgs = args-            }+        return $ I.Signal name args -instance Arbitrary Introspection.MethodArg where-    arbitrary = Introspection.methodArg+instance Arbitrary I.MethodArg where+    arbitrary = I.MethodArg         <$> gen_Ascii         <*> arbitrary         <*> arbitrary -instance Arbitrary Introspection.Direction where-    arbitrary = elements [Introspection.directionIn, Introspection.directionOut]+instance Arbitrary I.Direction where+    arbitrary = elements [I.In, I.Out] -instance Arbitrary Introspection.SignalArg where-    arbitrary = Introspection.signalArg+instance Arbitrary I.SignalArg where+    arbitrary = I.SignalArg         <$> gen_Ascii         <*> arbitrary -instance Arbitrary Introspection.Property where+instance Arbitrary I.Property where     arbitrary = do         name <- gen_Ascii         t <- arbitrary         canRead <- arbitrary         canWrite <- arbitrary-        return (Introspection.property name t)-            { Introspection.propertyRead = canRead-            , Introspection.propertyWrite = canWrite+        return I.Property+            { I.propertyName = name+            , I.propertyType = t+            , I.propertyRead = canRead+            , I.propertyWrite = canWrite             }  gen_Ascii :: Gen String
tests/DBusTests/Serialization.hs view
@@ -26,7 +26,6 @@ import System.Posix.Types (Fd) import Test.QuickCheck import Test.Tasty-import Test.Tasty.HUnit import Test.Tasty.QuickCheck import qualified Data.Map import qualified Data.Vector
+ tests/DBusTests/TH.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE TemplateHaskell #-}++module DBusTests.TH where++import DBus.Generation+import DBusTests.Generation++generateClient defaultGenerationParams testIntrospectionInterface+generateSignalsFromInterface defaultGenerationParams testIntrospectionInterface
tests/DBusTests/Transport.hs view
@@ -85,7 +85,7 @@  test_OpenUnix_Path :: TestTree test_OpenUnix_Path = testCase "path" $ runResourceT $ do-    (addr, networkSocket) <- listenRandomUnixPath+    addr <- listenRandomUnixPath     fdcountBefore <- countFileDescriptors      t <- liftIO (transportOpen socketTransportOptions addr)@@ -96,7 +96,7 @@  test_OpenUnix_Abstract :: TestTree test_OpenUnix_Abstract = testCase "abstract" $ runResourceT $ do-    (addr, networkSocket, _) <- listenRandomUnixAbstract+    (addr, _) <- listenRandomUnixAbstract     fdcountBefore <- countFileDescriptors      t <- liftIO (transportOpen socketTransportOptions addr)@@ -140,7 +140,7 @@ test_OpenUnix_NotListening = testCase "not-listening" $ runResourceT $ do     fdcountBefore <- countFileDescriptors -    (addr, networkSocket, key) <- listenRandomUnixAbstract+    (addr, key) <- listenRandomUnixAbstract     release key      liftIO $ assertThrows@@ -166,7 +166,7 @@  test_OpenTcp_IPv4 :: TestTree test_OpenTcp_IPv4 = testCase "ipv4" $ runResourceT $ do-    (addr, networkSocket, _) <- listenRandomIPv4+    (addr, _, _) <- listenRandomIPv4     fdcountBefore <- countFileDescriptors      t <- liftIO (transportOpen socketTransportOptions addr)@@ -177,7 +177,7 @@  test_OpenTcp_IPv6 :: TestTree test_OpenTcp_IPv6 = testCase "ipv6" $ unlessM noIPv6 $ runResourceT $ do-    (addr, networkSocket) <- listenRandomIPv6+    addr <- listenRandomIPv6     fdcountBefore <- countFileDescriptors      t <- liftIO (transportOpen socketTransportOptions addr)@@ -260,7 +260,7 @@ test_OpenTcp_NotListening = testCase "too-many" $ runResourceT $ do     fdcountBefore <- countFileDescriptors -    (addr, networkSocket, key) <- listenRandomIPv4+    (addr, _, key) <- listenRandomIPv4     release key     liftIO $ assertThrows         (\err -> and@@ -282,7 +282,7 @@         fix $ \loop -> do             bytes <- recv s 50             if Data.ByteString.null bytes-                then NS.sClose s+                then NS.close s                 else do                     sendAll s bytes                     loop@@ -315,7 +315,7 @@     _ <- liftIO $ forkIO $ do         (s, _) <- NS.accept networkSocket         sendAll s "123"-        NS.sClose s+        NS.close s      (_, t) <- allocate         (transportOpen socketTransportOptions addr)
tests/DBusTests/Util.hs view
@@ -91,7 +91,7 @@     uuid <- randomUUID     return (tmp </> formatUUID uuid) -listenRandomUnixPath :: MonadResource m => m (Address, N.Socket)+listenRandomUnixPath :: MonadResource m => m Address listenRandomUnixPath = do     path <- liftIO getTempPath @@ -99,16 +99,16 @@     (_, sock) <- allocate         (NS.socket NS.AF_UNIX NS.Stream NS.defaultProtocol)         N.sClose-    liftIO (NS.bindSocket sock sockAddr)+    liftIO (NS.bind sock sockAddr)     liftIO (NS.listen sock 1)     _ <- register (removeFile path)      let Just addr = address "unix" (Map.fromList             [ ("path", path)             ])-    return (addr, sock)+    return addr -listenRandomUnixAbstract :: MonadResource m => m (Address, N.Socket, ReleaseKey)+listenRandomUnixAbstract :: MonadResource m => m (Address, ReleaseKey) listenRandomUnixAbstract = do     uuid <- liftIO randomUUID     let sockAddr = NS.SockAddrUnix ('\x00' : formatUUID uuid)@@ -117,13 +117,13 @@         (NS.socket NS.AF_UNIX NS.Stream NS.defaultProtocol)         N.sClose -    liftIO $ NS.bindSocket sock sockAddr+    liftIO $ NS.bind sock sockAddr     liftIO $ NS.listen sock 1      let Just addr = address "unix" (Map.fromList             [ ("abstract", formatUUID uuid)             ])-    return (addr, sock, key)+    return (addr, key)  listenRandomIPv4 :: MonadResource m => m (Address, N.Socket, ReleaseKey) listenRandomIPv4 = do@@ -133,7 +133,7 @@     (key, sock) <- allocate         (NS.socket NS.AF_INET NS.Stream NS.defaultProtocol)         N.sClose-    liftIO $ NS.bindSocket sock sockAddr+    liftIO $ NS.bind sock sockAddr     liftIO $ NS.listen sock 1      sockPort <- liftIO $ NS.socketPort sock@@ -144,7 +144,7 @@             ])     return (addr, sock, key) -listenRandomIPv6 :: MonadResource m => m (Address, N.Socket)+listenRandomIPv6 :: MonadResource m => m Address listenRandomIPv6 = do     addrs <- liftIO $ NS.getAddrInfo Nothing (Just "::1") Nothing     let sockAddr = case addrs of@@ -154,7 +154,7 @@     (_, sock) <- allocate         (NS.socket NS.AF_INET6 NS.Stream NS.defaultProtocol)         N.sClose-    liftIO $ NS.bindSocket sock sockAddr+    liftIO $ NS.bind sock sockAddr     liftIO $ NS.listen sock 1      sockPort <- liftIO $ NS.socketPort sock@@ -163,7 +163,7 @@             , ("host", "::1")             , ("port", show (toInteger sockPort))             ])-    return (addr, sock)+    return addr  noIPv6 :: IO Bool noIPv6 = do