nim-json-rpc/eth-rpc/server/servertypes.nim

98 lines
3.1 KiB
Nim

import asyncdispatch, asyncnet, json, tables, macros, strutils, ../ jsonconverters, ../ jsonmarshal, stint
export asyncdispatch, asyncnet, json, jsonconverters, expect
type
RpcProc* = proc (params: JsonNode): Future[JsonNode]
RpcServer* = ref object
socket*: AsyncSocket
port*: Port
address*: string
procs*: TableRef[string, RpcProc]
RpcProcError* = ref object of Exception
code*: int
data*: JsonNode
proc register*(server: RpcServer, name: string, rpc: RpcProc) =
server.procs[name] = rpc
proc unRegisterAll*(server: RpcServer) = server.procs.clear
proc newRpcServer*(address = "localhost", port: Port = Port(8545)): RpcServer =
result = RpcServer(
socket: newAsyncSocket(),
port: port,
address: address,
procs: newTable[string, RpcProc]()
)
var sharedServer: RpcServer
proc sharedRpcServer*(): RpcServer =
if sharedServer.isNil: sharedServer = newRpcServer("")
result = sharedServer
proc `$`*(port: Port): string = $int(port)
proc makeProcName(s: string): string =
# only alphanumeric
result = ""
for c in s:
if c.isAlphaNumeric: result.add c
proc hasReturnType(params: NimNode): bool =
if params != nil and params.len > 0 and params[0] != nil and params[0].kind != nnkEmpty:
result = true
macro rpc*(server: var RpcServer, path: string, body: untyped): untyped =
result = newStmtList()
let
parameters = body.findChild(it.kind == nnkFormalParams)
paramsIdent = newIdentNode"params" # all remote calls have a single parameter: `params: JsonNode`
pathStr = $path # procs are generated from the stripped path
procNameStr = pathStr.makeProcName # strip non alphanumeric
procName = newIdentNode(procNameStr) # public rpc proc
doMain = newIdentNode(procNameStr & "DoMain") # when parameters: proc that contains our rpc body
res = newIdentNode("result") # async result
var
setup = jsonToNim(parameters, paramsIdent)
procBody = if body.kind == nnkStmtList: body else: body.body
if parameters.hasReturnType:
let returnType = parameters[0]
# delgate async proc allows return and setting of result as native type
result.add(quote do:
proc `doMain`(`paramsIdent`: JsonNode): Future[`returnType`] {.async.} =
`setup`
`procBody`
)
if returnType == ident"JsonNode":
# `JsonNode` results don't need conversion
result.add( quote do:
proc `procName`*(`paramsIdent`: JsonNode): Future[JsonNode] {.async.} =
`res` = await `doMain`(`paramsIdent`)
)
else:
result.add(quote do:
proc `procName`*(`paramsIdent`: JsonNode): Future[JsonNode] {.async.} =
`res` = %await `doMain`(`paramsIdent`)
)
else:
# no return types, inline contents
result.add(quote do:
proc `procName`*(`paramsIdent`: JsonNode): Future[JsonNode] {.async.} =
`setup`
`procBody`
)
result.add( quote do:
`server`.register(`path`, `procName`)
)
when defined(nimDumpRpcs):
echo "\n", pathStr, ": ", result.repr
# TODO: Check supplied rpc do signatures with ethcallsigs