From dcfa8fd27cab39d33ad9690e04d8359c60592ce2 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 27 Oct 2016 17:58:14 +0200 Subject: [PATCH] Implemented ignoreInputKey --- test/tserialization.nim | 23 +++++++++++++++++++++++ yaml/serialization.nim | 39 ++++++++++++++++++++++++++++++++++----- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/test/tserialization.nim b/test/tserialization.nim index f701f12..c089926 100644 --- a/test/tserialization.nim +++ b/test/tserialization.nim @@ -57,6 +57,9 @@ type WithDefault = object a, b, c, d: string + WithIgnoredField = object + x, y: int + markAsTransient(NonVariantWithTransient, a) markAsTransient(NonVariantWithTransient, c) @@ -67,6 +70,8 @@ markAsTransient(VariantWithTransient, neverThere) setDefaultValue(WithDefault, b, "b") setDefaultValue(WithDefault, d, "d") +ignoreInputKey(WithIgnoredField, "z") + proc `$`(v: BetterInt): string {.borrow.} proc `==`(left, right: BetterInt): bool {.borrow.} @@ -538,6 +543,24 @@ suite "Serialization": - kind: deD""", output + test "Load object with ignored key": + let input = "[{x: 1, y: 2}, {x: 3, z: 4, y: 5}, {z: [1, 2, 3], x: 4, y: 5}]" + var result: seq[WithIgnoredField] + load(input, result) + assert result.len == 3 + assert result[0].x == 1 + assert result[0].y == 2 + assert result[1].x == 3 + assert result[1].y == 5 + assert result[2].x == 4 + assert result[2].y == 5 + + test "Load object with ignored key - unknown field": + let input = "{x: 1, y: 2, zz: 3}" + var result: WithIgnoredField + expectConstructionError(1, 16, "While constructing WithIgnoredField: Unknown field: \"zz\""): + load(input, result) + test "Dump cyclic data structure": var a = newNode("a") diff --git a/yaml/serialization.nim b/yaml/serialization.nim index 99c559a..31a5820 100644 --- a/yaml/serialization.nim +++ b/yaml/serialization.nim @@ -545,10 +545,13 @@ let newIdentNode(":defaultBitvector") defaultValueGetter {.compileTime.} = newIdentNode(":defaultValueGetter") + ignoredKeyListProc {.compileTime.} = + newIdentNode(":ignoredKeyList") var transientVectors {.compileTime.} = newSeq[set[int16]]() defaultVectors {.compileTime.} = newSeq[set[int16]]() + ignoredKeyLists {.compileTime.} = newSeq[seq[string]]() proc addDefaultOr(tName: string, i: int, o: NimNode, field, elseBranch, defaultValues: NimNode): NimNode {.compileTime.} = @@ -570,7 +573,6 @@ proc checkMissing(s: NimNode, t: typedesc, tName: string, field: NimNode, newNimNode(nnkRaiseStmt).add(newCall( bindSym("constructionError"), s, newLit("While constructing " & tName & ": Missing field: " & escape($field)))), defaultValues))) - echo result.repr proc markAsFound(i: int, matched: NimNode): NimNode {.compileTime.} = newAssignment(newNimNode(nnkBracketExpr).add(matched, newLit(i)), @@ -722,6 +724,13 @@ proc isVariantObject(t: typedesc): bool {.compileTime.} = if child.kind == nnkRecCase: return true return false +macro injectIgnoredKeyList(t: typedesc, ident: untyped): typed = + result = quote do: + when compiles(`ignoredKeyListProc`(`t`)): + const `ident` = ignoredKeyLists[`ignoredKeyListproc`(`t`)] + else: + const `ident` = newSeq[string]() + proc constructObject*[O: object|tuple]( s: var YamlStream, c: ConstructionContext, result: var O) {.raises: [YamlConstructionError, YamlStreamError].} = @@ -736,9 +745,8 @@ proc constructObject*[O: object|tuple]( raise s.constructionError("While constructing " & typetraits.name(O) & ": Expected " & $startKind & ", got " & $e.kind) when isVariantObject(O): reset(result) # make discriminants writeable + injectIgnoredKeyList(O, ignoredKeyList) while s.peek.kind != endKind: - # todo: check for duplicates in input and raise appropriate exception - # also todo: check for missing items and raise appropriate exception e = s.next() when isVariantObject(O): if e.kind != yamlStartMap: @@ -764,7 +772,17 @@ proc constructObject*[O: object|tuple]( raise s.constructionError("While constructing " & typetraits.name(O) & ": Unknown field: " & escape(name)) else: - constructFieldValue(O, tIndex, s, c, name, result, matched) + if name notin ignoredKeyList: + constructFieldValue(O, tIndex, s, c, name, result, matched) + else: + e = s.next() + var depth = int(e.kind in {yamlStartMap, yamlStartSeq}) + while depth > 0: + case s.next().kind + of yamlStartMap, yamlStartSeq: inc(depth) + of yamlEndMap, yamlEndSeq: dec(depth) + of yamlScalar: discard + else: internalError("Unexpected event kind.") when isVariantObject(O): e = s.next() if e.kind != yamlEndMap: @@ -1336,4 +1354,15 @@ macro setDefaultValue*(t: typedesc, field: untyped, value: typed): typed = static: `defaultValueGetter`(`t`).`field` = `value` defaultVectors[`defaultBitvectorProc`(`t`)].incl(getFieldIndex(`t`, `field`)) - echo result.repr \ No newline at end of file + +macro ignoreInputKey*(t: typedesc, name: string{lit}): typed = + let nextIgnoredKeyList = ignoredKeyLists.len + result = quote do: + when not compiles(`ignoredKeyListProc`(`t`)): + proc `ignoredKeyListProc`*(t: typedesc[`t`]): int {.compileTime.} = + `nextIgnoredKeyList` + static: ignoredKeyLists.add(@[]) + when `name` in ignoredKeyLists[`ignoredKeyListProc`(`t`)]: + {.fatal: "Input key " & `name` & " is already ignored!".} + static: + ignoredKeyLists[`ignoredKeyListProc`(`t`)].add(`name`) \ No newline at end of file