mirror of https://github.com/status-im/NimYAML.git
Can load fields of parent types. ref #131
* dumping fields of parent types not working yet
This commit is contained in:
parent
8e1b07975b
commit
576cf11d81
|
@ -61,6 +61,11 @@ type
|
||||||
WithIgnoredField {.ignore: ["z"].} = object
|
WithIgnoredField {.ignore: ["z"].} = object
|
||||||
x, y: int
|
x, y: int
|
||||||
|
|
||||||
|
Parent = object of RootObj
|
||||||
|
i*: int
|
||||||
|
Child = object of Parent
|
||||||
|
s*: string
|
||||||
|
|
||||||
proc `$`(v: BetterInt): string {.borrow.}
|
proc `$`(v: BetterInt): string {.borrow.}
|
||||||
proc `==`(left, right: BetterInt): bool {.borrow.}
|
proc `==`(left, right: BetterInt): bool {.borrow.}
|
||||||
|
|
||||||
|
@ -458,6 +463,14 @@ suite "Serialization":
|
||||||
assertStringEqual(yamlDirs &
|
assertStringEqual(yamlDirs &
|
||||||
"!n!custom:Person \nfirstnamechar: P\nsurname: Pan\nage: 12\n", output)
|
"!n!custom:Person \nfirstnamechar: P\nsurname: Pan\nage: 12\n", output)
|
||||||
|
|
||||||
|
test "Load object with inherited fields":
|
||||||
|
let input =
|
||||||
|
"i: 4\ns: hello"
|
||||||
|
var result: Child
|
||||||
|
load(input, result)
|
||||||
|
assert result.i == 4
|
||||||
|
assert result.s == "hello"
|
||||||
|
|
||||||
test "Load custom variant object":
|
test "Load custom variant object":
|
||||||
let input =
|
let input =
|
||||||
"---\n- - name: Bastet\n - kind: akCat\n - purringIntensity: 7\n" &
|
"---\n- - name: Bastet\n - kind: akCat\n - purringIntensity: 7\n" &
|
||||||
|
|
|
@ -598,41 +598,65 @@ proc recListNode(n: NimNode): NimNode {.compileTime.} =
|
||||||
if n.kind == nnkRecList: result = n[0]
|
if n.kind == nnkRecList: result = n[0]
|
||||||
else: result = n
|
else: result = n
|
||||||
|
|
||||||
proc fieldCount(t: NimNode): int {.compiletime.} =
|
proc parentType(tDesc: NimNode): NimNode {.compileTime.} =
|
||||||
result = 0
|
var name: NimNode
|
||||||
let tDesc = getType(getType(t)[1])
|
case tDesc[1].kind
|
||||||
if tDesc.kind == nnkBracketExpr:
|
of nnkEmpty: return nil
|
||||||
# tuple
|
of nnkBracketExpr:
|
||||||
result = tDesc.len - 1
|
# happens when parent type is `ref X`
|
||||||
else:
|
name = tDesc[1][1]
|
||||||
# object
|
of nnkObjectTy, nnkSym:
|
||||||
for child in tDesc[2].children:
|
name = tDesc[1]
|
||||||
inc(result)
|
else:
|
||||||
if child.kind == nnkRecCase:
|
return nil
|
||||||
for bIndex in 1..<len(child):
|
result = newNimNode(nnkBracketExpr)
|
||||||
var increment = 0
|
result.add(bindSym("typeDesc"))
|
||||||
case child[bIndex].kind
|
result.add(name)
|
||||||
of nnkOfBranch:
|
|
||||||
let content = child[bIndex][len(child[bIndex])-1]
|
|
||||||
# We cannot assume that child[bIndex][1] is a RecList due to
|
|
||||||
# a one-liner like 'of akDog: barkometer' not resulting in a
|
|
||||||
# RecList but in an Ident node.
|
|
||||||
case content.kind
|
|
||||||
of nnkRecList:
|
|
||||||
increment = len(content)
|
|
||||||
else:
|
|
||||||
increment = 1
|
|
||||||
of nnkElse:
|
|
||||||
# Same goes for the else branch.
|
|
||||||
case child[bIndex][0].kind
|
|
||||||
of nnkRecList:
|
|
||||||
increment = len(child[bIndex][0])
|
|
||||||
else:
|
|
||||||
increment = 1
|
|
||||||
else:
|
|
||||||
internalError("Unexpected child kind: " & $child[bIndex].kind)
|
|
||||||
inc(result, increment)
|
|
||||||
|
|
||||||
|
proc fieldCount(t: NimNode): int {.compiletime.} =
|
||||||
|
result = 0
|
||||||
|
var tTypedesc: NimNode
|
||||||
|
if t.kind == nnkSym:
|
||||||
|
tTypedesc = getType(t)
|
||||||
|
else:
|
||||||
|
tTypedesc = t
|
||||||
|
|
||||||
|
let tDesc = getType(tTypedesc[1])
|
||||||
|
if tDesc.kind == nnkBracketExpr:
|
||||||
|
# tuple
|
||||||
|
result = tDesc.len - 1
|
||||||
|
else:
|
||||||
|
# object
|
||||||
|
let tParent = parentType(tDesc)
|
||||||
|
if tParent != nil:
|
||||||
|
# inherited fields
|
||||||
|
result += fieldCount(tParent)
|
||||||
|
for child in tDesc[2].children:
|
||||||
|
inc(result)
|
||||||
|
if child.kind == nnkRecCase:
|
||||||
|
for bIndex in 1..<len(child):
|
||||||
|
var increment = 0
|
||||||
|
case child[bIndex].kind
|
||||||
|
of nnkOfBranch:
|
||||||
|
let content = child[bIndex][len(child[bIndex])-1]
|
||||||
|
# We cannot assume that child[bIndex][1] is a RecList due to
|
||||||
|
# a one-liner like 'of akDog: barkometer' not resulting in a
|
||||||
|
# RecList but in an Ident node.
|
||||||
|
case content.kind
|
||||||
|
of nnkRecList:
|
||||||
|
increment = len(content)
|
||||||
|
else:
|
||||||
|
increment = 1
|
||||||
|
of nnkElse:
|
||||||
|
# Same goes for the else branch.
|
||||||
|
case child[bIndex][0].kind
|
||||||
|
of nnkRecList:
|
||||||
|
increment = len(child[bIndex][0])
|
||||||
|
else:
|
||||||
|
increment = 1
|
||||||
|
else:
|
||||||
|
internalError("Unexpected child kind: " & $child[bIndex].kind)
|
||||||
|
inc(result, increment)
|
||||||
|
|
||||||
macro matchMatrix(t: typedesc): untyped =
|
macro matchMatrix(t: typedesc): untyped =
|
||||||
let numFields = fieldCount(t)
|
let numFields = fieldCount(t)
|
||||||
|
@ -703,18 +727,18 @@ proc ifNotTransient(o, field: NimNode,
|
||||||
when not `o`.`field`.hasCustomPragma(transient):
|
when not `o`.`field`.hasCustomPragma(transient):
|
||||||
`stmts`
|
`stmts`
|
||||||
|
|
||||||
macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, o: typed,
|
proc recEnsureAllFieldsPresent(
|
||||||
matched: typed, m: Mark) =
|
s: NimNode, tDecl: NimNode, o: NimNode, matched: NimNode, m: NimNode,
|
||||||
result = newStmtList()
|
tName: string, field: var int, stmt: NimNode) {.compileTime.} =
|
||||||
let
|
var
|
||||||
tDecl = getType(t)
|
|
||||||
tName = $tDecl[1]
|
|
||||||
tDesc = getType(tDecl[1])
|
tDesc = getType(tDecl[1])
|
||||||
var field = 0
|
tParent = parentType(tDesc)
|
||||||
|
if tParent != nil:
|
||||||
|
recEnsureAllFieldsPresent(s, tParent, o, matched, m, tName, field, stmt)
|
||||||
for child in tDesc[2].children:
|
for child in tDesc[2].children:
|
||||||
if child.kind == nnkRecCase:
|
if child.kind == nnkRecCase:
|
||||||
result.add(checkMissing(
|
stmt.add(checkMissing(
|
||||||
s, t, tName, child[0], field, matched, o, m))
|
s, tDecl, tName, child[0], field, matched, o, m))
|
||||||
for bIndex in 1 .. len(child) - 1:
|
for bIndex in 1 .. len(child) - 1:
|
||||||
let discChecks = newStmtList()
|
let discChecks = newStmtList()
|
||||||
var
|
var
|
||||||
|
@ -731,13 +755,22 @@ macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, o: typed,
|
||||||
for item in child[bIndex][recListIndex].recListItems:
|
for item in child[bIndex][recListIndex].recListItems:
|
||||||
inc(field)
|
inc(field)
|
||||||
discChecks.add(checkMissing(
|
discChecks.add(checkMissing(
|
||||||
s, t, tName, item, field, matched, o, m))
|
s, tDecl, tName, item, field, matched, o, m))
|
||||||
result.add(newIfStmt((infix(newDotExpr(o, newIdentNode($child[0])),
|
stmt.add(newIfStmt((infix(newDotExpr(o, newIdentNode($child[0])),
|
||||||
"in", curValues), discChecks)))
|
"in", curValues), discChecks)))
|
||||||
else:
|
else:
|
||||||
result.add(checkMissing(s, t, tName, child, field, matched, o, m))
|
stmt.add(checkMissing(s, tDecl, tName, child, field, matched, o, m))
|
||||||
inc(field)
|
inc(field)
|
||||||
|
|
||||||
|
macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, o: typed,
|
||||||
|
matched: typed, m: Mark) =
|
||||||
|
result = newStmtList()
|
||||||
|
let
|
||||||
|
tDecl = getType(t)
|
||||||
|
tName = $tDecl[1]
|
||||||
|
var field = 0
|
||||||
|
recEnsureAllFieldsPresent(s, tDecl, o, matched, m, tName, field, result)
|
||||||
|
|
||||||
proc skipOverValue(s: var YamlStream) =
|
proc skipOverValue(s: var YamlStream) =
|
||||||
var e = s.next()
|
var e = s.next()
|
||||||
var depth = int(e.kind in {yamlStartMap, yamlStartSeq})
|
var depth = int(e.kind in {yamlStartMap, yamlStartSeq})
|
||||||
|
@ -748,16 +781,15 @@ proc skipOverValue(s: var YamlStream) =
|
||||||
of yamlScalar, yamlAlias: discard
|
of yamlScalar, yamlAlias: discard
|
||||||
else: internalError("Unexpected event kind.")
|
else: internalError("Unexpected event kind.")
|
||||||
|
|
||||||
macro constructFieldValue(t: typedesc, stream: untyped,
|
proc addFieldCases(
|
||||||
context: untyped, name: untyped, o: untyped,
|
tDecl: NimNode, stream: NimNode, context: NimNode, name: NimNode, o: NimNode,
|
||||||
matched: untyped, failOnUnknown: bool, m: untyped) =
|
matched: NimNode, failOnUnknown: NimNode, m: NimNode, tName: string, caseStmt: NimNode,
|
||||||
let
|
fieldIndex: var int) {.compileTime.} =
|
||||||
tDecl = getType(t)
|
var
|
||||||
tName = $tDecl[1]
|
|
||||||
tDesc = getType(tDecl[1])
|
tDesc = getType(tDecl[1])
|
||||||
result = newStmtList()
|
tParent = parentType(tDesc)
|
||||||
var caseStmt = newNimNode(nnkCaseStmt).add(name)
|
if tParent != nil:
|
||||||
var fieldIndex = 0
|
addFieldCases(tParent, stream, context, name, o, matched, failOnUnknown, m, tName, caseStmt, fieldIndex)
|
||||||
for child in tDesc[2].children:
|
for child in tDesc[2].children:
|
||||||
if child.kind == nnkRecCase:
|
if child.kind == nnkRecCase:
|
||||||
let
|
let
|
||||||
|
@ -828,6 +860,18 @@ macro constructFieldValue(t: typedesc, stream: untyped,
|
||||||
markAsFound(fieldIndex, matched)], true, stream, m, tName, $child))
|
markAsFound(fieldIndex, matched)], true, stream, m, tName, $child))
|
||||||
caseStmt.add(ob)
|
caseStmt.add(ob)
|
||||||
inc(fieldIndex)
|
inc(fieldIndex)
|
||||||
|
|
||||||
|
macro constructFieldValue(t: typedesc, stream: untyped,
|
||||||
|
context: untyped, name: untyped, o: untyped,
|
||||||
|
matched: untyped, failOnUnknown: bool, m: untyped) =
|
||||||
|
let
|
||||||
|
tDecl = getType(t)
|
||||||
|
tName = $tDecl[1]
|
||||||
|
tDesc = getType(tDecl[1])
|
||||||
|
result = newStmtList()
|
||||||
|
var caseStmt = newNimNode(nnkCaseStmt).add(name)
|
||||||
|
var fieldIndex = 0
|
||||||
|
addFieldCases(tDecl, stream, context, name, o, matched, failOnUnknown, m, tName, caseStmt, fieldIndex)
|
||||||
caseStmt.add(newNimNode(nnkElse).add(newNimNode(nnkWhenStmt).add(
|
caseStmt.add(newNimNode(nnkElse).add(newNimNode(nnkWhenStmt).add(
|
||||||
newNimNode(nnkElifBranch).add(failOnUnknown,
|
newNimNode(nnkElifBranch).add(failOnUnknown,
|
||||||
newNimNode(nnkRaiseStmt).add(
|
newNimNode(nnkRaiseStmt).add(
|
||||||
|
@ -854,6 +898,11 @@ proc hasIgnore(t: typedesc): bool {.compileTime.} =
|
||||||
else:
|
else:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
proc constructObjectDefault*(
|
||||||
|
s: var YamlStream, c: ConstructionContext, result: var RootObj) =
|
||||||
|
# specialization of generic proc for RootObj, doesn't do anything
|
||||||
|
return
|
||||||
|
|
||||||
proc constructObjectDefault*[O: object|tuple](
|
proc constructObjectDefault*[O: object|tuple](
|
||||||
s: var YamlStream, c: ConstructionContext, result: var O)
|
s: var YamlStream, c: ConstructionContext, result: var O)
|
||||||
{.raises: [YamlConstructionError, YamlStreamError].} =
|
{.raises: [YamlConstructionError, YamlStreamError].} =
|
||||||
|
|
Loading…
Reference in New Issue