File: Generation.hs

package info (click to toggle)
haskell-dbus 1.3.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 536 kB
  • sloc: haskell: 7,693; xml: 90; makefile: 2
file content (552 lines) | stat: -rw-r--r-- 24,160 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
{-# LANGUAGE CPP #-}
{-# 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.Parse as I
import qualified DBus.Introspection.Types as I
import qualified Data.ByteString as BS
import qualified Data.Char as Char
import           Data.Coerce
import           Data.Int
import           Data.List
import qualified Data.Map as Map
import           Data.Maybe
import           Data.Monoid
import           Data.String
import qualified Data.Text.IO as Text
import           Data.Traversable
import           Data.Word
import           Language.Haskell.TH
import           Prelude hiding (mapM)
import           System.Posix.Types (Fd(..))

-- | Compatibility helper to create (total) tuple expressions
mkTupE :: [Exp] -> Exp
mkTupE = TupE
#if MIN_VERSION_template_haskell(2,16,0)
         . map Just
#endif

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 mkTupE
        finalResultTuple = mapOrHead' VarE finalOutputNames mkTupE
        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 $ addTypeArg (getArgType propType) $
                        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 mkTupE
        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 = do
    xml <- runIO $ Text.readFile filepath
    let obj = head $ maybeToList $ I.parseXML "/" xml
        interface = head $ I.objectInterfaces obj
        signals = generateSignalsFromInterface generationParams interface
        client = generateClient generationParams interface
     in fmap (++) signals <*> client