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 |