Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / THH / HsRPC.hs @ aa4a4b76

History | View | Annotate | Download (3.9 kB)

1
{-# LANGUAGE TemplateHaskell, FunctionalDependencies, FlexibleContexts #-}
2
-- {-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}
3

    
4
{-| Creates a client out of list of RPC server components.
5

    
6
-}
7

    
8
{-
9

    
10
Copyright (C) 2014 Google Inc.
11

    
12
This program is free software; you can redistribute it and/or modify
13
it under the terms of the GNU General Public License as published by
14
the Free Software Foundation; either version 2 of the License, or
15
(at your option) any later version.
16

    
17
This program is distributed in the hope that it will be useful, but
18
WITHOUT ANY WARRANTY; without even the implied warranty of
19
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20
General Public License for more details.
21

    
22
You should have received a copy of the GNU General Public License
23
along with this program; if not, write to the Free Software
24
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25
02110-1301, USA.
26

    
27
-}
28

    
29
module Ganeti.THH.HsRPC
30
  ( RpcClientMonad
31
  , runRpcClient
32
  , mkRpcCall
33
  , mkRpcCalls
34
  ) where
35

    
36
import Control.Applicative
37
import Control.Monad
38
import Control.Monad.Base
39
import Control.Monad.Error
40
import Control.Monad.Reader
41
import Language.Haskell.TH
42
import qualified Text.JSON as J
43

    
44
import Ganeti.BasicTypes
45
import Ganeti.Errors
46
import Ganeti.JSON (fromJResultE)
47
import Ganeti.THH.Types
48
import Ganeti.UDSServer
49

    
50

    
51
-- * The monad for RPC clients
52

    
53
-- | The monad for all client RPC functions.
54
-- Given a client value, it runs the RPC call in IO and either retrieves the
55
-- result or the error.
56
newtype RpcClientMonad a =
57
  RpcClientMonad { runRpcClientMonad :: ReaderT Client ResultG a }
58

    
59
instance Functor RpcClientMonad where
60
  fmap f = RpcClientMonad . fmap f . runRpcClientMonad
61

    
62
instance Applicative RpcClientMonad where
63
  pure = RpcClientMonad . pure
64
  (RpcClientMonad f) <*> (RpcClientMonad k) = RpcClientMonad (f <*> k)
65

    
66
instance Monad RpcClientMonad where
67
  return = RpcClientMonad . return
68
  (RpcClientMonad k) >>= f = RpcClientMonad (k >>= runRpcClientMonad . f)
69

    
70
instance MonadBase IO RpcClientMonad where
71
  liftBase = RpcClientMonad . liftBase
72

    
73
instance MonadIO RpcClientMonad where
74
  liftIO = RpcClientMonad . liftIO
75

    
76
instance MonadError GanetiException RpcClientMonad where
77
  throwError = RpcClientMonad . throwError
78
  catchError (RpcClientMonad k) h =
79
    RpcClientMonad (catchError k (runRpcClientMonad . h))
80

    
81
-- * The TH functions to construct RPC client functions from RPC server ones
82

    
83
-- | Given a client run a given client RPC action.
84
runRpcClient :: (MonadBase IO m, MonadError GanetiException m)
85
             => RpcClientMonad a -> Client -> m a
86
runRpcClient = (toErrorBase .) . runReaderT . runRpcClientMonad
87

    
88
callMethod :: (J.JSON r, J.JSON args) => String -> args -> RpcClientMonad r
89
callMethod method args = do
90
  client <- RpcClientMonad ask
91
  let request = buildCall method (J.showJSON args)
92
  liftIO $ sendMsg client request
93
  response <- liftIO $ recvMsg client
94
  toError $ parseResponse response
95
            >>= fromJResultE "Parsing RPC JSON response" . J.readJSON
96

    
97
-- | Given a server RPC function (such as from WConfd.Core), creates
98
-- the corresponding client function. The monad of the result type of the
99
-- given function is replaced by 'RpcClientMonad' and the new function
100
-- is implemented to issue a RPC call to the server.
101
mkRpcCall :: Name -> Q [Dec]
102
mkRpcCall name = do
103
  let bname = nameBase name
104
      fname = mkName bname  -- the name of the generated function
105
  (args, rtype) <- funArgs <$> typeOfFun name
106
  rarg <- argumentType rtype
107
  let ftype = foldr (\a t -> AppT (AppT ArrowT a) t)
108
                    (AppT (ConT ''RpcClientMonad) rarg) args
109
  body <- [| $(curryN $ length args) (callMethod $(stringE bname)) |]
110
  return [ SigD fname ftype
111
         , ValD (VarP fname) (NormalB body) []
112
         ]
113

    
114
-- Given a list of server RPC functions creates the corresponding client
115
-- RPC functions.
116
--
117
-- See 'mkRpcCall'
118
mkRpcCalls :: [Name] -> Q [Dec]
119
mkRpcCalls = liftM concat . mapM mkRpcCall