Document to macro in JSON and add example.

This commit is contained in:
Dominik Picheta 2017-04-09 12:48:07 +02:00
commit eedc6fecd7
2 changed files with 71 additions and 22 deletions

View file

@ -1336,6 +1336,7 @@ proc createGetEnumCall(jsonNode, kindType: NimNode): NimNode =
return getEnumCall return getEnumCall
proc createOfBranchCond(ofBranch, getEnumCall: NimNode): NimNode = proc createOfBranchCond(ofBranch, getEnumCall: NimNode): NimNode =
## Creates an expression that acts as the condition for an ``of`` branch.
var cond = newIdentNode("false") var cond = newIdentNode("false")
for ofCond in ofBranch: for ofCond in ofBranch:
if ofCond.kind == nnkRecList: if ofCond.kind == nnkRecList:
@ -1346,20 +1347,6 @@ proc createOfBranchCond(ofBranch, getEnumCall: NimNode): NimNode =
return cond return cond
proc createIfStmtForOf(ofBranch, jsonNode, kindType,
value: NimNode): NimNode {.compileTime.} =
## Transforms a case ``of`` branch into an if statement to be placed as the
## ExprColonExpr body expr.
expectKind(ofBranch, nnkOfBranch)
let getEnumCall = createGetEnumCall(jsonNode, kindType)
let cond = createOfBranchCond(ofBranch, getEnumCall)
return newIfStmt(
(cond, value)
)
proc createConstructor(typeSym, jsonNode: NimNode): NimNode {.compileTime.}
proc processObjField(field, jsonNode: NimNode): seq[NimNode] {.compileTime.} proc processObjField(field, jsonNode: NimNode): seq[NimNode] {.compileTime.}
proc processOfBranch(ofBranch, jsonNode, kindType, proc processOfBranch(ofBranch, jsonNode, kindType,
kindJsonNode: NimNode): seq[NimNode] {.compileTime.} = kindJsonNode: NimNode): seq[NimNode] {.compileTime.} =
@ -1377,6 +1364,8 @@ proc processOfBranch(ofBranch, jsonNode, kindType,
## Sym "foodPos" ## Sym "foodPos"
## Sym "enemyPos" ## Sym "enemyPos"
result = @[] result = @[]
let getEnumCall = createGetEnumCall(kindJsonNode, kindType)
for branchField in ofBranch[^1]: for branchField in ofBranch[^1]:
let objFields = processObjField(branchField, jsonNode) let objFields = processObjField(branchField, jsonNode)
@ -1387,8 +1376,10 @@ proc processOfBranch(ofBranch, jsonNode, kindType,
exprColonExpr.add(toIdentNode(objField[0])) exprColonExpr.add(toIdentNode(objField[0]))
# Add the value of the field. # Add the value of the field.
let ifStmt = createIfStmtForOf(ofBranch, kindJsonNode, kindType, objField[1]) let cond = createOfBranchCond(ofBranch, getEnumCall)
exprColonExpr.add(ifStmt) exprColonExpr.add(newIfStmt(
(cond, objField[1])
))
proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType, proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType,
kindJsonNode: NimNode): seq[NimNode] {.compileTime.} = kindJsonNode: NimNode): seq[NimNode] {.compileTime.} =
@ -1427,6 +1418,7 @@ proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType,
let ifStmt = newIfStmt((cond, objField[1])) let ifStmt = newIfStmt((cond, objField[1]))
exprColonExpr.add(ifStmt) exprColonExpr.add(ifStmt)
proc createConstructor(typeSym, jsonNode: NimNode): NimNode {.compileTime.}
proc processObjField(field, jsonNode: NimNode): seq[NimNode] = proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
## Process a field from a ``RecList``. ## Process a field from a ``RecList``.
## ##
@ -1541,8 +1533,8 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
## The ``jsonNode`` refers to the node variable that we are deserialising. ## The ``jsonNode`` refers to the node variable that we are deserialising.
## ##
## Returns an object constructor node. ## Returns an object constructor node.
echo("--createConsuctor-- \n", treeRepr(typeSym)) # echo("--createConsuctor-- \n", treeRepr(typeSym))
echo() # echo()
case typeSym.kind case typeSym.kind
of nnkBracketExpr: of nnkBracketExpr:
@ -1576,7 +1568,6 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
else: else:
# Generic type. # Generic type.
let obj = getType(typeSym) let obj = getType(typeSym)
echo(obj.treeRepr, typeSym[0].treeRepr)
result = processType(typeSym, obj, jsonNode) result = processType(typeSym, obj, jsonNode)
of nnkSym: of nnkSym:
let obj = getType(typeSym) let obj = getType(typeSym)
@ -1667,6 +1658,40 @@ proc postProcess(node: NimNode): NimNode =
macro to*(node: JsonNode, T: typedesc): untyped = macro to*(node: JsonNode, T: typedesc): untyped =
## `Unmarshals`:idx: the specified node into the object type specified.
##
## Known limitations:
##
## * Heterogeneous arrays are not supported.
## * Sets in object variants are not supported.
##
## Example:
##
## .. code-block:: Nim
## let jsonNode = parseJson("""
## {
## "person": {
## "name": "Nimmer",
## "age": 21
## },
## "list": [1, 2, 3, 4]
## }
## """)
##
## type
## Person = object
## name: string
## age: int
##
## Data = object
## person: Person
## list: seq[int]
##
## var data = to(jsonNode, Data)
## doAssert data.person.name == "Nimmer"
## doAssert data.person.age == 21
## doAssert data.list == @[1, 2, 3, 4]
let typeNode = getType(T) let typeNode = getType(T)
expectKind(typeNode, nnkBracketExpr) expectKind(typeNode, nnkBracketExpr)
assert(($typeNode[0]).normalize == "typedesc") assert(($typeNode[0]).normalize == "typedesc")
@ -1674,9 +1699,7 @@ macro to*(node: JsonNode, T: typedesc): untyped =
result = createConstructor(typeNode[1], node) result = createConstructor(typeNode[1], node)
result = postProcess(result) result = postProcess(result)
echo(toStrLit(result)) #echo(toStrLit(result))
# TODO: Remove this
#result = newStmtList()
when false: when false:
import os import os

View file

@ -91,6 +91,7 @@ when isMainModule:
doAssert result.other == node["other"].getNum() doAssert result.other == node["other"].getNum()
# TODO: Test object variant with set in of branch. # TODO: Test object variant with set in of branch.
# TODO: Should we support heterogenous arrays?
# Tests that verify the error messages for invalid data. # Tests that verify the error messages for invalid data.
block: block:
@ -141,3 +142,28 @@ when isMainModule:
except: except:
doAssert false doAssert false
# Test the example in json module.
block:
let jsonNode = parseJson("""
{
"person": {
"name": "Nimmer",
"age": 21
},
"list": [1, 2, 3, 4]
}
""")
type
Person = object
name: string
age: int
Data = object
person: Person
list: seq[int]
var data = to(jsonNode, Data)
doAssert data.person.name == "Nimmer"
doAssert data.person.age == 21
doAssert data.list == @[1, 2, 3, 4]