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

159 lines
5.7 KiB
Nim
Raw Normal View History

import asyncdispatch, asyncnet, json, tables, macros, strutils
export asyncdispatch, asyncnet, json
2018-03-02 11:46:59 +00:00
type
2018-04-11 20:08:12 +01:00
RpcProc* = proc (params: JsonNode): Future[JsonNode]
2018-03-02 11:46:59 +00:00
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(
2018-03-02 11:46:59 +00:00
socket: newAsyncSocket(),
port: port,
address: address,
procs: newTable[string, RpcProc]()
)
var sharedServer: RpcServer
proc sharedRpcServer*(): RpcServer =
if sharedServer.isNil: sharedServer = newRpcServer("")
result = sharedServer
proc fromJson(n: JsonNode, argName: string, result: var bool) =
if n.kind != JBool: raise newException(ValueError, "Parameter \"" & argName & "\" expected JBool but got " & $n.kind)
result = n.getBool()
2018-05-08 11:51:24 +01:00
proc fromJson(n: JsonNode, argName: string, result: var int) =
if n.kind != JInt: raise newException(ValueError, "Parameter \"" & argName & "\" expected JInt but got " & $n.kind)
2018-05-08 11:51:24 +01:00
result = n.getInt()
proc fromJson(n: JsonNode, argName: string, result: var byte) =
if n.kind != JInt: raise newException(ValueError, "Parameter \"" & argName & "\" expected JInt but got " & $n.kind)
2018-05-08 11:51:24 +01:00
let v = n.getInt()
if v > 255 or v < 0: raise newException(ValueError, "Parameter \"" & argName & "\" value out of range for byte: " & $v)
2018-05-08 11:51:24 +01:00
result = byte(v)
proc fromJson(n: JsonNode, argName: string, result: var float) =
if n.kind != JFloat: raise newException(ValueError, "Parameter \"" & argName & "\" expected JFloat but got " & $n.kind)
2018-05-08 11:51:24 +01:00
result = n.getFloat()
proc fromJson(n: JsonNode, argName: string, result: var string) =
if n.kind != JString: raise newException(ValueError, "Parameter \"" & argName & "\" expected JString but got " & $n.kind)
2018-05-08 11:51:24 +01:00
result = n.getStr()
proc fromJson[T](n: JsonNode, argName: string, result: var seq[T]) =
if n.kind != JArray: raise newException(ValueError, "Parameter \"" & argName & "\" expected JArray but got " & $n.kind)
2018-05-08 11:51:24 +01:00
result = newSeq[T](n.len)
for i in 0 ..< n.len:
fromJson(n[i], argName, result[i])
2018-05-08 11:51:24 +01:00
proc fromJson[N, T](n: JsonNode, argName: string, result: var array[N, T]) =
if n.kind != JArray: raise newException(ValueError, "Parameter \"" & argName & "\" expected JArray but got " & $n.kind)
if n.len > result.len: raise newException(ValueError, "Parameter \"" & argName & "\" item count is too big for array")
for i in 0 ..< n.len:
fromJson(n[i], argName, result[i])
2018-05-08 11:51:24 +01:00
proc fromJson[T: object](n: JsonNode, argName: string, result: var T) =
if n.kind != JObject: raise newException(ValueError, "Parameter \"" & argName & "\" expected JObject but got " & $n.kind)
2018-05-08 11:51:24 +01:00
for k, v in fieldpairs(result):
fromJson(n[k], k, v)
2018-05-08 11:51:24 +01:00
proc unpackArg[T](argIdx: int, argName: string, argtype: typedesc[T], args: JsonNode): T =
fromJson(args[argIdx], argName, result)
proc setupParams(parameters, paramsIdent: NimNode): NimNode =
# Add code to verify input and load parameters into Nim types
result = newStmtList()
if not parameters.isNil:
# initial parameter array length check
var expectedLen = parameters.len - 1
let expectedStr = "Expected " & $expectedLen & " Json parameter(s) but got "
result.add(quote do:
if `paramsIdent`.kind != JArray:
raise newException(ValueError, "Parameter params expected JArray but got " & $`paramsIdent`.kind)
if `paramsIdent`.len != `expectedLen`:
raise newException(ValueError, `expectedStr` & $`paramsIdent`.len)
)
# unpack each parameter and provide assignments
2018-05-08 11:51:24 +01:00
for i in 1 ..< parameters.len:
2018-05-01 20:32:28 +01:00
let
paramName = parameters[i][0]
pos = i - 1
2018-05-08 11:51:24 +01:00
paramNameStr = $paramName
2018-05-01 20:32:28 +01:00
paramType = parameters[i][1]
result.add(quote do:
2018-05-08 11:51:24 +01:00
var `paramName` = `unpackArg`(`pos`, `paramNameStr`, `paramType`, `paramsIdent`)
2018-05-01 20:32:28 +01:00
)
macro multiRemove(s: string, values: varargs[string]): untyped =
## Wrapper for multiReplace
var
body = newStmtList()
multiReplaceCall = newCall(ident"multiReplace", s)
body.add(newVarStmt(ident"eStr", newStrLitNode("")))
let emptyStr = ident"eStr"
for item in values:
# generate tuples of values with the empty string `eStr`
let sItem = $item
multiReplaceCall.add(newPar(newStrLitNode(sItem), emptyStr))
body.add multiReplaceCall
result = newBlockStmt(body)
2018-05-01 20:32:28 +01:00
macro on*(server: var RpcServer, path: string, body: untyped): untyped =
result = newStmtList()
let
parameters = body.findChild(it.kind == nnkFormalParams)
paramsIdent = ident"params"
pathStr = $path
2018-05-08 16:03:28 +01:00
procName = ident(pathStr.multiRemove(".", "/"))
var
setup = setupParams(parameters, paramsIdent)
procBody: NimNode
2018-05-08 17:29:23 +01:00
bodyWrapper = newStmtList()
if body.kind == nnkStmtList: procBody = body
else: procBody = body.body
2018-05-08 17:29:23 +01:00
if parameters.len > 0 and parameters[0] != nil:
# when a return type is specified, shadow async's result
# and pass it back jsonified
let
returnType = parameters[0]
res = ident"result"
template doMain(body: untyped): untyped =
# create a new scope to allow shadowing result
block:
body
bodyWrapper = quote do:
`res` = `doMain`:
var `res`: `returnType`
`procBody`
%`res`
else:
bodyWrapper = quote do: `procBody`
# async proc wrapper around body
result = quote do:
2018-05-08 17:29:23 +01:00
proc `procName`*(`paramsIdent`: JsonNode): Future[JsonNode] {.async.} =
`setup`
`bodyWrapper`
`server`.register(`path`, `procName`)
when defined(nimDumpRpcs):
2018-05-08 16:03:28 +01:00
echo "\n", pathStr, ": ", result.repr