mirror of
https://github.com/status-im/nim-json-rpc.git
synced 2025-02-24 10:18:15 +00:00
allow optional parameters in the middle of parameters list
This commit is contained in:
parent
ee3ba6d5ad
commit
a1fe7d57b4
32
README.md
32
README.md
@ -97,6 +97,37 @@ At runtime, the json is checked to ensure that it contains the correct number an
|
|||||||
|
|
||||||
Compiling with `-d:nimDumpRpcs` will show the output code for the RPC call. To see the output of the `async` generation, add `-d:nimDumpAsync`.
|
Compiling with `-d:nimDumpRpcs` will show the output code for the RPC call. To see the output of the `async` generation, add `-d:nimDumpAsync`.
|
||||||
|
|
||||||
|
### Special type : `Option[T] `
|
||||||
|
|
||||||
|
Option[T] is a special type indicating that parameter may have value or not.
|
||||||
|
* If optional parameters located in the middle of parameters list, you set it to `null` to tell the server that it has no value.
|
||||||
|
* If optional parameters located at the end of parameter list and there are no more mandatory parameters after that, those optional parameters can be omitted altogether.
|
||||||
|
|
||||||
|
```Nim
|
||||||
|
# d can be omitted, b should use null to indicate it has no value
|
||||||
|
router.rpc("updateData") do(a: int, b: Option[int], c: string, d: Option[T]):
|
||||||
|
if b.isSome:
|
||||||
|
# do something
|
||||||
|
else:
|
||||||
|
# do something else
|
||||||
|
```
|
||||||
|
|
||||||
|
* If Option[T] used as return type, it also denotes the returned value might not available.
|
||||||
|
|
||||||
|
```Nim
|
||||||
|
router.rpc("getData") do(name: string) -> Option[int]:
|
||||||
|
if name == "monkey":
|
||||||
|
result = some(4)
|
||||||
|
```
|
||||||
|
|
||||||
|
* If Option[T] used as field type of an object, it also tell us that field might be present or not, and the rpc mechanism will automatically set it to some value if it available.
|
||||||
|
|
||||||
|
```Nim
|
||||||
|
type
|
||||||
|
MyOptional = object
|
||||||
|
maybeInt: Option[int]
|
||||||
|
```
|
||||||
|
|
||||||
## Marshalling
|
## Marshalling
|
||||||
|
|
||||||
Note that `array` parameters are explicitly checked for length, and will return an error node if the length differs from their declaration size.
|
Note that `array` parameters are explicitly checked for length, and will return an error node if the length differs from their declaration size.
|
||||||
@ -413,3 +444,4 @@ Licensed and distributed under either of
|
|||||||
|
|
||||||
at your option. This file may not be copied, modified, or distributed except according to those terms.
|
at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
|
|
||||||
|
@ -151,6 +151,13 @@ iterator paramsIter(params: NimNode): tuple[name, ntype: NimNode] =
|
|||||||
for j in 0 ..< arg.len-2:
|
for j in 0 ..< arg.len-2:
|
||||||
yield (arg[j], argType)
|
yield (arg[j], argType)
|
||||||
|
|
||||||
|
iterator paramsRevIter(params: NimNode): tuple[name, ntype: NimNode] =
|
||||||
|
for i in countDown(params.len-1,0):
|
||||||
|
let arg = params[i]
|
||||||
|
let argType = arg[^2]
|
||||||
|
for j in 0 ..< arg.len-2:
|
||||||
|
yield (arg[j], argType)
|
||||||
|
|
||||||
proc isOptionalArg(typeNode: NimNode): bool =
|
proc isOptionalArg(typeNode: NimNode): bool =
|
||||||
if typeNode.kind != nnkBracketExpr:
|
if typeNode.kind != nnkBracketExpr:
|
||||||
result = false
|
result = false
|
||||||
@ -159,20 +166,12 @@ proc isOptionalArg(typeNode: NimNode): bool =
|
|||||||
result = typeNode[0].kind == nnkIdent and
|
result = typeNode[0].kind == nnkIdent and
|
||||||
typeNode[0].strVal == "Option"
|
typeNode[0].strVal == "Option"
|
||||||
|
|
||||||
proc expectOptionalArrayLen(node, parameters: NimNode, jsonIdent: untyped, maxLength: int) =
|
proc expectOptionalArrayLen(node, parameters: NimNode, jsonIdent: untyped, maxLength: int): int =
|
||||||
var
|
var minLength = maxLength
|
||||||
meetOptional = false
|
|
||||||
minLength = 0
|
|
||||||
idx = 0
|
|
||||||
|
|
||||||
for arg, typ in paramsIter(parameters):
|
for arg, typ in paramsRevIter(parameters):
|
||||||
if typ.isOptionalArg:
|
if not typ.isOptionalArg: break
|
||||||
if not meetOptional: minLength = idx
|
dec minLength
|
||||||
meetOptional = true
|
|
||||||
else:
|
|
||||||
if meetOptional:
|
|
||||||
macros.error("cannot have regular parameters: `" & $arg & "` after optional one", arg)
|
|
||||||
inc idx
|
|
||||||
|
|
||||||
let
|
let
|
||||||
identStr = jsonIdent.repr
|
identStr = jsonIdent.repr
|
||||||
@ -184,6 +183,8 @@ proc expectOptionalArrayLen(node, parameters: NimNode, jsonIdent: untyped, maxLe
|
|||||||
raise newException(ValueError, `expectedStr` & $`jsonIdent`.len)
|
raise newException(ValueError, `expectedStr` & $`jsonIdent`.len)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
result = minLength
|
||||||
|
|
||||||
proc containsOptionalArg(params: NimNode): bool =
|
proc containsOptionalArg(params: NimNode): bool =
|
||||||
for n, t in paramsIter(params):
|
for n, t in paramsIter(params):
|
||||||
if t.isOptionalArg:
|
if t.isOptionalArg:
|
||||||
@ -214,10 +215,10 @@ proc jsonToNim*(params, jsonIdent: NimNode): NimNode =
|
|||||||
# Add code to verify input and load params into Nim types
|
# Add code to verify input and load params into Nim types
|
||||||
result = newStmtList()
|
result = newStmtList()
|
||||||
if not params.isNil:
|
if not params.isNil:
|
||||||
let paramsWithOptionalArg = params.containsOptionalArg()
|
var minLength = 0
|
||||||
if paramsWithOptionalArg:
|
if params.containsOptionalArg():
|
||||||
# more elaborate parameters array check
|
# more elaborate parameters array check
|
||||||
result.expectOptionalArrayLen(params, jsonIdent,
|
minLength = result.expectOptionalArrayLen(params, jsonIdent,
|
||||||
calcActualParamCount(params))
|
calcActualParamCount(params))
|
||||||
else:
|
else:
|
||||||
# simple parameters array length check
|
# simple parameters array length check
|
||||||
@ -241,8 +242,15 @@ proc jsonToNim*(params, jsonIdent: NimNode): NimNode =
|
|||||||
|
|
||||||
if paramType.isOptionalArg:
|
if paramType.isOptionalArg:
|
||||||
let
|
let
|
||||||
|
nullAble = pos < minLength
|
||||||
innerType = paramType[1]
|
innerType = paramType[1]
|
||||||
innerNode = jsonToNim(paramIdent, innerType, jsonElement, paramName, true)
|
innerNode = jsonToNim(paramIdent, innerType, jsonElement, paramName, true)
|
||||||
|
|
||||||
|
if nullAble:
|
||||||
|
result.add(quote do:
|
||||||
|
if `jsonElement`.kind != JNull: `innerNode`
|
||||||
|
)
|
||||||
|
else:
|
||||||
result.add(quote do:
|
result.add(quote do:
|
||||||
if `jsonIdent`.len >= `pos`: `innerNode`
|
if `jsonIdent`.len >= `pos`: `innerNode`
|
||||||
)
|
)
|
||||||
|
@ -77,6 +77,23 @@ s.rpc("rpc.optionalArg") do(val: int, obj: Option[MyOptional]) -> MyOptional:
|
|||||||
else:
|
else:
|
||||||
result = MyOptional(maybeInt: some(val))
|
result = MyOptional(maybeInt: some(val))
|
||||||
|
|
||||||
|
type
|
||||||
|
OptionalFields = object
|
||||||
|
a: int
|
||||||
|
b: Option[int]
|
||||||
|
c: string
|
||||||
|
d: Option[int]
|
||||||
|
e: Option[string]
|
||||||
|
|
||||||
|
s.rpc("rpc.mixedOptionalArg") do(a: int, b: Option[int], c: string,
|
||||||
|
d: Option[int], e: Option[string]) -> OptionalFields:
|
||||||
|
|
||||||
|
result.a = a
|
||||||
|
result.b = b
|
||||||
|
result.c = c
|
||||||
|
result.d = d
|
||||||
|
result.e = e
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
suite "Server types":
|
suite "Server types":
|
||||||
test "On macro registration":
|
test "On macro registration":
|
||||||
@ -170,5 +187,12 @@ suite "Server types":
|
|||||||
check r1 == %int1
|
check r1 == %int1
|
||||||
check r2 == %int2
|
check r2 == %int2
|
||||||
|
|
||||||
|
test "mixed optional arg":
|
||||||
|
var ax = waitFor rpcMixedOptionalArg(%[%10, %11, %"hello", %12, %"world"])
|
||||||
|
check ax == %OptionalFields(a: 10, b: some(11), c: "hello", d: some(12), e: some("world"))
|
||||||
|
var bx = waitFor rpcMixedOptionalArg(%[%10, newJNull(), %"hello"])
|
||||||
|
check bx == %OptionalFields(a: 10, c: "hello")
|
||||||
|
|
||||||
s.stop()
|
s.stop()
|
||||||
waitFor s.closeWait()
|
waitFor s.closeWait()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user