mirror of
https://github.com/status-im/nim-json-rpc.git
synced 2025-02-25 10:45:21 +00:00
82 lines
2.3 KiB
Nim
82 lines
2.3 KiB
Nim
import asyncdispatch, asyncnet, json, tables, macros, strutils
|
|
|
|
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
|
|
|
|
var rpcCallRefs {.compiletime.} = newSeq[(string)]()
|
|
|
|
proc register*(server: RpcServer, name: string, rpc: RpcProc) =
|
|
server.procs[name] = rpc
|
|
|
|
proc unRegisterAll*(server: RpcServer) = server.procs.clear
|
|
|
|
macro rpc*(prc: untyped): untyped =
|
|
## Converts a procedure into the following format:
|
|
## <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
|
|
let
|
|
params = prc.params
|
|
procName = prc.name
|
|
|
|
procName.expectKind(nnkIdent)
|
|
|
|
# check there isn't already a result type
|
|
assert params[0].kind == nnkEmpty
|
|
|
|
# add parameter
|
|
params.add nnkIdentDefs.newTree(
|
|
newIdentNode("params"),
|
|
newIdentNode("JsonNode"),
|
|
newEmptyNode()
|
|
)
|
|
# set result type
|
|
params[0] = nnkBracketExpr.newTree(
|
|
newIdentNode("Future"),
|
|
newIdentNode("JsonNode")
|
|
)
|
|
# add async pragma; we can assume there isn't an existing .async.
|
|
# as this would mean there's a return type and fail the result check above.
|
|
prc.addPragma(newIdentNode("async"))
|
|
|
|
# Adds to compiletime list of rpc calls so we can register them in bulk
|
|
# for multiple servers using `registerRpcs`.
|
|
rpcCallRefs.add $procName
|
|
|
|
macro registerRpcs*(server: RpcServer): untyped =
|
|
## Step through procs currently registered with {.rpc.} and add a register call for this server
|
|
result = newStmtList()
|
|
result.add(quote do:
|
|
`server`.unRegisterAll
|
|
)
|
|
for procName in rpcCallRefs:
|
|
let i = ident(procName)
|
|
result.add(quote do:
|
|
`server`.register(`procName`, `i`)
|
|
)
|
|
|
|
include ethprocs # TODO: This isn't ideal as it means editing code in ethprocs shows errors
|
|
|
|
proc newRpcServer*(address: string, port: Port = Port(8545)): RpcServer =
|
|
result = RpcServer(
|
|
socket: newAsyncSocket(),
|
|
port: port,
|
|
address: address,
|
|
procs: newTable[string, RpcProc]()
|
|
)
|
|
result.registerRpcs
|
|
|