This commit is contained in:
Felix Krause 2017-09-20 18:58:09 +02:00
parent af58ab68de
commit 8d8464da71
1 changed files with 41 additions and 12 deletions

View File

@ -638,6 +638,8 @@ let
newIdentNode(":defaultValueGetter") newIdentNode(":defaultValueGetter")
ignoredKeyListProc {.compileTime.} = ignoredKeyListProc {.compileTime.} =
newIdentNode(":ignoredKeyList") newIdentNode(":ignoredKeyList")
ignoreUnknownKeysProc {.compileTime.} =
newIdentNode(":ignoreUnknownKeys")
var var
transientVectors {.compileTime.} = newSeq[set[int16]]() transientVectors {.compileTime.} = newSeq[set[int16]]()
@ -733,7 +735,7 @@ macro fetchTransientIndex(t: typedesc, tIndex: untyped): typed =
macro constructFieldValue(t: typedesc, tIndex: int, stream: untyped, macro constructFieldValue(t: typedesc, tIndex: int, stream: untyped,
context: untyped, name: untyped, o: untyped, context: untyped, name: untyped, o: untyped,
matched: untyped): typed = matched: untyped, failOnUnknown: bool): typed =
let let
tDecl = getType(t) tDecl = getType(t)
tName = $tDecl[1] tName = $tDecl[1]
@ -800,10 +802,12 @@ macro constructFieldValue(t: typedesc, tIndex: int, stream: untyped,
markAsFound(fieldIndex, matched)], true, stream, tName, $child)) markAsFound(fieldIndex, matched)], true, stream, tName, $child))
caseStmt.add(ob) caseStmt.add(ob)
inc(fieldIndex) inc(fieldIndex)
caseStmt.add(newNimNode(nnkElse).add(newNimNode(nnkRaiseStmt).add( caseStmt.add(newNimNode(nnkElse).add(newNimNode(nnkWhenStmt).add(
newCall(bindSym("constructionError"), stream, newNimNode(nnkElifBranch).add(failOnUnknown,
infix(newLit("While constructing " & tName & ": Unknown field: "), "&", newNimNode(nnkRaiseStmt).add(
newCall(bindSym("escape"), name)))))) newCall(bindSym("constructionError"), stream,
infix(newLit("While constructing " & tName & ": Unknown field: "), "&",
newCall(bindSym("escape"), name))))))))
result.add(caseStmt) result.add(caseStmt)
proc isVariantObject(t: typedesc): bool {.compileTime.} = proc isVariantObject(t: typedesc): bool {.compileTime.} =
@ -822,6 +826,13 @@ macro injectIgnoredKeyList(t: typedesc, ident: untyped): typed =
else: else:
const `ident` = newSeq[string]() const `ident` = newSeq[string]()
macro injectFailOnUnknownKeys(t: typedesc, ident: untyped): typed =
result = quote do:
when compiles(`ignoreUnknownKeysProc`(`t`)):
const `ident` = false
else:
const `ident` = true
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].} =
@ -841,6 +852,7 @@ proc constructObjectDefault*[O: object|tuple](
typetraits.name(O) & ": Expected " & $startKind & ", got " & $e.kind) typetraits.name(O) & ": Expected " & $startKind & ", got " & $e.kind)
when isVariantObject(O): reset(result) # make discriminants writeable when isVariantObject(O): reset(result) # make discriminants writeable
injectIgnoredKeyList(O, ignoredKeyList) injectIgnoredKeyList(O, ignoredKeyList)
injectFailOnUnknownKeys(O, failOnUnknown)
while s.peek.kind != endKind: while s.peek.kind != endKind:
e = s.next() e = s.next()
when isVariantObject(O): when isVariantObject(O):
@ -863,12 +875,14 @@ proc constructObjectDefault*[O: object|tuple](
found = true found = true
break break
inc(i) inc(i)
if not found: when failOnUnknown:
raise s.constructionError("While constructing " & if not found:
typetraits.name(O) & ": Unknown field: " & escape(name)) raise s.constructionError("While constructing " &
typetraits.name(O) & ": Unknown field: " & escape(name))
else: else:
if name notin ignoredKeyList: if name notin ignoredKeyList:
constructFieldValue(O, tIndex, s, c, name, result, matched) constructFieldValue(O, tIndex, s, c, name, result, matched,
failOnUnknown)
else: else:
e = s.next() e = s.next()
var depth = int(e.kind in {yamlStartMap, yamlStartSeq}) var depth = int(e.kind in {yamlStartMap, yamlStartSeq})
@ -1548,8 +1562,9 @@ macro setDefaultValue*(t: typedesc, field: untyped, value: typed): typed =
macro ignoreInputKey*(t: typedesc, name: string{lit}): typed = macro ignoreInputKey*(t: typedesc, name: string{lit}): typed =
## Tell NimYAML that when loading an object of type ``t``, any mapping key ## Tell NimYAML that when loading an object of type ``t``, any mapping key
## named ``name`` shall be ignored. This makes it possible to only load ## named ``name`` shall be ignored. Note that this even ignores the key if
## relevant parts of a YAML input and ignoring other portions of the input. ## the value of that key is necessary to construct a value of type ``t``,
## making deserialization fail.
## ##
## Example usage: ## Example usage:
## ##
@ -1567,3 +1582,17 @@ macro ignoreInputKey*(t: typedesc, name: string{lit}): typed =
{.fatal: "Input key " & `name` & " is already ignored!".} {.fatal: "Input key " & `name` & " is already ignored!".}
static: static:
ignoredKeyLists[`ignoredKeyListProc`(`t`)].add(`name`) ignoredKeyLists[`ignoredKeyListProc`(`t`)].add(`name`)
macro ignoreUnknownKeys*(t: typedesc): typed =
## Tell NimYAML that when loading an object or tuple of type ``t``, any
## mapping key that does not map to an existing field inside the object or
## tuple shall be ignored.
##
## Example usage:
##
## .. code-block::
## type MyObject = object
## a, b: string
## ignoreUnknownKeys(MyObject)
result = quote do:
proc `ignoreUnknownKeysProc`*(t: typedesc[`t`]): bool {.compileTime.} = true