2018-04-12 18:48:46 +01:00
|
|
|
import asyncdispatch, asyncnet, json, tables, macros, strutils
|
2018-04-24 13:41:59 +01:00
|
|
|
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
|
|
|
|
|
2018-04-12 18:48:46 +01:00
|
|
|
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"))
|
|
|
|
|
2018-04-20 21:19:08 +01:00
|
|
|
proc newRpcServer*(address = "localhost", port: Port = Port(8545)): RpcServer =
|
2018-04-12 18:48:46 +01:00
|
|
|
result = RpcServer(
|
2018-03-02 11:46:59 +00:00
|
|
|
socket: newAsyncSocket(),
|
|
|
|
port: port,
|
|
|
|
address: address,
|
|
|
|
procs: newTable[string, RpcProc]()
|
|
|
|
)
|
2018-04-20 21:19:08 +01:00
|
|
|
|
|
|
|
var sharedServer: RpcServer
|
|
|
|
|
|
|
|
proc sharedRpcServer*(): RpcServer =
|
|
|
|
if sharedServer.isNil: sharedServer = newRpcServer("")
|
|
|
|
result = sharedServer
|
|
|
|
|
|
|
|
macro multiRemove(s: string, values: varargs[string]): untyped =
|
2018-04-24 16:28:01 +01:00
|
|
|
## Wrapper for multiReplace
|
|
|
|
var
|
|
|
|
body = newStmtList()
|
|
|
|
multiReplaceCall = newCall(ident"multiReplace", s)
|
|
|
|
|
|
|
|
body.add(newVarStmt(ident"eStr", newStrLitNode("")))
|
|
|
|
let emptyStr = ident"eStr"
|
2018-04-20 21:19:08 +01:00
|
|
|
for item in values:
|
2018-04-24 16:28:01 +01:00
|
|
|
# generate tuples of values with the empty string `eStr`
|
2018-04-20 21:19:08 +01:00
|
|
|
let sItem = $item
|
2018-04-24 16:28:01 +01:00
|
|
|
multiReplaceCall.add(newPar(newStrLitNode(sItem), emptyStr))
|
|
|
|
|
|
|
|
body.add multiReplaceCall
|
|
|
|
result = newBlockStmt(body)
|
2018-04-20 21:19:08 +01:00
|
|
|
|
2018-05-03 20:20:10 +01:00
|
|
|
proc jsonGetFunc(paramType: string): (NimNode, JsonNodeKind) =
|
2018-05-03 22:40:28 +01:00
|
|
|
# Unknown types get attempted as int
|
2018-05-03 20:20:10 +01:00
|
|
|
case paramType
|
|
|
|
of "string": result = (ident"getStr", JString)
|
|
|
|
of "int": result = (ident"getInt", JInt)
|
|
|
|
of "float": result = (ident"getFloat", JFloat)
|
|
|
|
of "bool": result = (ident"getBool", JBool)
|
2018-05-03 22:40:28 +01:00
|
|
|
else:
|
|
|
|
if paramType == "byte" or paramType[0..3] == "uint" or paramType[0..2] == "int":
|
|
|
|
result = (ident"getInt", JInt)
|
|
|
|
else:
|
|
|
|
result = (nil, JInt)
|
2018-05-03 20:20:10 +01:00
|
|
|
|
2018-05-02 23:12:07 +01:00
|
|
|
proc jsonTranslate(translation: var NimNode, paramType: string): NimNode =
|
2018-05-03 22:40:28 +01:00
|
|
|
# TODO: Remove or rework this into `translate`
|
2018-05-02 23:12:07 +01:00
|
|
|
case paramType
|
|
|
|
of "uint8":
|
|
|
|
result = genSym(nskTemplate)
|
|
|
|
translation = quote do:
|
|
|
|
template `result`(value: int): uint8 =
|
|
|
|
if value > 255 or value < 0:
|
|
|
|
raise newException(ValueError, "Value out of range of byte, expected 0-255, got " & $value)
|
|
|
|
uint8(value and 0xff)
|
2018-05-03 22:40:28 +01:00
|
|
|
of "int8":
|
|
|
|
result = genSym(nskTemplate)
|
|
|
|
translation = quote do:
|
|
|
|
template `result`(value: int): uint8 =
|
|
|
|
if value > 255 or value < 0:
|
|
|
|
raise newException(ValueError, "Value out of range of byte, expected 0-255, got " & $value)
|
|
|
|
uint8(value and 0xff)
|
2018-05-02 23:12:07 +01:00
|
|
|
else:
|
|
|
|
result = genSym(nskTemplate)
|
|
|
|
translation = quote do:
|
|
|
|
template `result`(value: untyped): untyped = value
|
|
|
|
|
2018-05-03 20:20:10 +01:00
|
|
|
proc expectKind(node, jsonIdent, fieldName: NimNode, tn: JsonNodeKind) =
|
2018-05-01 20:32:28 +01:00
|
|
|
let
|
|
|
|
expectedStr = "Expected parameter `" & fieldName.repr & "` to be " & $tn & " but got "
|
|
|
|
tnIdent = ident($tn)
|
|
|
|
node.add(quote do:
|
|
|
|
if `jsonIdent`.kind != `tnIdent`:
|
|
|
|
raise newException(ValueError, `expectedStr` & $`jsonIdent`.kind)
|
|
|
|
)
|
2018-05-02 16:21:05 +01:00
|
|
|
|
2018-05-03 22:40:28 +01:00
|
|
|
proc getDigit(s: string): (bool, int) =
|
|
|
|
if s.len == 0: return (false, 0)
|
|
|
|
for c in s:
|
|
|
|
if not c.isDigit: return (false, 0)
|
|
|
|
return (true, s.parseInt)
|
|
|
|
|
|
|
|
|
|
|
|
from math import pow
|
|
|
|
|
|
|
|
proc translate(paramTypeStr: string, getField: NimNode): NimNode =
|
|
|
|
# Add checking and type conversion for more constrained types
|
|
|
|
# Note:
|
|
|
|
# * specific types add extra run time bounds checking code
|
|
|
|
# * types that map one-one get passed as is
|
|
|
|
# * any other types get a simple cast, ie; MyType(value) and
|
|
|
|
# get are assumed to be integer.
|
|
|
|
# NOTE: However this will never occur because currently jsonFunc
|
|
|
|
# is required to return nil to process other types.
|
|
|
|
# TODO: Allow distinct types
|
|
|
|
var paramType = paramTypeStr
|
|
|
|
if paramType == "byte": paramType = "uint8"
|
|
|
|
|
2018-05-03 20:20:10 +01:00
|
|
|
case paramType
|
2018-05-03 22:40:28 +01:00
|
|
|
of "string", "int", "bool":
|
2018-05-03 20:20:10 +01:00
|
|
|
result = quote do: `getField`
|
2018-05-03 22:40:28 +01:00
|
|
|
else:
|
|
|
|
if paramType[0 .. 3].toLowerAscii == "uint":
|
|
|
|
let (numeric, bitSize) = paramType[4 .. high(paramType)].getDigit
|
|
|
|
if numeric:
|
|
|
|
assert bitSize mod 8 == 0
|
|
|
|
let
|
|
|
|
maxSize = 1 shl bitSize - 1
|
|
|
|
sizeRangeStr = "0 to " & $maxSize
|
|
|
|
uintType = ident("uint" & $bitSize)
|
|
|
|
result = quote do:
|
|
|
|
let x = `getField`
|
|
|
|
if x > `maxSize` or x < 0:
|
|
|
|
raise newException(ValueError, "Value out of range of byte, expected " & `sizeRangeStr` & ", got " & $x)
|
|
|
|
`uintType`(x)
|
|
|
|
elif paramType[0 .. 2].toLowerAscii == "int":
|
|
|
|
let (numeric, bitSize) = paramType[3 .. paramType.high].getDigit
|
|
|
|
if numeric:
|
|
|
|
assert bitSize mod 8 == 0
|
|
|
|
let
|
|
|
|
maxSize = 1 shl (bitSize - 1)
|
|
|
|
minVal = -maxSize
|
|
|
|
maxVal = maxSize - 1
|
|
|
|
sizeRangeStr = $minVal & " to " & $maxVal
|
|
|
|
intType = ident("int" & $bitSize)
|
|
|
|
result = quote do:
|
|
|
|
let x = `getField`
|
|
|
|
if x < `minVal` or x > `maxVal`:
|
|
|
|
raise newException(ValueError, "Value out of range of byte, expected " & `sizeRangeStr` & ", got " & $x)
|
|
|
|
`intType`(x)
|
|
|
|
else:
|
|
|
|
let nativeParamType = ident(paramTypeStr)
|
|
|
|
result = quote do: `nativeParamType`(`getField`)
|
2018-05-03 20:20:10 +01:00
|
|
|
|
2018-05-01 20:32:28 +01:00
|
|
|
macro processFields(jsonIdent, fieldName, fieldType: typed): untyped =
|
|
|
|
result = newStmtList()
|
2018-05-02 16:21:05 +01:00
|
|
|
let
|
2018-05-03 22:40:28 +01:00
|
|
|
fieldTypeStr = fieldType.repr.toLowerAscii()
|
2018-05-03 20:20:10 +01:00
|
|
|
(jFetch, jKind) = jsonGetFunc(fieldTypeStr)
|
2018-05-03 22:40:28 +01:00
|
|
|
|
2018-05-02 16:21:05 +01:00
|
|
|
if not jFetch.isNil:
|
2018-05-03 22:40:28 +01:00
|
|
|
# TODO: getType(fieldType) to translate byte -> uint8 and avoid special cases
|
2018-05-03 20:20:10 +01:00
|
|
|
result.expectKind(jsonIdent, fieldName, jKind)
|
|
|
|
let
|
|
|
|
getField = quote do: `jsonIdent`.`jFetch`
|
|
|
|
res = translate(`fieldTypeStr`, `getField`)
|
2018-05-02 16:21:05 +01:00
|
|
|
result.add(quote do:
|
2018-05-03 20:20:10 +01:00
|
|
|
`fieldName` = `res`
|
2018-05-02 16:21:05 +01:00
|
|
|
)
|
|
|
|
else:
|
|
|
|
var fetchedType = getType(fieldType)
|
|
|
|
var derivedType: NimNode
|
|
|
|
if fetchedType[0].repr == "typeDesc":
|
|
|
|
derivedType = getType(fetchedType[1])
|
2018-05-01 20:32:28 +01:00
|
|
|
else:
|
2018-05-02 16:21:05 +01:00
|
|
|
derivedType = fetchedType
|
|
|
|
if derivedType.kind == nnkObjectTy:
|
2018-05-03 20:20:10 +01:00
|
|
|
result.expectKind(jsonIdent, fieldName, JObject)
|
2018-05-02 16:21:05 +01:00
|
|
|
let recs = derivedType.findChild it.kind == nnkRecList
|
|
|
|
for i in 0..<recs.len:
|
|
|
|
let
|
|
|
|
objFieldName = recs[i]
|
|
|
|
objFieldNameStr = objFieldName.toStrLit
|
|
|
|
objFieldType = getType(recs[i])
|
|
|
|
realType = getType(objFieldType)
|
|
|
|
jsonIdentStr = jsonIdent.repr
|
|
|
|
result.add(quote do:
|
|
|
|
if not `jsonIdent`.hasKey(`objFieldNameStr`):
|
|
|
|
raise newException(ValueError, "Cannot find field " & `objFieldNameStr` & " in " & `jsonIdentStr`)
|
|
|
|
processFields(`jsonIdent`[`objFieldNameStr`], `fieldName`.`objfieldName`, `realType`)
|
|
|
|
)
|
|
|
|
elif derivedType.kind == nnkBracketExpr:
|
|
|
|
# this should be a seq or array
|
2018-05-03 20:20:10 +01:00
|
|
|
result.expectKind(jsonIdent, fieldName, JArray)
|
2018-05-01 20:32:28 +01:00
|
|
|
let
|
2018-05-02 16:21:05 +01:00
|
|
|
formatType = derivedType[0].repr
|
2018-05-01 20:59:10 +01:00
|
|
|
expectedLen = genSym(nskConst)
|
2018-05-03 20:20:10 +01:00
|
|
|
var rootType: NimNode
|
2018-05-02 16:21:05 +01:00
|
|
|
case formatType
|
|
|
|
of "array":
|
|
|
|
let
|
|
|
|
startLen = derivedType[1][1]
|
|
|
|
endLen = derivedType[1][2]
|
|
|
|
expectedParamLen = quote do:
|
|
|
|
const `expectedLen` = `endLen` - `startLen` + 1
|
|
|
|
expectedLenStr = "Expected parameter `" & fieldName.repr & "` to have a length of "
|
2018-05-02 23:12:07 +01:00
|
|
|
# TODO: Note, currently only raising if greater than length, not different size
|
2018-05-02 16:21:05 +01:00
|
|
|
result.add(quote do:
|
|
|
|
`expectedParamLen`
|
|
|
|
if `jsonIdent`.len > `expectedLen`:
|
|
|
|
raise newException(ValueError, `expectedLenStr` & $`expectedLen` & " but got " & $`jsonIdent`.len)
|
|
|
|
)
|
2018-05-02 23:12:07 +01:00
|
|
|
rootType = derivedType[2]
|
2018-05-02 16:21:05 +01:00
|
|
|
of "seq":
|
|
|
|
result.add(quote do:
|
|
|
|
`fieldName` = @[]
|
|
|
|
`fieldName`.setLen(`jsonIdent`.len)
|
|
|
|
)
|
2018-05-02 23:12:07 +01:00
|
|
|
rootType = derivedType[1]
|
2018-05-02 16:21:05 +01:00
|
|
|
else:
|
|
|
|
raise newException(ValueError, "Cannot determine bracket expression type of \"" & derivedType.treerepr & "\"")
|
|
|
|
# add fetch code for array/seq
|
2018-05-03 22:40:28 +01:00
|
|
|
var translation: NimNode
|
|
|
|
let
|
|
|
|
(jFunc, jKind) = jsonGetFunc(($rootType).toLowerAscii)
|
|
|
|
transIdent = translation.jsonTranslate($rootType)
|
|
|
|
# TODO: Add checks PER ITEM (performance hit!) in the array, if required by the type
|
|
|
|
# TODO: Refactor `jsonTranslate` into `translate`
|
2018-05-01 20:32:28 +01:00
|
|
|
result.add(quote do:
|
2018-05-02 23:12:07 +01:00
|
|
|
`translation`
|
2018-05-02 16:21:05 +01:00
|
|
|
for i in 0 ..< `jsonIdent`.len:
|
2018-05-02 23:12:07 +01:00
|
|
|
`fieldName`[i] = `transIdent`(`jsonIdent`.elems[i].`jFunc`)
|
2018-05-01 20:32:28 +01:00
|
|
|
)
|
2018-05-02 16:21:05 +01:00
|
|
|
else:
|
|
|
|
raise newException(ValueError, "Unknown type \"" & derivedType.treerepr & "\"")
|
2018-05-03 22:40:28 +01:00
|
|
|
when defined(nimDumpRpcs):
|
|
|
|
echo result.repr
|
2018-04-26 19:35:57 +01:00
|
|
|
|
2018-05-01 20:32:28 +01:00
|
|
|
proc setupParams(node, parameters, paramsIdent: NimNode) =
|
|
|
|
# recurse parameter's fields until we only have symbols
|
2018-04-20 21:19:08 +01:00
|
|
|
if not parameters.isNil:
|
2018-05-01 20:32:28 +01:00
|
|
|
var
|
|
|
|
errorCheck = newStmtList()
|
|
|
|
expectedParams = parameters.len - 1
|
2018-04-25 19:18:42 +01:00
|
|
|
let expectedStr = "Expected " & $`expectedParams` & " Json parameter(s) but got "
|
2018-05-01 20:32:28 +01:00
|
|
|
node.add(quote do:
|
2018-04-25 19:18:42 +01:00
|
|
|
if `paramsIdent`.len != `expectedParams`:
|
|
|
|
raise newException(ValueError, `expectedStr` & $`paramsIdent`.len)
|
|
|
|
)
|
2018-04-24 17:37:01 +01:00
|
|
|
|
2018-05-01 20:32:28 +01:00
|
|
|
for i in 1..< parameters.len:
|
|
|
|
let
|
|
|
|
paramName = parameters[i][0]
|
|
|
|
pos = i - 1
|
|
|
|
var
|
|
|
|
paramType = parameters[i][1]
|
2018-05-02 23:12:07 +01:00
|
|
|
#discard paramType.preParseTypes(paramName, errorCheck)
|
2018-05-01 20:32:28 +01:00
|
|
|
node.add(quote do:
|
|
|
|
var `paramName`: `paramType`
|
|
|
|
processFields(`paramsIdent`[`pos`], `paramName`, `paramType`)
|
|
|
|
`errorCheck`
|
|
|
|
)
|
|
|
|
# TODO: Check for byte ranges
|
2018-04-25 19:18:42 +01:00
|
|
|
|
2018-05-01 20:32:28 +01:00
|
|
|
macro on*(server: var RpcServer, path: string, body: untyped): untyped =
|
|
|
|
result = newStmtList()
|
|
|
|
var setup = newStmtList()
|
|
|
|
let
|
|
|
|
parameters = body.findChild(it.kind == nnkFormalParams)
|
|
|
|
paramsIdent = ident"params"
|
|
|
|
setup.setupParams(parameters, paramsIdent)
|
2018-04-25 19:18:42 +01:00
|
|
|
|
2018-05-01 20:32:28 +01:00
|
|
|
# wrapping proc
|
2018-04-20 21:19:08 +01:00
|
|
|
let
|
|
|
|
pathStr = $path
|
2018-04-24 13:41:59 +01:00
|
|
|
procName = ident(pathStr.multiRemove(".", "/")) # TODO: Make this unique to avoid potential clashes, or allow people to know the name for calling?
|
|
|
|
var procBody: NimNode
|
2018-04-20 21:19:08 +01:00
|
|
|
if body.kind == nnkStmtList: procBody = body
|
|
|
|
else: procBody = body.body
|
|
|
|
result = quote do:
|
|
|
|
proc `procName`*(`paramsIdent`: JsonNode): Future[JsonNode] {.async.} =
|
2018-05-01 20:32:28 +01:00
|
|
|
#`checkTypeError`
|
|
|
|
`setup`
|
2018-04-20 21:19:08 +01:00
|
|
|
`procBody`
|
|
|
|
`server`.register(`path`, `procName`)
|
2018-04-26 19:35:57 +01:00
|
|
|
when defined(nimDumpRpcs):
|
2018-05-01 20:32:28 +01:00
|
|
|
echo pathStr, ": ", result.repr
|
|
|
|
|
2018-04-20 21:19:08 +01:00
|
|
|
when isMainModule:
|
2018-04-24 13:41:59 +01:00
|
|
|
import unittest
|
2018-04-20 21:19:08 +01:00
|
|
|
var s = newRpcServer("localhost")
|
2018-04-26 19:35:57 +01:00
|
|
|
s.on("rpc.arrayparam") do(arr: array[0..5, byte], b: string):
|
2018-04-24 17:37:01 +01:00
|
|
|
var res = newJArray()
|
2018-04-24 19:21:51 +01:00
|
|
|
for item in arr:
|
|
|
|
res.add %int(item)
|
|
|
|
res.add %b
|
|
|
|
result = %res
|
2018-05-01 20:32:28 +01:00
|
|
|
s.on("rpc.seqparam") do(a: string, s: seq[int]):
|
2018-04-24 19:21:51 +01:00
|
|
|
var res = newJArray()
|
2018-05-01 20:32:28 +01:00
|
|
|
res.add %a
|
2018-04-26 19:35:57 +01:00
|
|
|
for item in s:
|
2018-04-24 17:37:01 +01:00
|
|
|
res.add %int(item)
|
|
|
|
result = res
|
2018-05-02 16:21:05 +01:00
|
|
|
|
|
|
|
type
|
|
|
|
Test2 = object
|
|
|
|
x: array[2, int]
|
|
|
|
Test = object
|
|
|
|
d: array[0..1, int]
|
|
|
|
e: Test2
|
2018-05-01 20:32:28 +01:00
|
|
|
|
2018-04-26 19:35:57 +01:00
|
|
|
type MyObject* = object
|
|
|
|
a: int
|
2018-05-01 20:32:28 +01:00
|
|
|
b: Test
|
2018-04-26 19:35:57 +01:00
|
|
|
c: float
|
2018-05-03 20:20:10 +01:00
|
|
|
|
2018-05-01 20:32:28 +01:00
|
|
|
s.on("rpc.objparam") do(a: string, obj: MyObject):
|
2018-04-26 19:35:57 +01:00
|
|
|
result = %obj
|
2018-05-03 22:40:28 +01:00
|
|
|
s.on("rpc.uinttypes") do(a: byte, b: uint16, c: uint32):
|
|
|
|
result = %[int(a), int(b), int(c)]
|
|
|
|
s.on("rpc.inttypes") do(a: int8, b: int16, c: int32, d: int8, e: int16, f: int32):
|
|
|
|
result = %[int(a), int(b), int(c), int(d), int(e), int(f)]
|
2018-05-03 20:20:10 +01:00
|
|
|
|
2018-04-24 13:41:59 +01:00
|
|
|
suite "Server types":
|
2018-04-24 19:21:51 +01:00
|
|
|
test "Array/seq parameters":
|
2018-04-26 19:35:57 +01:00
|
|
|
let r1 = waitfor rpcArrayParam(%[%[1, 2, 3], %"hello"])
|
2018-04-24 19:21:51 +01:00
|
|
|
var ckR1 = %[1, 2, 3, 0, 0, 0]
|
|
|
|
ckR1.elems.add %"hello"
|
|
|
|
check r1 == ckR1
|
|
|
|
|
2018-04-26 19:35:57 +01:00
|
|
|
let r2 = waitfor rpcSeqParam(%[%"abc", %[1, 2, 3, 4, 5]])
|
2018-04-24 19:21:51 +01:00
|
|
|
var ckR2 = %["abc"]
|
|
|
|
for i in 0..4: ckR2.add %(i + 1)
|
|
|
|
check r2 == ckR2
|
2018-04-26 19:35:57 +01:00
|
|
|
test "Object parameters":
|
|
|
|
let
|
2018-05-02 16:21:05 +01:00
|
|
|
obj = %*{"a": %1, "b": %*{"d": %[5, 0], "e": %*{"x": %[1, 1]}}, "c": %1.23}
|
2018-05-03 20:20:10 +01:00
|
|
|
r = waitfor rpcObjParam(%[%"Test", obj])
|
2018-04-26 19:35:57 +01:00
|
|
|
check r == obj
|
2018-05-02 16:21:05 +01:00
|
|
|
expect ValueError:
|
|
|
|
# here we fail to provide one of the nested fields in json to the rpc
|
2018-05-03 20:20:10 +01:00
|
|
|
# TODO: Should this be allowed? We either allow partial non-ambiguous parsing or not
|
2018-05-02 16:21:05 +01:00
|
|
|
# Currently, as long as the Nim fields are satisfied, other fields are ignored
|
|
|
|
let
|
|
|
|
obj = %*{"a": %1, "b": %*{"a": %[5, 0]}, "c": %1.23}
|
|
|
|
discard waitFor rpcObjParam(%[%"abc", obj]) # Why doesn't asyncCheck raise?
|
2018-05-03 22:40:28 +01:00
|
|
|
test "Uint types":
|
|
|
|
let
|
|
|
|
testCase = %[%255, %65534, %4294967295]
|
|
|
|
r = waitfor rpcUIntTypes(testCase)
|
|
|
|
check r == testCase
|
|
|
|
test "Int types":
|
|
|
|
let
|
|
|
|
testCase = %[
|
|
|
|
%(127), %(32767), %(2147483647),
|
|
|
|
%(-128), %(-32768), %(-2147483648)
|
|
|
|
]
|
|
|
|
r = waitfor rpcIntTypes(testCase)
|
|
|
|
check r == testCase
|
2018-04-25 19:18:42 +01:00
|
|
|
test "Runtime errors":
|
|
|
|
expect ValueError:
|
2018-05-01 20:32:28 +01:00
|
|
|
echo waitfor rpcArrayParam(%[%[0, 1, 2, 3, 4, 5, 6], %"hello"])
|
|
|
|
|
2018-05-03 22:40:28 +01:00
|
|
|
# TODO: Split runtime strictness checking into defines - is there ever a reason to trust input?
|
|
|
|
# TODO: Add path as constant for each rpc
|