mirror of
https://github.com/logos-storage/nim-json-rpc.git
synced 2026-01-09 00:53:07 +00:00
add optional arg support to rpc macro
This commit is contained in:
parent
814dd7254b
commit
ee3ba6d5ad
@ -18,7 +18,7 @@ template expectType*(actual: JsonNodeKind, expected: typedesc, argName: string,
|
||||
elif expected is string:
|
||||
expType = JString
|
||||
else:
|
||||
const eStr = "Unable to convert " & expected.name & " to JSON for expectType"
|
||||
const eStr = "Unable to convert " & expected.name & " to JSON for expectType"
|
||||
{.fatal: eStr}
|
||||
if actual != expType:
|
||||
if allowNull == false or (allowNull and actual != JNull):
|
||||
@ -144,48 +144,108 @@ proc expectArrayLen(node: NimNode, jsonIdent: untyped, length: int) =
|
||||
raise newException(ValueError, `expectedStr` & $`jsonIdent`.len)
|
||||
)
|
||||
|
||||
proc jsonToNim*(assignIdent, paramType, jsonIdent: NimNode, paramNameStr: string): NimNode =
|
||||
iterator paramsIter(params: NimNode): tuple[name, ntype: NimNode] =
|
||||
for i in 1 ..< params.len:
|
||||
let arg = params[i]
|
||||
let argType = arg[^2]
|
||||
for j in 0 ..< arg.len-2:
|
||||
yield (arg[j], argType)
|
||||
|
||||
proc isOptionalArg(typeNode: NimNode): bool =
|
||||
if typeNode.kind != nnkBracketExpr:
|
||||
result = false
|
||||
return
|
||||
|
||||
result = typeNode[0].kind == nnkIdent and
|
||||
typeNode[0].strVal == "Option"
|
||||
|
||||
proc expectOptionalArrayLen(node, parameters: NimNode, jsonIdent: untyped, maxLength: int) =
|
||||
var
|
||||
meetOptional = false
|
||||
minLength = 0
|
||||
idx = 0
|
||||
|
||||
for arg, typ in paramsIter(parameters):
|
||||
if typ.isOptionalArg:
|
||||
if not meetOptional: minLength = idx
|
||||
meetOptional = true
|
||||
else:
|
||||
if meetOptional:
|
||||
macros.error("cannot have regular parameters: `" & $arg & "` after optional one", arg)
|
||||
inc idx
|
||||
|
||||
let
|
||||
identStr = jsonIdent.repr
|
||||
expectedStr = "Expected at least " & $minLength & " and maximum " & $maxLength & " Json parameter(s) but got "
|
||||
|
||||
node.add(quote do:
|
||||
`jsonIdent`.kind.expect(JArray, `identStr`)
|
||||
if `jsonIdent`.len < `minLength`:
|
||||
raise newException(ValueError, `expectedStr` & $`jsonIdent`.len)
|
||||
)
|
||||
|
||||
proc containsOptionalArg(params: NimNode): bool =
|
||||
for n, t in paramsIter(params):
|
||||
if t.isOptionalArg:
|
||||
result = true
|
||||
break
|
||||
|
||||
proc jsonToNim*(assignIdent, paramType, jsonIdent: NimNode, paramNameStr: string, optional = false): NimNode =
|
||||
# verify input and load a Nim type from json data
|
||||
# note: does not create `assignIdent`, so can be used for `result` variables
|
||||
result = newStmtList()
|
||||
# unpack each parameter and provide assignments
|
||||
result.add(quote do:
|
||||
`assignIdent` = `unpackArg`(`jsonIdent`, `paramNameStr`, type(`paramType`))
|
||||
)
|
||||
let unpackNode = quote do:
|
||||
`unpackArg`(`jsonIdent`, `paramNameStr`, type(`paramType`))
|
||||
|
||||
proc calcActualParamCount(parameters: NimNode): int =
|
||||
if optional:
|
||||
result.add(quote do: `assignIdent` = `some`(`unpackNode`))
|
||||
else:
|
||||
result.add(quote do: `assignIdent` = `unpackNode`)
|
||||
|
||||
proc calcActualParamCount(params: NimNode): int =
|
||||
# this proc is needed to calculate the actual parameter count
|
||||
# not matter what is the declaration form
|
||||
# e.g. (a: U, b: V) vs. (a, b: T)
|
||||
for i in 1 ..< parameters.len:
|
||||
inc(result, parameters[i].len-2)
|
||||
for n, t in paramsIter(params):
|
||||
inc result
|
||||
|
||||
proc jsonToNim*(parameters, jsonIdent: NimNode): NimNode =
|
||||
# Add code to verify input and load parameters into Nim types
|
||||
proc jsonToNim*(params, jsonIdent: NimNode): NimNode =
|
||||
# Add code to verify input and load params into Nim types
|
||||
result = newStmtList()
|
||||
if not parameters.isNil:
|
||||
# initial parameter array length check
|
||||
result.expectArrayLen(jsonIdent, calcActualParamCount(parameters))
|
||||
if not params.isNil:
|
||||
let paramsWithOptionalArg = params.containsOptionalArg()
|
||||
if paramsWithOptionalArg:
|
||||
# more elaborate parameters array check
|
||||
result.expectOptionalArrayLen(params, jsonIdent,
|
||||
calcActualParamCount(params))
|
||||
else:
|
||||
# simple parameters array length check
|
||||
result.expectArrayLen(jsonIdent, calcActualParamCount(params))
|
||||
|
||||
# unpack each parameter and provide assignments
|
||||
var pos = 0
|
||||
for i in 1 ..< parameters.len:
|
||||
let
|
||||
param = parameters[i]
|
||||
paramType = param[^2]
|
||||
|
||||
for paramIdent, paramType in paramsIter(params):
|
||||
# processing multiple variables of one type
|
||||
# e.g. (a, b: T), including common (a: U, b: V) form
|
||||
for j in 0 ..< param.len-2:
|
||||
let
|
||||
paramName = $paramIdent
|
||||
jsonElement = quote do:
|
||||
`jsonIdent`.elems[`pos`]
|
||||
|
||||
inc pos
|
||||
# declare variable before assignment
|
||||
result.add(quote do:
|
||||
var `paramIdent`: `paramType`
|
||||
)
|
||||
|
||||
if paramType.isOptionalArg:
|
||||
let
|
||||
paramIdent = param[j]
|
||||
paramName = $paramIdent
|
||||
jsonElement = quote do:
|
||||
`jsonIdent`.elems[`pos`]
|
||||
inc pos
|
||||
# declare variable before assignment
|
||||
innerType = paramType[1]
|
||||
innerNode = jsonToNim(paramIdent, innerType, jsonElement, paramName, true)
|
||||
result.add(quote do:
|
||||
var `paramIdent`: `paramType`
|
||||
if `jsonIdent`.len >= `pos`: `innerNode`
|
||||
)
|
||||
else:
|
||||
# unpack Nim type and assign from json
|
||||
result.add jsonToNim(paramIdent, paramType, jsonElement, paramName)
|
||||
|
||||
@ -34,7 +34,6 @@ let
|
||||
var s = newRpcSocketServer(["localhost:8545"])
|
||||
|
||||
# RPC definitions
|
||||
|
||||
s.rpc("rpc.simplepath"):
|
||||
result = %1
|
||||
|
||||
@ -72,9 +71,14 @@ s.rpc("rpc.multivarsofonetype") do(a, b: string) -> string:
|
||||
s.rpc("rpc.optional") do(obj: MyOptional) -> MyOptional:
|
||||
result = obj
|
||||
|
||||
s.rpc("rpc.optionalArg") do(val: int, obj: Option[MyOptional]) -> MyOptional:
|
||||
if obj.isSome():
|
||||
result = obj.get()
|
||||
else:
|
||||
result = MyOptional(maybeInt: some(val))
|
||||
|
||||
# Tests
|
||||
suite "Server types":
|
||||
|
||||
test "On macro registration":
|
||||
check s.hasMethod("rpc.simplepath")
|
||||
check s.hasMethod("rpc.differentparams")
|
||||
@ -85,6 +89,7 @@ suite "Server types":
|
||||
check s.hasMethod("rpc.returntypecomplex")
|
||||
check s.hasMethod("rpc.testreturns")
|
||||
check s.hasMethod("rpc.multivarsofonetype")
|
||||
check s.hasMethod("rpc.optionalArg")
|
||||
|
||||
test "Simple paths":
|
||||
let r = waitFor rpcSimplePath(%[])
|
||||
@ -156,5 +161,14 @@ suite "Server types":
|
||||
let r = waitfor rpcMultiVarsOfOneType(%[%"hello", %"world"])
|
||||
check r == %"hello world"
|
||||
|
||||
test "Optional arg":
|
||||
let
|
||||
int1 = MyOptional(maybeInt: some(75))
|
||||
int2 = MyOptional(maybeInt: some(117))
|
||||
r1 = waitFor rpcOptionalArg(%[%117, %int1])
|
||||
r2 = waitFor rpcOptionalArg(%[%117])
|
||||
check r1 == %int1
|
||||
check r2 == %int2
|
||||
|
||||
s.stop()
|
||||
waitFor s.closeWait()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user