Implement support for Option[T] in json.to macro. Fixes #5848.
This commit is contained in:
parent
d3c9b58c00
commit
8ca41ce637
2 changed files with 50 additions and 2 deletions
|
|
@ -1346,6 +1346,16 @@ proc createJsonIndexer(jsonNode: NimNode,
|
||||||
indexNode
|
indexNode
|
||||||
)
|
)
|
||||||
|
|
||||||
|
proc transformJsonIndexer(jsonNode: NimNode): NimNode =
|
||||||
|
case jsonNode.kind
|
||||||
|
of nnkBracketExpr:
|
||||||
|
result = newNimNode(nnkCurlyExpr)
|
||||||
|
else:
|
||||||
|
result = jsonNode.copy()
|
||||||
|
|
||||||
|
for child in jsonNode:
|
||||||
|
result.add(transformJsonIndexer(child))
|
||||||
|
|
||||||
template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
|
template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
|
||||||
ast: string) =
|
ast: string) =
|
||||||
if node.kind notin kinds:
|
if node.kind notin kinds:
|
||||||
|
|
@ -1637,6 +1647,10 @@ proc processType(typeName: NimNode, obj: NimNode,
|
||||||
|
|
||||||
doAssert(not result.isNil(), "processType not initialised.")
|
doAssert(not result.isNil(), "processType not initialised.")
|
||||||
|
|
||||||
|
import options
|
||||||
|
proc workaroundMacroNone[T](): Option[T] =
|
||||||
|
none(T)
|
||||||
|
|
||||||
proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
|
proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
|
||||||
## Accepts a type description, i.e. "ref Type", "seq[Type]", "Type" etc.
|
## Accepts a type description, i.e. "ref Type", "seq[Type]", "Type" etc.
|
||||||
##
|
##
|
||||||
|
|
@ -1650,6 +1664,19 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
|
||||||
of nnkBracketExpr:
|
of nnkBracketExpr:
|
||||||
var bracketName = ($typeSym[0]).normalize
|
var bracketName = ($typeSym[0]).normalize
|
||||||
case bracketName
|
case bracketName
|
||||||
|
of "option":
|
||||||
|
# TODO: Would be good to verify that this is Option[T] from
|
||||||
|
# options module I suppose.
|
||||||
|
let lenientJsonNode = transformJsonIndexer(jsonNode)
|
||||||
|
|
||||||
|
let optionGeneric = typeSym[1]
|
||||||
|
let value = createConstructor(typeSym[1], jsonNode)
|
||||||
|
let workaround = bindSym("workaroundMacroNone") # TODO: Nim Bug: This shouldn't be necessary.
|
||||||
|
|
||||||
|
result = quote do:
|
||||||
|
(
|
||||||
|
if `lenientJsonNode`.isNil: `workaround`[`optionGeneric`]() else: some[`optionGeneric`](`value`)
|
||||||
|
)
|
||||||
of "ref":
|
of "ref":
|
||||||
# Ref type.
|
# Ref type.
|
||||||
var typeName = $typeSym[1]
|
var typeName = $typeSym[1]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ discard """
|
||||||
file: "tjsonmacro.nim"
|
file: "tjsonmacro.nim"
|
||||||
output: ""
|
output: ""
|
||||||
"""
|
"""
|
||||||
import json, strutils
|
import json, strutils, options
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
# Tests inspired by own use case (with some additional tests).
|
# Tests inspired by own use case (with some additional tests).
|
||||||
|
|
@ -296,3 +296,24 @@ when isMainModule:
|
||||||
doAssert parsed.engine.name == "V8"
|
doAssert parsed.engine.name == "V8"
|
||||||
|
|
||||||
doAssert i == 1
|
doAssert i == 1
|
||||||
|
|
||||||
|
block:
|
||||||
|
# Option[T] support!
|
||||||
|
type
|
||||||
|
Car1 = object # TODO: Codegen bug when `Car`
|
||||||
|
engine: tuple[name: string, capacity: Option[float]]
|
||||||
|
model: string
|
||||||
|
year: Option[int]
|
||||||
|
|
||||||
|
let noYear = """
|
||||||
|
{"engine": {"name": "V8", "capacity": 5.5}, "model": "Skyline"}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let noYearParsed = parseJson(noYear)
|
||||||
|
let noYearDeser = to(noYearParsed, Car1)
|
||||||
|
doAssert noYearDeser.engine.capacity == some(5.5)
|
||||||
|
doAssert noYearDeser.year.isNone
|
||||||
|
doAssert noYearDeser.engine.name == "V8"
|
||||||
|
|
||||||
|
# TODO: Table[T, Y] support.
|
||||||
|
# TODO: JsonNode support
|
||||||
Loading…
Add table
Add a link
Reference in a new issue