mirror of
https://github.com/status-im/nim-json-rpc.git
synced 2025-02-25 02:35:15 +00:00
98 lines
3.1 KiB
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
|