mirror of https://github.com/status-im/NimYAML.git
Improved error messages and test them.
This commit is contained in:
parent
2ad41d349e
commit
3942e80d9b
|
@ -69,15 +69,17 @@ template assertStringEqual(expected, actual: string) =
|
||||||
echo "expected:\n", expected, "\nactual:\n", actual
|
echo "expected:\n", expected, "\nactual:\n", actual
|
||||||
assert(false)
|
assert(false)
|
||||||
|
|
||||||
template expectConstructionError(l, c: int, body: typed) =
|
template expectConstructionError(li, co: int, message: string, body: typed) =
|
||||||
try:
|
try:
|
||||||
body
|
body
|
||||||
echo "Expected YamlConstructionError, but none was raised!"
|
echo "Expected YamlConstructionError, but none was raised!"
|
||||||
fail()
|
fail()
|
||||||
except YamlConstructionError:
|
except YamlConstructionError:
|
||||||
let e = (ref YamlConstructionError)(getCurrentException())
|
let e = (ref YamlConstructionError)(getCurrentException())
|
||||||
doAssert l == e.line, "Expected error line " & $l & ", was " & $e.line
|
doAssert li == e.line, "Expected error line " & $li & ", was " & $e.line
|
||||||
doAssert c == e.column, "Expected error column " & $c & ", was " & $e.column
|
doAssert co == e.column, "Expected error column " & $co & ", was " & $e.column
|
||||||
|
doAssert message == e.msg, "Expected error message " & escape(message) &
|
||||||
|
", got " & escape(e.msg)
|
||||||
|
|
||||||
proc newNode(v: string): ref Node =
|
proc newNode(v: string): ref Node =
|
||||||
new(result)
|
new(result)
|
||||||
|
@ -315,19 +317,19 @@ suite "Serialization":
|
||||||
test "Load Tuple - unknown field":
|
test "Load Tuple - unknown field":
|
||||||
let input = "str: value\nfoo: bar\ni: 42\nb: true"
|
let input = "str: value\nfoo: bar\ni: 42\nb: true"
|
||||||
var result: MyTuple
|
var result: MyTuple
|
||||||
expectConstructionError(2, 1):
|
expectConstructionError(2, 1, "While constructing MyTuple: Unknown field: \"foo\""):
|
||||||
load(input, result)
|
load(input, result)
|
||||||
|
|
||||||
test "Load Tuple - missing field":
|
test "Load Tuple - missing field":
|
||||||
let input = "str: value\nb: true"
|
let input = "str: value\nb: true"
|
||||||
var result: MyTuple
|
var result: MyTuple
|
||||||
expectConstructionError(2, 8):
|
expectConstructionError(2, 8, "While constructing MyTuple: Missing field: \"i\""):
|
||||||
load(input, result)
|
load(input, result)
|
||||||
|
|
||||||
test "Load Tuple - duplicate field":
|
test "Load Tuple - duplicate field":
|
||||||
let input = "str: value\ni: 42\nb: true\nb: true"
|
let input = "str: value\ni: 42\nb: true\nb: true"
|
||||||
var result: MyTuple
|
var result: MyTuple
|
||||||
expectConstructionError(4, 1):
|
expectConstructionError(4, 1, "While constructing MyTuple: Duplicate field: \"b\""):
|
||||||
load(input, result)
|
load(input, result)
|
||||||
|
|
||||||
test "Load Multiple Documents":
|
test "Load Multiple Documents":
|
||||||
|
@ -362,19 +364,19 @@ suite "Serialization":
|
||||||
test "Load custom object - unknown field":
|
test "Load custom object - unknown field":
|
||||||
let input = " firstnamechar: P\n surname: Pan\n age: 12\n occupation: free"
|
let input = " firstnamechar: P\n surname: Pan\n age: 12\n occupation: free"
|
||||||
var result: Person
|
var result: Person
|
||||||
expectConstructionError(4, 3):
|
expectConstructionError(4, 3, "While constructing Person: Unknown field: \"occupation\""):
|
||||||
load(input, result)
|
load(input, result)
|
||||||
|
|
||||||
test "Load custom object - missing field":
|
test "Load custom object - missing field":
|
||||||
let input = "surname: Pan\nage: 12\n "
|
let input = "surname: Pan\nage: 12\n "
|
||||||
var result: Person
|
var result: Person
|
||||||
expectConstructionError(3, 3):
|
expectConstructionError(3, 3, "While constructing Person: Missing field: \"firstnamechar\""):
|
||||||
load(input, result)
|
load(input, result)
|
||||||
|
|
||||||
test "Load custom object - duplicate field":
|
test "Load custom object - duplicate field":
|
||||||
let input = "firstnamechar: P\nsurname: Pan\nage: 12\nsurname: Pan"
|
let input = "firstnamechar: P\nsurname: Pan\nage: 12\nsurname: Pan"
|
||||||
var result: Person
|
var result: Person
|
||||||
expectConstructionError(4, 1):
|
expectConstructionError(4, 1, "While constructing Person: Duplicate field: \"surname\""):
|
||||||
load(input, result)
|
load(input, result)
|
||||||
|
|
||||||
test "Load sequence with explicit tags":
|
test "Load sequence with explicit tags":
|
||||||
|
@ -445,7 +447,7 @@ suite "Serialization":
|
||||||
test "Load custom variant object - missing field":
|
test "Load custom variant object - missing field":
|
||||||
let input = "[{name: Bastet}, {kind: akCat}]"
|
let input = "[{name: Bastet}, {kind: akCat}]"
|
||||||
var result: Animal
|
var result: Animal
|
||||||
expectConstructionError(1, 32):
|
expectConstructionError(1, 32, "While constructing Animal: Missing field: \"purringIntensity\""):
|
||||||
load(input, result)
|
load(input, result)
|
||||||
|
|
||||||
test "Dump cyclic data structure":
|
test "Dump cyclic data structure":
|
||||||
|
|
|
@ -510,19 +510,19 @@ macro matchMatrix(t: typedesc): untyped =
|
||||||
for i in 0..<numFields:
|
for i in 0..<numFields:
|
||||||
result.add(newLit(false))
|
result.add(newLit(false))
|
||||||
|
|
||||||
proc checkDuplicate(s: NimNode, t: typedesc, name: string, i: int,
|
proc checkDuplicate(s: NimNode, tName: string, name: string, i: int,
|
||||||
matched: NimNode): NimNode {.compileTime.} =
|
matched: NimNode): NimNode {.compileTime.} =
|
||||||
result = newIfStmt((newNimNode(nnkBracketExpr).add(matched, newLit(i)),
|
result = newIfStmt((newNimNode(nnkBracketExpr).add(matched, newLit(i)),
|
||||||
newNimNode(nnkRaiseStmt).add(newCall(bindSym("constructionError"), s,
|
newNimNode(nnkRaiseStmt).add(newCall(bindSym("constructionError"), s,
|
||||||
newLit("While constructing " & typetraits.name(t) &
|
newLit("While constructing " & tName & ": Duplicate field: " &
|
||||||
": Duplicate field: " & escape(name))))))
|
escape(name))))))
|
||||||
|
|
||||||
proc checkMissing(s: NimNode, t: typedesc, name: string, i: int,
|
proc checkMissing(s: NimNode, tName: string, name: string, i: int,
|
||||||
matched: NimNode): NimNode {.compileTime.} =
|
matched: NimNode): NimNode {.compileTime.} =
|
||||||
result = newIfStmt((newCall("not", newNimNode(nnkBracketExpr).add(matched,
|
result = newIfStmt((newCall("not", newNimNode(nnkBracketExpr).add(matched,
|
||||||
newLit(i))), newNimNode(nnkRaiseStmt).add(newCall(
|
newLit(i))), newNimNode(nnkRaiseStmt).add(newCall(
|
||||||
bindSym("constructionError"), s, newLit("While constructing " &
|
bindSym("constructionError"), s, newLit("While constructing " &
|
||||||
typetraits.name(t) & ": Missing field: " & escape(name))))))
|
tName & ": Missing field: " & escape(name))))))
|
||||||
|
|
||||||
proc markAsFound(i: int, matched: NimNode): NimNode {.compileTime.} =
|
proc markAsFound(i: int, matched: NimNode): NimNode {.compileTime.} =
|
||||||
newAssignment(newNimNode(nnkBracketExpr).add(matched, newLit(i)),
|
newAssignment(newNimNode(nnkBracketExpr).add(matched, newLit(i)),
|
||||||
|
@ -532,25 +532,30 @@ macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, o: typed,
|
||||||
matched: typed): typed =
|
matched: typed): typed =
|
||||||
result = newStmtList()
|
result = newStmtList()
|
||||||
let
|
let
|
||||||
tDesc = getType(getType(t)[1])
|
tDecl = getType(t)
|
||||||
|
tName = $tDecl[1]
|
||||||
|
tDesc = getType(tDecl[1])
|
||||||
var field = 0
|
var field = 0
|
||||||
for child in tDesc[2].children:
|
for child in tDesc[2].children:
|
||||||
if child.kind == nnkRecCase:
|
if child.kind == nnkRecCase:
|
||||||
result.add(checkMissing(s, t, $child[0], field, matched))
|
result.add(checkMissing(s, tName, $child[0], field, matched))
|
||||||
for bIndex in 1 .. len(child) - 1:
|
for bIndex in 1 .. len(child) - 1:
|
||||||
let discChecks = newStmtList()
|
let discChecks = newStmtList()
|
||||||
for item in child[bIndex][1].children:
|
for item in child[bIndex][1].children:
|
||||||
inc(field)
|
inc(field)
|
||||||
discChecks.add(checkMissing(s, t, $item, field, matched))
|
discChecks.add(checkMissing(s, tName, $item, field, matched))
|
||||||
result.add(newIfStmt((infix(newDotExpr(o, newIdentNode($child[0])),
|
result.add(newIfStmt((infix(newDotExpr(o, newIdentNode($child[0])),
|
||||||
"==", child[bIndex][0]), discChecks)))
|
"==", child[bIndex][0]), discChecks)))
|
||||||
else:
|
else:
|
||||||
result.add(checkMissing(s, t, $child, field, matched))
|
result.add(checkMissing(s, tName, $child, field, matched))
|
||||||
inc(field)
|
inc(field)
|
||||||
|
|
||||||
macro constructFieldValue(t: typedesc, stream: untyped, context: untyped,
|
macro constructFieldValue(t: typedesc, stream: untyped, context: untyped,
|
||||||
name: untyped, o: untyped, matched: untyped): typed =
|
name: untyped, o: untyped, matched: untyped): typed =
|
||||||
let tDesc = getType(getType(t)[1])
|
let
|
||||||
|
tDecl = getType(t)
|
||||||
|
tName = $tDecl[1]
|
||||||
|
tDesc = getType(tDecl[1])
|
||||||
result = newNimNode(nnkCaseStmt).add(name)
|
result = newNimNode(nnkCaseStmt).add(name)
|
||||||
var fieldIndex = 0
|
var fieldIndex = 0
|
||||||
for child in tDesc[2].children:
|
for child in tDesc[2].children:
|
||||||
|
@ -560,7 +565,7 @@ macro constructFieldValue(t: typedesc, stream: untyped, context: untyped,
|
||||||
discType = newCall("type", discriminant)
|
discType = newCall("type", discriminant)
|
||||||
var disOb = newNimNode(nnkOfBranch).add(newStrLitNode($child[0]))
|
var disOb = newNimNode(nnkOfBranch).add(newStrLitNode($child[0]))
|
||||||
disOb.add(newStmtList(
|
disOb.add(newStmtList(
|
||||||
checkDuplicate(stream, t, $child[0], fieldIndex, matched),
|
checkDuplicate(stream, tName, $child[0], fieldIndex, matched),
|
||||||
newNimNode(nnkVarSection).add(
|
newNimNode(nnkVarSection).add(
|
||||||
newNimNode(nnkIdentDefs).add(
|
newNimNode(nnkIdentDefs).add(
|
||||||
newIdentNode("value"), discType, newEmptyNode())),
|
newIdentNode("value"), discType, newEmptyNode())),
|
||||||
|
@ -581,7 +586,7 @@ macro constructFieldValue(t: typedesc, stream: untyped, context: untyped,
|
||||||
newCall(bindSym("constructionError"), stream,
|
newCall(bindSym("constructionError"), stream,
|
||||||
infix(newStrLitNode("Field " & $item & " not allowed for " &
|
infix(newStrLitNode("Field " & $item & " not allowed for " &
|
||||||
$child[0] & " == "), "&", prefix(discriminant, "$"))))))
|
$child[0] & " == "), "&", prefix(discriminant, "$"))))))
|
||||||
ob.add(newStmtList(checkDuplicate(stream, t, $item, fieldIndex,
|
ob.add(newStmtList(checkDuplicate(stream, tName, $item, fieldIndex,
|
||||||
matched), ifStmt, markAsFound(fieldIndex, matched)))
|
matched), ifStmt, markAsFound(fieldIndex, matched)))
|
||||||
result.add(ob)
|
result.add(ob)
|
||||||
else:
|
else:
|
||||||
|
@ -589,15 +594,15 @@ macro constructFieldValue(t: typedesc, stream: untyped, context: untyped,
|
||||||
var ob = newNimNode(nnkOfBranch).add(newStrLitNode($child))
|
var ob = newNimNode(nnkOfBranch).add(newStrLitNode($child))
|
||||||
let field = newDotExpr(o, newIdentNode($child))
|
let field = newDotExpr(o, newIdentNode($child))
|
||||||
ob.add(newStmtList(
|
ob.add(newStmtList(
|
||||||
checkDuplicate(stream, t, $child, fieldIndex, matched),
|
checkDuplicate(stream, tName, $child, fieldIndex, matched),
|
||||||
newCall("constructChild", stream, context, field),
|
newCall("constructChild", stream, context, field),
|
||||||
markAsFound(fieldIndex, matched)))
|
markAsFound(fieldIndex, matched)))
|
||||||
result.add(ob)
|
result.add(ob)
|
||||||
inc(fieldIndex)
|
inc(fieldIndex)
|
||||||
result.add(newNimNode(nnkElse).add(newNimNode(nnkRaiseStmt).add(
|
result.add(newNimNode(nnkElse).add(newNimNode(nnkRaiseStmt).add(
|
||||||
newCall(bindSym("constructionError"), stream,
|
newCall(bindSym("constructionError"), stream,
|
||||||
infix(newLit("While constructing " & typetraits.name(t) &
|
infix(newLit("While constructing " & tName & ": Unknown field: "), "&",
|
||||||
": Unknown field: "), "&", name)))))
|
newCall(bindSym("escape"), name))))))
|
||||||
|
|
||||||
proc isVariantObject(t: typedesc): bool {.compileTime.} =
|
proc isVariantObject(t: typedesc): bool {.compileTime.} =
|
||||||
let tDesc = getType(t)
|
let tDesc = getType(t)
|
||||||
|
@ -659,7 +664,7 @@ proc constructObject*[O: object|tuple](
|
||||||
for fname, value in fieldPairs(result):
|
for fname, value in fieldPairs(result):
|
||||||
if not matched[i]:
|
if not matched[i]:
|
||||||
raise s.constructionError("While constructing " &
|
raise s.constructionError("While constructing " &
|
||||||
typetraits.name(O) & ": Field missing: " & escape(fname))
|
typetraits.name(O) & ": Missing field: " & escape(fname))
|
||||||
inc(i)
|
inc(i)
|
||||||
else: ensureAllFieldsPresent(s, O, result, matched)
|
else: ensureAllFieldsPresent(s, O, result, matched)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue