packages feed

ribosome-host-0.9.9.9: lib/Ribosome/Host/Interpreter/Handlers.hs

-- |Interpreters for 'Handlers'.
module Ribosome.Host.Interpreter.Handlers where

import qualified Data.Map.Strict as Map
import Data.MessagePack (Object)

import Ribosome.Host.Data.BootError (BootError)
import Ribosome.Host.Data.Report (Report)
import Ribosome.Host.Data.Request (RpcMethod)
import Ribosome.Host.Data.RpcError (RpcError)
import Ribosome.Host.Data.RpcHandler (
  Handler,
  RpcHandler (RpcHandler),
  RpcHandlerFun,
  hoistRpcHandlers,
  rpcHandlerMethod,
  )
import qualified Ribosome.Host.Effect.Handlers as Handlers
import Ribosome.Host.Effect.Handlers (Handlers (Register, Run))
import Ribosome.Host.Effect.Rpc (Rpc)
import Ribosome.Host.RegisterHandlers (registerHandlers)

-- |Interpret 'Handlers' by performing no actions.
interpretHandlersNull :: InterpreterFor (Handlers !! Report) r
interpretHandlersNull =
  interpretResumable \case
    Register ->
      unit
    Run _ _ ->
      pure Nothing

-- |Interpret 'Handlers' by performing no actions.
noHandlers :: InterpreterFor (Handlers !! Report) r
noHandlers =
  interpretHandlersNull

-- |Create a method-indexed 'Map' from a set of 'RpcHandler's.
handlersByName ::
  [RpcHandler r] ->
  Map RpcMethod (RpcHandlerFun r)
handlersByName =
  Map.fromList . fmap \ rpcDef@(RpcHandler _ _ _ handler) -> (rpcHandlerMethod rpcDef, handler)

-- |Execute the handler corresponding to an 'RpcMethod', if it exists.
runHandler ::
  Map RpcMethod (RpcHandlerFun r) ->
  RpcMethod ->
  [Object] ->
  Handler r (Maybe Object)
runHandler handlers method args =
  traverse ($ args) (Map.lookup method handlers)

-- |Add a set of 'RpcHandler's to the plugin.
--
-- This can be used multiple times and has to be terminated by 'interpretHandlersNull', which is done automatically when
-- using the plugin main functions.
withHandlers ::
  Members [Handlers !! Report, Rpc !! RpcError, Log, Error BootError] r =>
  [RpcHandler r] ->
  Sem r a ->
  Sem r a
withHandlers handlersList@(handlersByName -> handlers) =
  interceptResumable @Report \case
    Register -> do
      restop @Report Handlers.register
      registerHandlers handlersList
    Run method args ->
      maybe (runHandler handlers method args) (pure . Just) =<< restop (Handlers.run method args)

-- |Interpret 'Handlers' with a set of 'RpcHandlers'.
interpretHandlers ::
  Members [Rpc !! RpcError, Log, Error BootError] r =>
  [RpcHandler r] ->
  InterpreterFor (Handlers !! Report) r
interpretHandlers handlers =
  interpretHandlersNull .
  withHandlers (hoistRpcHandlers raiseUnder handlers)