Make rpc transform use async

This commit is contained in:
coffeepots 2018-04-11 20:08:12 +01:00 committed by zah
parent 2f7b91c035
commit ffc7a6378b
3 changed files with 21 additions and 12 deletions

View File

@ -1,10 +1,14 @@
import macros, servertypes import macros, servertypes, strutils
var rpcCallRefs {.compiletime.} = newSeq[(string)]() var rpcCallRefs {.compiletime.} = newSeq[(string)]()
macro rpc*(prc: untyped): untyped = macro rpc*(prc: untyped): untyped =
# REVIEW: (IMPORTANT) I think the rpc procs should be async. ## Converts a procedure into the following format:
# they may need to call into other async procs of the VM ## <proc name>*(params: JsonNode): Future[JsonNode] {.async.}
## This procedure is then added into a compile-time list
## so that it is automatically registered for every server that
## calls registerRpcs(server)
prc.expectKind nnkProcDef
result = prc result = prc
let let
params = prc.findChild(it.kind == nnkFormalParams) params = prc.findChild(it.kind == nnkFormalParams)
@ -20,8 +24,13 @@ macro rpc*(prc: untyped): untyped =
identDefs.add ident("params"), ident("JsonNode"), newEmptyNode() identDefs.add ident("params"), ident("JsonNode"), newEmptyNode()
# check there isn't already a result type # check there isn't already a result type
assert params.len == 1 and params[0].kind == nnkEmpty assert params.len == 1 and params[0].kind == nnkEmpty
params[0] = ident("JsonNode") params[0] = newNimNode(nnkBracketExpr)
params[0].add ident("Future"), ident("JsonNode")
params.add identDefs params.add identDefs
# add async pragma, we can assume there isn't an existing .async.
# as this would fail the result check above.
prc.addPragma(newIdentNode("async"))
# Adds to compiletime list of rpc calls so we can register them in bulk # Adds to compiletime list of rpc calls so we can register them in bulk
# for multiple servers using `registerRpcs`. # for multiple servers using `registerRpcs`.
rpcCallRefs.add $procName rpcCallRefs.add $procName
@ -33,3 +42,4 @@ macro registerRpcs*(server: RpcServer): untyped =
for procName in rpcCallRefs: for procName in rpcCallRefs:
let de = newDotExpr(ident($server), ident("register")) let de = newDotExpr(ident($server), ident("register"))
result.add(newCall(de, newStrLitNode(procName), ident(procName))) result.add(newCall(de, newStrLitNode(procName), ident(procName)))
echo result.repr

View File

@ -20,7 +20,7 @@ proc processMessage(server: RpcServer, client: AsyncSocket, line: string) {.asyn
if not server.procs.hasKey(methodName): if not server.procs.hasKey(methodName):
await client.sendError(METHOD_NOT_FOUND, "Method not found", id, %(methodName & " is not a registered method.")) await client.sendError(METHOD_NOT_FOUND, "Method not found", id, %(methodName & " is not a registered method."))
else: else:
let callRes = server.procs[methodName](node["params"]) let callRes = await server.procs[methodName](node["params"])
await client.send($wrapReply(id, callRes, newJNull()) & "\c\l") await client.send($wrapReply(id, callRes, newJNull()) & "\c\l")
proc processClient(server: RpcServer, client: AsyncSocket) {.async.} = proc processClient(server: RpcServer, client: AsyncSocket) {.async.} =
@ -33,12 +33,11 @@ proc processClient(server: RpcServer, client: AsyncSocket) {.async.} =
ifDebug: echo "Process client: ", server.port, ":" & line ifDebug: echo "Process client: ", server.port, ":" & line
let fut = processMessage(server, client, line) let future = processMessage(server, client, line)
await fut await future
if fut.failed: if future.failed:
if fut.readError of RpcProcError: if future.readError of RpcProcError:
# TODO: Currently exceptions in rpc calls are not properly handled let err = future.readError.RpcProcError
let err = fut.readError.RpcProcError
await client.sendError(err.code, err.msg, err.data) await client.sendError(err.code, err.msg, err.data)
else: else:
await client.sendError(-32000, "Error", %getCurrentExceptionMsg()) await client.sendError(-32000, "Error", %getCurrentExceptionMsg())

View File

@ -1,7 +1,7 @@
import asyncdispatch, asyncnet, json, tables import asyncdispatch, asyncnet, json, tables
type type
RpcProc* = proc (params: JsonNode): JsonNode RpcProc* = proc (params: JsonNode): Future[JsonNode]
RpcServer* = ref object RpcServer* = ref object
socket*: AsyncSocket socket*: AsyncSocket