capnp-0.4.0.0: examples/lib/Examples/Rpc/CalculatorServer.hs
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Examples.Rpc.CalculatorServer (main) where
import Prelude hiding (subtract)
import Data.Int
import Control.Concurrent.STM (STM, atomically)
import Control.Monad (when)
import Network.Simple.TCP (serve)
import Supervisors (Supervisor)
import qualified Data.Vector as V
import Capnp (def, defaultLimit)
import Capnp.Rpc
( ConnConfig(..)
, handleConn
, pureHandler
, socketTransport
, throwFailed
, toClient
, wait
, (?)
)
import Capnp.Gen.Calculator.Pure
newtype LitValue = LitValue Double
instance Value'server_ IO LitValue where
value'read = pureHandler $ \(LitValue val) _ ->
pure Value'read'results { value = val }
newtype OpFunc = OpFunc (Double -> Double -> Double)
instance Function'server_ IO OpFunc where
function'call = pureHandler $ \(OpFunc op) Function'call'params{params} -> do
when (V.length params /= 2) $
throwFailed "Wrong number of parameters."
pure Function'call'results
{ value = (params V.! 0) `op` (params V.! 1) }
data ExprFunc = ExprFunc
{ paramCount :: !Int32
, body :: Expression
}
instance Function'server_ IO ExprFunc where
function'call =
pureHandler $ \ExprFunc{..} Function'call'params{params} -> do
when (fromIntegral (V.length params) /= paramCount) $
throwFailed "Wrong number of parameters."
Function'call'results <$> eval params body
data MyCalc = MyCalc
{ add :: Function
, subtract :: Function
, multiply :: Function
, divide :: Function
, sup :: Supervisor
}
instance Calculator'server_ IO MyCalc where
calculator'evaluate =
pureHandler $ \MyCalc{sup} Calculator'evaluate'params{expression} ->
Calculator'evaluate'results <$>
(eval V.empty expression >>= atomically . export_Value sup . LitValue)
calculator'getOperator =
pureHandler $ \MyCalc{..} Calculator'getOperator'params{op} ->
Calculator'getOperator'results <$> case op of
Operator'add -> pure add
Operator'subtract -> pure subtract
Operator'multiply -> pure multiply
Operator'divide -> pure divide
Operator'unknown' _ ->
throwFailed "Unknown operator"
calculator'defFunction =
pureHandler $ \MyCalc{sup} Calculator'defFunction'params{..} ->
Calculator'defFunction'results <$>
atomically (export_Function sup ExprFunc{..})
newCalculator :: Supervisor -> STM Calculator
newCalculator sup = do
add <- export_Function sup $ OpFunc (+)
subtract <- export_Function sup $ OpFunc (-)
multiply <- export_Function sup $ OpFunc (*)
divide <- export_Function sup $ OpFunc (/)
export_Calculator sup MyCalc{..}
eval :: V.Vector Double -> Expression -> IO Double
eval _ (Expression'literal lit) =
pure lit
eval _ (Expression'previousResult val) = do
Value'read'results{value} <- value'read val ? def >>= wait
pure value
eval args (Expression'parameter idx)
| fromIntegral idx >= V.length args =
throwFailed "Parameter index out of bounds"
| otherwise =
pure $ args V.! fromIntegral idx
eval outerParams (Expression'call Expression'call'{function, params=innerParams}) = do
args' <- traverse (eval outerParams) innerParams
Function'call'results{value} <-
function'call function ? Function'call'params { params = args' }
>>= wait
pure value
eval _ (Expression'unknown' _) =
throwFailed "Unknown expression type"
main :: IO ()
main = serve "localhost" "4000" $ \(sock, _addr) ->
handleConn (socketTransport sock defaultLimit) def
{ getBootstrap = fmap (Just . toClient) . newCalculator
, debugMode = True
}