ribosome-host-0.9.9.9: lib/Ribosome/Host/Interpreter/Host.hs
module Ribosome.Host.Interpreter.Host where
import Conc (Restoration, withAsync_)
import Data.MessagePack (Object (ObjectNil))
import Exon (exon)
import Log (Severity (Error, Warn), dataLog)
import Polysemy.Process (Process)
import System.IO.Error (IOError)
import Ribosome.Host.Data.BootError (BootError (BootError))
import Ribosome.Host.Data.Event (Event (Event), EventName (EventName))
import Ribosome.Host.Data.Report (LogReport (LogReport), Report (Report), severity)
import Ribosome.Host.Data.Request (Request (Request), RequestId, RpcMethod (RpcMethod))
import qualified Ribosome.Host.Data.Response as Response
import Ribosome.Host.Data.Response (Response)
import Ribosome.Host.Data.RpcError (RpcError)
import Ribosome.Host.Data.RpcMessage (RpcMessage)
import qualified Ribosome.Host.Effect.Handlers as Handlers
import Ribosome.Host.Effect.Handlers (Handlers)
import qualified Ribosome.Host.Effect.Host as Host
import Ribosome.Host.Effect.Host (Host)
import Ribosome.Host.Effect.Responses (Responses)
import Ribosome.Host.Effect.Rpc (Rpc)
import Ribosome.Host.Error (resumeBootError)
import Ribosome.Host.Listener (listener)
invalidMethod ::
Request ->
Response
invalidMethod (Request (RpcMethod name) _) =
Response.Error [exon|Invalid method for request: #{name}|]
publishEvent ::
Member (Events er Event) r =>
Request ->
Sem r ()
publishEvent (Request (RpcMethod name) args) =
publish (Event (EventName name) args)
handlerIOError ::
Members [Error Report, Final IO] r =>
Sem r a ->
Sem r a
handlerIOError =
fromExceptionSemVia \ (e :: IOError) ->
Report "Internal error" ["Handler exception", show e] Error
handlerReport ::
Member (DataLog LogReport) r =>
Bool ->
RpcMethod ->
Report ->
Sem r ()
handlerReport notification (RpcMethod method) r =
dataLog (LogReport r (notification || severity r < Error) (severity r >= Warn) (fromText method))
handle ::
Members [Handlers !! Report, Rpc !! RpcError, DataLog LogReport, Log, Final IO] r =>
Bool ->
Request ->
Sem r (Maybe Response)
handle notification (Request method args) =
errorToIOFinal (handlerIOError (resuming throw (Handlers.run method args))) >>= \case
Right Nothing ->
pure Nothing
Right (Just a) ->
pure (Just (Response.Success a))
Left r@(Report e _ severity) -> do
handlerReport notification method r
let
response =
if severity < Error then Response.Success ObjectNil else Response.Error e
pure (Just response)
interpretHost ::
Members [Handlers !! Report, Rpc !! RpcError, DataLog LogReport, Events er Event, Log, Final IO] r =>
InterpreterFor Host r
interpretHost =
interpret \case
Host.Request req ->
fromMaybe (invalidMethod req) <$> handle False req
Host.Notification req -> do
res <- handle True req
when (isNothing res) (publishEvent req)
register ::
Members [Handlers !! Report, Error BootError] r =>
Sem r ()
register =
Handlers.register !! \ e -> throw (BootError [exon|Registering handlers: #{show e}|])
type HostDeps er =
[
Handlers !! Report,
Process RpcMessage (Either Text RpcMessage),
Rpc !! RpcError,
DataLog LogReport,
Events er Event,
Responses RequestId Response !! RpcError,
Log,
Error BootError,
Resource,
Mask Restoration,
Race,
Async,
Embed IO,
Final IO
]
withHost ::
Members (HostDeps er) r =>
InterpreterFor Host r
withHost sem =
interpretHost do
withAsync_ listener do
register
sem
testHost ::
Members (HostDeps er) r =>
InterpretersFor [Rpc, Host] r
testHost =
withHost .
resumeBootError @Rpc
runHost ::
Members (HostDeps er) r =>
Sem r ()
runHost =
interpretHost do
withAsync_ register do
listener